工厂模式

工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题。

function createPerson(name, age, job) {var o = new Object();o.name = name;o.age = age;o.job = job;o.sayName = function() {alert(this.age);};return o;
}var person = createPerson('hanzichi', 30, 'JavaScript Engineer');

构造函数模式

function Person(name, age, job) {this.name = name;this.age = age;this.job = job;this.sayName = function() {alert(this.name);};
}var person = new Person('hanzichi', 30, 'JavaScript Engineer');

按照惯例,构造函数始终都应该以一个大写字母开头,这个做法借鉴自其他 OO 语言。

要创建 Person 的新实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4 个步骤(也可以参考 一道有意思的笔试题引发的对于new操作符的思考):

  1. 创建一个对象
  2. 将构造函数的作用域赋给新的对象(因此 this 就指向了这个新对象)
  3. 执行构造函数中的代码(为这个新对象添加属性)
  4. 返回新对象

原型模式

function Person() {}Person.prototype.name = 'hanzichi';
Person.prototype.age = 30;
Person.prototype.job = 'JavaScript Engineer';
Person.prototype.sayName = function() {alert(this.name);
};var person = new Person();
person.sayName();

在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。

我们也可以用一个包含所有属性和方法的对象字面量来重写整个原型对象:

function Person() {}Person.prototype = {name: 'hanzichi',age: 30,job: 'JavaScript Engineer',sayName: function() {alert(this.name);}
};var person = new Person();

在上面的代码中,我们将 Person.prototype 设置为等于一个以对象字面量形式创建的新对象。最终结果相同,但有一个例外:constructor 属性不再指向 Person 了。每创建一个函数,就会同时创建它的 prototype 对象,这个对象也会自动获得 constructor 属性。而我们在这里使用的语法,本质上完全重写了默认的 prototype 对象,因此 constructor 属性也就变成了新对象的 constructor 属性(指向 Object 构造函数),不再指向 Person 函数。此时,尽管 instanceof 操作符还能返回正确的结果,但通过 constructor 已经无法确定对象的类型了。

function Person() {}Person.prototype = {name: 'hanzichi',age: 30,job: 'JavaScript Engineer',sayName: function() {alert(this.name);}
};var person = new Person();
console.log(person instanceof Object); // true
console.log(person instanceof Person); // true
console.log(person.constructor === Person); // false
console.log(person.constructor === Object); // true

如果 constructor 的值真的很重要,可以像下面这样特意将它设置回适当的值:

function Person() {}Person.prototype = {constructor: Person,name: 'hanzichi',age: 30,job: 'JavaScript Engineer',sayName: function() {alert(this.name);}
};var person = new Person();
console.log(person.constructor === Person); // true

注意,以这种方式重设 constructor 属性会导致它的 [[Enumerable]] 特性被设置为 true。默认情况下,原生的 constructor 属性是不可枚举的,可以用 Object.definePropety(ES5):

function Person() {}Person.prototype = {name: 'hanzichi',age: 30,job: 'JavaScript Engineer',sayName: function() {alert(this.name);}
};// 重设构造函数,适用 ES5+ 浏览器
Object.defineProperty(Person.prototype, 'constructor', {value: Person,enumerable: false
});var person = new Person();
console.log(person.constructor === Person); // true

构造函数模式 + 原型模式

function Person(name, age, job) {this.name = name;this.age = age;this.job = job;
}Person.prototype = {constructor: Person,sayName: function() {alert(this.name);}
};var person = new Person('hanzichi', 30, 'JavaScript Engineer');

这种构造函数与原型混合的模式,是目前 ECMAScript 中使用最广泛、认同度最高的一种创建自定义类型的方法

动态原型模式

有其他 OO 语言经验的开发人员在看到独立的构造函数和原型时,很可能会感到非常困惑。动态原型模式正是致力于解决这个问题的一个方案,它把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型(仅在必要的情况下),又保持了同时使用构造函数和原型的优点。换句话说,可以通过检查某个应该存在的方法是否有效,来决定是否需要初始化原型。

function Person(name, age, job) {this.name = name;this.age = age;this.job = job;if (typeof this.sayName !== 'function') {Person.prototype.sayName = function() {alert(this.name);};}
}var person = new Person('hanzichi', 30, 'JavaScript Engineer');

有点「延迟加载」的意思(参 高性能JavaScript 编程实践 「不要重复工作」一节)。

寄生构造函数模式

这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。但从表面上看,这个函数又很像是典型的构造函数。

function Person(name, age, job) {var o = new Object();o.name = name;o.age = age;o.job = job;o.sayName = function() {alert(this.name);};return o;
}var person = new Person('hanzichi', 30, 'JavaScript Engineer');

这个模式可以在特殊的情况下用来为对象创建构造函数。假设我们想创建一个具有额外方法的特殊的数组,由于不能直接修改 Array 构造函数,因此可以使用这个模式。

function SpecialArray() {var values = new Array();values.push.apply(values, arguments);// 添加方法values.toPipedString = function() {return this.join('|');};return values;
}var colors = new SpecialArray('red', 'blue', 'green');
console.log(colors.toPipedString()); // red|blue|green

关于寄生构造函数模式,有一点需要说明:首先,返回的对象和构造函数或者与构造函数的原型属性之间并没有关系,也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。为此,不能依赖 instanceof 操作符来确定对象类型。由于存在上述问题,我们建议在可以使用其他模式的情况下,不要使用这种模式。

稳妥构造函数模式

所谓稳妥对象,指的是没有公共属性,而且其方法也不引用 this 的对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用 this 和 new),或者在防止数据被其他应用程序改动时使用。

