深入理解JavaScript创建对象的多种方式以及优缺点


Posted in Javascript onJune 01, 2017

写在前面

这篇文章讲解创建对象的各种方式,以及优缺点。

但是注意:

这篇文章更像是笔记,因为《JavaScript高级程序设计》写得真是太好了!

1. 工厂模式

function createPerson(name) {
  var o = new Object();
  o.name = name;
  o.getName = function () {
    console.log(this.name);
  };

  return o;
}

var person1 = createPerson('kevin');

缺点:对象无法识别,因为所有的实例都指向一个原型

2. 构造函数模式

function Person(name) {
  this.name = name;
  this.getName = function () {
    console.log(this.name);
  };
}

var person1 = new Person('kevin');

优点:实例可以识别为一个特定的类型

缺点:每次创建实例时,每个方法都要被创建一次

2.1 构造函数模式优化

function Person(name) {
  this.name = name;
  this.getName = getName;
}

function getName() {
  console.log(this.name);
}

var person1 = new Person('kevin');

优点:解决了每个方法都要被重新创建的问题

缺点:这叫啥封装……

3. 原型模式

function Person(name) {

}

Person.prototype.name = 'keivn';
Person.prototype.getName = function () {
  console.log(this.name);
};

var person1 = new Person();

优点:方法不会重新创建

缺点:1. 所有的属性和方法都共享 2. 不能初始化参数

3.1 原型模式优化

function Person(name) {

}

Person.prototype = {
  name: 'kevin',
  getName: function () {
    console.log(this.name);
  }
};

var person1 = new Person();

优点:封装性好了一点

缺点:重写了原型,丢失了constructor属性

3.2 原型模式优化

function Person(name) {

}

Person.prototype = {
  constructor: Person,
  name: 'kevin',
  getName: function () {
    console.log(this.name);
  }
};

var person1 = new Person();

优点:实例可以通过constructor属性找到所属构造函数

缺点:原型模式该有的缺点还是有

4. 组合模式

构造函数模式与原型模式双剑合璧。

function Person(name) {
  this.name = name;
}

Person.prototype = {
  constructor: Person,
  getName: function () {
    console.log(this.name);
  }
};

var person1 = new Person();

优点:该共享的共享,该私有的私有,使用最广泛的方式

缺点:有的人就是希望全部都写在一起,即更好的封装性

4.1 动态原型模式

function Person(name) {
  this.name = name;
  if (typeof this.getName != "function") {
    Person.prototype.getName = function () {
      console.log(this.name);
    }
  }
}

var person1 = new Person();

注意:使用动态原型模式时,不能用对象字面量重写原型

解释下为什么:

function Person(name) {
  this.name = name;
  if (typeof this.getName != "function") {
    Person.prototype = {
      constructor: Person,
      getName: function () {
        console.log(this.name);
      }
    }
  }
}
 
var person1 = new Person('kevin');
var person2 = new Person('daisy');
 
// 报错 并没有该方法
person1.getName();
 
// 注释掉上面的代码,这句是可以执行的。
person2.getName();

为了解释这个问题,假设开始执行var person1 = new Person('kevin')。

如果对 new 和 apply 的底层执行过程不是很熟悉,可以阅读底部相关链接中的文章。

我们回顾下 new 的实现步骤:

  1. 首先新建一个对象
  2. 然后将对象的原型指向 Person.prototype
  3. 然后 Person.apply(obj)
  4. 返回这个对象

注意这个时候,回顾下 apply 的实现步骤,会执行 obj.Person 方法,这个时候就会执行 if 语句里的内容,注意构造函数的 prototype 属性指向了实例的原型,使用字面量方式直接覆盖 Person.prototype,并不会更改实例的原型的值,person1 依然是指向了以前的原型,而不是 Person.prototype。而之前的原型是没有 getName 方法的,所以就报错了!

如果你就是想用字面量方式写代码,可以尝试下这种:

function Person(name) {
  this.name = name;
  if (typeof this.getName != "function") {
    Person.prototype = {
      constructor: Person,
      getName: function () {
        console.log(this.name);
      }
    }
 
    return new Person(name);
  }
}
 
var person1 = new Person('kevin');
var person2 = new Person('daisy');
 
person1.getName(); // kevin
person2.getName(); // daisy

5.1 寄生构造函数模式

function Person(name) {
 
  var o = new Object();
  o.name = name;
  o.getName = function () {
    console.log(this.name);
  };
 
  return o;
 
}
 
var person1 = new Person('kevin');
console.log(person1 instanceof Person) // false
console.log(person1 instanceof Object) // true

寄生构造函数模式,我个人认为应该这样读:

寄生-构造函数-模式,也就是说寄生在构造函数的一种方法。

也就是说打着构造函数的幌子挂羊头卖狗肉,你看创建的实例使用 instanceof 都无法指向构造函数!

这样方法可以在特殊情况下使用。比如我们想创建一个具有额外方法的特殊数组,但是又不想直接修改Array构造函数,我们可以这样写:

function SpecialArray() {
  var values = new Array();
 
  for (var i = 0, len = arguments.length; i len; i++) {
    values.push(arguments[i]);
  }
 
  values.toPipedString = function () {
    return this.join("|");
  };
  return values;
}
 
var colors = new SpecialArray('red', 'blue', 'green');
var colors2 = SpecialArray('red2', 'blue2', 'green2');
 
 
console.log(colors);
console.log(colors.toPipedString()); // red|blue|green
 
console.log(colors2);
console.log(colors2.toPipedString()); // red2|blue2|green2

