JavaScript OOP 之「创建对象」
工厂模式
工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题。
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操作符的思考):
- 创建一个对象
- 将构造函数的作用域赋给新的对象(因此 this 就指向了这个新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象
原型模式
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 之「创建对象」的更多相关文章
- 「2014-3-13」Javascript Engine, Java VM, Python interpreter, PyPy – a glance
提要: url anchor (ajax) => javascript engine (1~4 articles) => java VM vs. python interpreter =& ...
- 「译」JavaScript 的怪癖 1:隐式类型转换
原文:JavaScript quirk 1: implicit conversion of values 译文:「译」JavaScript 的怪癖 1:隐式类型转换 译者:justjavac 零:提要 ...
- JavaScript 引擎「V8」发布 8.0 版本,内存占用量大幅下降
上周,JavaScript 引擎「V8」的开发团队在该项目官方网站上正式宣布推出最新的 8.0 版本.这次更新的重点主要集中在错误修复及性能改善上,正式的版本将在数周后随着谷歌 Chrome 80 稳 ...
- 「MoreThanJava」Day 4:面向对象基础
「MoreThanJava」 宣扬的是 「学习,不止 CODE」,本系列 Java 基础教程是自己在结合各方面的知识之后,对 Java 基础的一个总回顾,旨在 「帮助新朋友快速高质量的学习」. 当然 ...
- jvm系列(十):如何优化Java GC「译」
本文由CrowHawk翻译,是Java GC调优的经典佳作. 本文翻译自Sangmin Lee发表在Cubrid上的"Become a Java GC Expert"系列文章的第三 ...
- 一个「学渣」从零开始的Web前端自学之路
从 13 年专科毕业开始,一路跌跌撞撞走了很多弯路,做过餐厅服务员,进过工厂干过流水线,做过客服,干过电话销售可以说经历相当的“丰富”. 最后的机缘巧合下,走上了前端开发之路,作为一个非计算机专业且低 ...
- 使用JavaScript OOP特性搭建Web应用
最近,我面试了一个有五年 Web 应用程序开发经验的软件开发人员.四年半来她一直在从事 JavaScript 相关的工作,她自认为 JavaScript 技能非常好,但在不久之后我就发现实际上她对 J ...
- 「模板」 树链剖分 HLD
「模板」 树链剖分 HLD 不懂OOP的OIer乱用OOP出人命了. 谨此纪念人生第一次类套类. 以及第一次OI相关代码打过200行. #include <algorithm> #incl ...
- 【微信小程序】开发实战 之 「配置项」与「逻辑层」
微信小程序作为微信生态重要的一环,在实际生活.工作.商业中的应用越来越广泛.想学习微信小程序开发的朋友也越来越多,本文将在小程序框架的基础上就微信小程序项目开发所必需的基础知识及语法特点进行了详细总结 ...
随机推荐
- ASP.NET Core 中文文档 第二章 指南(4.1)ASP.NET Core MVC 与 Visual Studio 入门
原文:Getting started with ASP.NET Core MVC and Visual Studio 作者:Rick Anderson 翻译:娄宇(Lyrics) 校对:刘怡(Alex ...
- ASP.NET Core 中文文档 第四章 MVC(3.6.1 )Tag Helpers 介绍
原文:Introduction to Tag Helpers 作者:Rick Anderson 翻译:刘浩杨 校对:高嵩(Jack) 什么是 Tag Helpers? Tag Helpers 提供了什 ...
- .NET Core 1.1 发布 文档下载资源汇总
.NET Core 1.1 RTM 版2016/11/16 发布.对应发布 ASP.NET Core 1.1 .EF Core 1.1. 你可以通过Visual Studio 2015, Visual ...
- [C1] 分离 C1FlexGrid 滚动条
一 场景介绍 Silverlight 5.0 的 C1FlexGrid 控件里自带的滚动条,是嵌入在 C1FlexGrid 宽度和高度的范围里的,效果如下图所示: (未隐藏自带滚动条) (隐藏自带的 ...
- Visual Studio (VSIX,项目模板 )制作
下载Vsiual Studio 2012 SDK 下载地址:http://www.microsoft.com/en-us/download/details.aspx?id=30668 提示:一定要注意 ...
- Day01 login module
知识点:模块导入 变量赋值的两种形式 格式化输出 for循环 if...else 嵌套 #!C:\Program Files\Python35/bin # -*- conding:utf-8 ...
- Position属性四个值:static、fixed、relative、absolute的区别和用法
1.static(静态定位):默认值.没有定位,元素出现在正常的文档流中(如果设置 top, bottom, left, right, z-index这些属性就不起做作了). 2.relative(相 ...
- 浅谈Angular的 $q, defer, promise
浅谈Angular的 $q, defer, promise 时间 2016-01-13 00:28:00 博客园-原创精华区 原文 http://www.cnblogs.com/big-snow/ ...
- cmd命令汇总
一 cmd 命令 cmd命令大全(第一部分) winver---------检查Windows版本 wmimgmt.msc----打开windows管理体系结构(WMI) wupdmgr------ ...
- js生成一个不重复的ID的函数的进化之路
在MongoDB中的ObjectID,可以理解为是一个不会重复的ID,这里有个链接http://blog.csdn.net/xiamizy/article/details/41521025感兴趣可以去 ...