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 ...
- 【微信小程序】开发实战 之 「配置项」与「逻辑层」
微信小程序作为微信生态重要的一环,在实际生活.工作.商业中的应用越来越广泛.想学习微信小程序开发的朋友也越来越多,本文将在小程序框架的基础上就微信小程序项目开发所必需的基础知识及语法特点进行了详细总结 ...
随机推荐
- 最适合作为Java基础面试题之Singleton模式
看似只是最简单的一种设计模式,可细细挖掘,static.synchronized.volatile关键字.内部类.对象克隆.序列化.枚举类型.反射和类加载机制等基础却又不易理解透彻的Java知识纷纷呼 ...
- 学习javascript数据结构(二)——链表
前言 人生总是直向前行走,从不留下什么. 原文地址:学习javascript数据结构(二)--链表 博主博客地址:Damonare的个人博客 正文 链表简介 上一篇博客-学习javascript数据结 ...
- internet协议入门
前言 劳于读书,逸于作文. 原文地址:internet协议入门 博主博客地址:Damonare的个人博客 博主之前写过一篇博客:网络协议分析,在这篇博客里通过抓包,具体的分析了不同网络协议的传送的数据 ...
- jQuery使用
jQuery jQuery是一个快速,小,功能丰富的JavaScript库. 它使 HTML文档遍历和操作.事件处理. 动画和Ajax更简单和易于使用的API,在工作 众多的浏览器. 和多功能性的结合 ...
- Be a new gentlemen
学好技术的同时,更要注重自身素养的提升! 一 .有则改之,无责加冕 1.女士优先 2. 不随地吐痰, 不乱扔垃圾, 不在人群中抽烟 3. 不大声喧哗 4. 不插队,碰到别人要说抱歉 5. 不在公共交 ...
- ubuntu安装navicat及常见问题解决
1.安装navicat Step1: 下载Navicat ,网址:http://www.navicat.com/en/download/download.html Step2:进入下载目录,解压压缩包 ...
- Linux网络查看命令
1.ifconfig 查看当前生效的网卡. 2.ifdown ifup 网卡禁用与启动. 3.netstat -tuln 查看当前tcp/udp通讯端口连接情况. 4.netstat -an 查看当前 ...
- Java中使用IO流实现大文件的分裂与合并
文件分割应该算一个比较实用的功能,举例子说明吧比如说:你有一个3G的文件要从一台电脑Copy到另一台电脑, 但是你的存储设备(比如SD卡)只有1G ,这个时候就可以把这个文件切割成3个1G的文件 ,分 ...
- Normalize.css的使用及下载
Normalize.css 只是一个很小的CSS文件,但它在默认的HTML元素样式上提供了跨浏览器的高度一致性.相比于传统的CSS reset,Normalize.css是一种现代的.为HTML5准备 ...
- freeswitch呼叫流程分析
今天翻文档时发现之前整理的关于freeswitch呼叫相关的内容,写成博文分享出来也方便我以后查阅. 整体结构图 FreeswitchCore 模块加载过程 freeswitch主程序初始化时会从mo ...