你会发现,其实所谓的寄生构造函数模式就是比工厂模式在创建对象的时候,多使用了一个new,实际上两者的结果是一样的。

但是作者可能是希望能像使用普通 Array 一样使用 SpecialArray,虽然把 SpecialArray 当成函数也一样能用,但是这并不是作者的本意,也变得不优雅。

在可以使用其他模式的情况下,不要使用这种模式。

但是值得一提的是,上面例子中的循环:

for (var i = 0, len = arguments.length; i len; i++) {
  values.push(arguments[i]);
}

可以替换成:

values.push.apply(values, arguments);

5.2 稳妥构造函数模式

function person(name){
  var o = new Object();
  o.sayName = function(){
    console.log(name);
  };
  return o;
}

var person1 = person('kevin');

person1.sayName(); // kevin

person1.name = "daisy";

person1.sayName(); // kevin

console.log(person1.name); // daisy

所谓稳妥对象,指的是没有公共属性,而且其方法也不引用 this 的对象。

与寄生构造函数模式有两点不同:

  1. 新创建的实例方法不引用 this
  2. 不使用 new 操作符调用构造函数

稳妥对象最适合在一些安全的环境中。

稳妥构造函数模式也跟工厂模式一样,无法识别对象所属类型。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js 点击按钮弹出另一页,选择值后,返回到当前页
May 26 Javascript
jquery键盘事件介绍
Jan 31 Javascript
获取服务器传来的数据 用JS去空格的正则表达式
Mar 26 Javascript
jQuery设置指定网页元素宽度和高度的方法
Mar 25 Javascript
javascript实现淘宝幻灯片广告展示效果
Apr 27 Javascript
js模仿php中strtotime()与date()函数实现方法
Aug 11 Javascript
JS 动态加载js文件和css文件 同步/异步的两种简单方式
Sep 23 Javascript
jQuery按需加载轮播图(web前端性能优化)
Feb 17 Javascript
微信小程序 动态绑定数据及动态事件处理
Mar 14 Javascript
jQuery实现的解析本地 XML 文档操作示例
Apr 30 jQuery
微信小程序开发(二):页面跳转并传参操作示例
Jun 01 Javascript
详解JavaScript数据类型和判断方法
Sep 04 Javascript
解决BootStrap Fileinput手机图片上传显示旋转问题
Jun 01 #Javascript
详解Vue2.0里过滤器容易踩到的坑
Jun 01 #Javascript
Angularjs验证用户输入的字符串是否为日期时间
Jun 01 #Javascript
jQuery实现web页面樱花坠落的特效
Jun 01 #jQuery
jquery网页加载进度条的实现
Jun 01 #jQuery
BootStrap selectpicker后台动态绑定数据
Jun 01 #Javascript
JS实现仿饿了么在浏览器标签页失去焦点时网页Title改变
Jun 01 #Javascript
You might like
检查url链接是否已经有参数的php代码 添加 ? 或 &
2010/02/09 PHP
Yii2使用自带的UploadedFile实现的文件上传
2016/06/20 PHP
PHP中字符串长度的截取用法示例
2017/01/12 PHP
一个符号插入器 中用到的js代码
2007/09/04 Javascript
javascript学习笔记(十四) window对象使用介绍
2012/06/20 Javascript
浅析JavaScript中两种类型的全局对象/函数
2013/12/05 Javascript
jquery获得option的值和对option进行操作
2013/12/13 Javascript
判断一个对象是否为jquery对象的方法
2014/03/12 Javascript
javascript面向对象之共享成员属性与方法及prototype关键字用法
2015/01/13 Javascript
Javascript实现前端简单的路由实例
2016/09/11 Javascript
基于Bootstrap和jQuery构建前端分页工具实例代码
2016/11/23 Javascript
jquery实现轮播图效果
2017/02/13 Javascript
整理关于Bootstrap表单的慕课笔记
2017/03/29 Javascript
ES6深入理解之“let”能替代”var“吗?
2017/06/28 Javascript
React中使用collections时key的重要性详解
2017/08/07 Javascript
jQuery轮播图实例详解
2018/08/15 jQuery
JS事件绑定的常用方式实例总结
2019/03/02 Javascript
vue 框架下自定义滚动条(easyscroll)实现方法
2019/08/29 Javascript
js获取本日、本周、本月的时间代码
2020/02/01 Javascript
[01:31]完美与DOTA2历程
2014/07/31 DOTA
利用Python命令行传递实例化对象的方法
2016/11/02 Python
利用python求解物理学中的双弹簧质能系统详解
2017/09/29 Python
Python网络编程之TCP与UDP协议套接字用法示例
2018/02/02 Python
Python实现微信自动好友验证,自动回复,发送群聊链接方法
2019/02/21 Python
如何在Django项目中引入静态文件
2019/07/26 Python
Python线上环境使用日志的及配置文件
2019/07/28 Python
pandas的to_datetime时间转换使用及学习心得
2019/08/11 Python
python数据处理之如何选取csv文件中某几行的数据
2019/09/02 Python
pyinstaller将含有多个py文件的python程序做成exe
2020/04/29 Python
win10下python3.8的PIL库安装过程
2020/06/08 Python
什么是符号链接,什么是硬链接?符号链接与硬链接的区别是什么?
2014/01/19 面试题
小学运动会入场式解说词
2014/02/18 职场文书
旅游管理毕业生自荐信范文
2014/03/19 职场文书
实习单位鉴定评语
2014/04/26 职场文书
初中信息技术教学反思
2016/02/16 职场文书
读《方与圆》有感:交友方圆有度
2020/01/14 职场文书