function Person(name, age, job) {// 创建要返回的对象var o = new Object();// 可以在这里定义私有变量和函数var name = name;var age = age;var job = job;// 添加方法o.sayName = function() {alert(name);};// 返回对象return o;
}var person = Person('hanzichi', 30, 'JavaScript Engineer');
person.sayName();

注意,在以这种模式创建的对象中,除了使用 sayName() 方法之外,没有其他方法访问 name 的值。

变量 person 保存的是一个稳妥对象,而除了调用 sayName() 方法外,没有别的方式可以访问其数据成员。

和寄生构造函数模式类似,使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,因此 instanceof 操作符对这种对象也没有意义。

JavaScript OOP 之「创建对象」相关推荐

  1. 以及其任何超类对此上下文都是未知的_web前端入门到实战:Javascript 中的「上下文」你只需要看这一篇

    正文 上下文 是Javascript 中的一个比较重要的概念, 可能很多朋友对这个概念并不是很熟悉, 那换成「作用域」 和 「闭包」呢?是不是就很亲切了. 「作用域」和「闭包」 都是和「执行上下文」密 ...

  2. javascript感叹号1_「翻译」JavaScript的可视化学习之三:作用域(链)

    儿子:爸,我有个愿望,当有一天我疲惫回家时,你打电话告诉我,家里有百亿家产等我继承,之前装穷都是为了锻炼我.爸:别着急,爸爸也在等你爷爷的电话呢.- 随时继承百亿家业的Scope(Chain) 周生生 ...

  3. 「译」一起探讨 JavaScript 的对象

    「译」一起探讨 JavaScript 的对象 原文地址:Let's explore objects in JavaScript 原文作者:Cristi Salcescu 译文出自:阿里云翻译小组 译文 ...

  4. 详解 Chrome 「V8 」引擎,让你更懂JavaScript !

    今天来聊聊 V8,它的主要职责是用来执行 JavaScript 代码的.在正式全面了解 V8 之前,先来了解下「JavaScript 的基本特性和设计思想」. 1.JavaScript 的基本特性和设 ...

  5. 变量、中文-「译」javascript 的 12 个怪癖(quirks)-by小雨

    在写这篇文章之前,xxx已经写过了几篇关于改变量.中文-主题的文章,想要懂得的朋友可以去翻一下之前的文章 原文:12 JavaScript quirks 译文:「译」javascript 的 12 个 ...

  6. javascript最新版本_JavaScript 引擎「V8」发布 8.0 版本,内存占用量大幅下降

    上周,JavaScript 引擎「V8」的开发团队在该项目官方网站上正式宣布推出最新的 8.0 版本.这次更新的重点主要集中在错误修复及性能改善上,正式的版本将在数周后随着谷歌 Chrome 80 稳 ...

  7. es6删除数组某一项_「JavaScript 从入门到精通」10.数组

    往期回顾 「JavaScript 从入门到精通」1.语法和数据类型 「JavaScript 从入门到精通」2.流程控制和错误处理 「JavaScript 从入门到精通」3.循环和迭代 「JavaScr ...

  8. extjs中滚动条属性_36个工作中常用的JavaScript函数片段「值得收藏」

    作者:Eno_Yao 转发链接:https://segmentfault.com/a/1190000022623676 前言 如果文章和笔记能带您一丝帮助或者启发,请不要吝啬你的赞和收藏,你的肯定是我 ...

  9. R和Python谁更好?这次让你「鱼与熊掌」兼得

    作者 | Parul Pandey 译者 | 大鱼 责编 | Jane 出品 | Python大本营(公众号id:pythonnews) 如果你从事在数据科学领域,提到编程语言,一定能马上想到 R 语 ...

  10. jvm 系列(九):如何优化 Java GC 「译」

    本文由CrowHawk翻译,地址:如何优化Java GC「译」,是Java GC调优的经典佳作. Sangmin Lee发表在Cubrid上的"Become a Java GC Expert ...

最新文章

  1. Unity进阶技巧 - RectTransform详解
  2. [POJ 3345] Bribing FIPA
  3. 垃圾回收机制之标记清除算法
  4. SpringBoot如何把mysql中的数据显示到html页面上?
  5. 区块链软件公司:区块链使用程序如何成为战胜商场应战的垫脚石
  6. jquery表单数据反序列化为字典
  7. (九)python3 只需3小时带你轻松入门——函数自定义
  8. mall-swarm是一套微服务商城系统
  9. url 加密解密, email 加密
  10. java中正则表达式,编译报错:Invalid escape sequence (valid ones are \b \t \n \f \r \ \' \\ )...
  11. 高性能服务器中的C10K问题
  12. 信息熵、互信息、KL散度
  13. 业务如何驱动技术发展
  14. matlab 股票分时图_利用Matlab读取股市数据
  15. Noya批量加解密工具使用方法
  16. 图片倒影控件ReflectionImage
  17. 中国计算机制造业比较优势分析,在全球产业链中,中国制造业拥有哪些显著的比较优势?()...
  18. 成为智者的四个敌人——唐望
  19. mysql decimal、numeric数据类型
  20. Opencv-Python提取掌纹图片ROI

热门文章

  1. [wcf]入门.3.1
  2. 提示wininet.dll文件找不到的解决
  3. oracle数据库状态是started,ORACLE数据库状态与v$instance视图
  4. 华为的手册和官网视频,学习网络基础
  5. DPDK收发包流程分析(一)
  6. SRv6技术课堂:SRv6可靠性方案(一)
  7. 编译mcu media server
  8. ffmpeg编码个参数的设置以及作用
  9. 【多媒体封装格式详解】---MKV【3】完
  10. B - 好数 51Nod - 1717