深入理解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 相关文章推荐
jquery.validate分组验证代码
Mar 17 Javascript
学习从实践开始之jQuery插件开发 菜单插件开发
May 03 Javascript
JS操作Cookies包括(读取添加与删除)
Dec 26 Javascript
jquery使用ColorBox弹出图片组浏览层实例演示
Mar 14 Javascript
文本框文本自动补全效果示例分享
Jan 19 Javascript
node.js中的socket.io入门实例
Apr 26 Javascript
AngularJS基础 ng-disabled 指令详解及简单示例
Aug 01 Javascript
Jquery遍历select option和添加移除option的实现方法
Aug 26 Javascript
微信小程序模板之分页滑动栏
Feb 10 Javascript
Angular.js中处理页面闪烁的方法详解
Mar 09 Javascript
详解angularjs4部署文件过大解决过程
Dec 05 Javascript
JavaScript鼠标拖拽事件详解
Apr 03 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
一个用php实现的获取URL信息的类
2007/01/02 PHP
PHP中输出转义JavaScript代码的实现代码
2011/04/22 PHP
PHP获取用户的浏览器与操作系统信息的代码
2012/09/04 PHP
php5.4以下版本json不支持不转义内容中文的解决方法
2015/01/13 PHP
php 问卷调查结果统计
2015/10/08 PHP
php通过两层过滤获取留言内容的方法
2016/07/11 PHP
PHP中PDO事务处理操作示例
2018/05/02 PHP
jquery api参考 visualjquery 中国线路 速度快
2007/11/30 Javascript
实现png图片和png背景透明(支持多浏览器)的方法
2009/09/08 Javascript
JS获取计算机mac地址以及IP的实现方法
2014/01/08 Javascript
js判断手机和pc端选择不同执行事件的方法
2015/01/30 Javascript
js检测用户输入密码强度
2015/10/22 Javascript
javascript嵌套函数和在函数内调用外部函数的区别分析
2016/01/31 Javascript
jQuery Mobile页面返回不需要重新get
2016/04/26 Javascript
vue使用vue-i18n实现国际化的实现代码
2018/04/08 Javascript
4个顶级开源JavaScript图表库
2018/09/29 Javascript
继承行为在 ES5 与 ES6 中的区别详解
2019/12/24 Javascript
[02:04]2016国际邀请赛中国区预选赛VG.R晋级之路
2016/07/01 DOTA
python中getattr函数使用方法 getattr实现工厂模式
2014/01/20 Python
python网络编程学习笔记(10):webpy框架
2014/06/09 Python
PyCharm下载和安装详细步骤
2019/12/17 Python
Python中的 ansible 动态Inventory 脚本
2020/01/19 Python
浅谈ROC曲线的最佳阈值如何选取
2020/02/28 Python
深入浅析python 中的self和cls的区别
2020/06/20 Python
宝塔面板成功部署Django项目流程(图文)
2020/06/22 Python
Python利用Pillow(PIL)库实现验证码图片的全过程
2020/10/04 Python
Python logging自定义字段输出及打印颜色
2020/11/30 Python
King Apparel官网:英国街头服饰品牌
2019/09/05 全球购物
医大实习自我鉴定
2013/12/07 职场文书
《金子》教学反思
2014/04/13 职场文书
小学生综合素质评语
2014/04/23 职场文书
纪律教育学习月活动总结
2014/08/27 职场文书
土建技术员岗位职责
2015/04/11 职场文书
安全生产会议制度
2015/08/06 职场文书
Nginx内网单机反向代理的实现
2021/11/07 Servers
vue3种table表格选项个数的控制方法
2022/04/14 Vue.js