# 构造器模式
在面向对象编程中,构造器是一个特殊函数:当新建对象的内存被分配后,用来初始化该对象。而在JavaScript中几乎所有的东西都是对象,所以我们会关注对象的构造器。
对象构造器是被用来创建特殊类型的对象的,首先它要准备使用的对象,其次在对象初次被创建时,通过接收参数,构造器要用来对成员的属性和方法进行赋值。
做插件、类库,基本逃离不了构造器模式。因为构造器里面有类(构造函数)和实例。实例和实例都是单独存在的,各个实例能拥有自己的私有属性或方法,又能共同调用所处构造函数原型上的公共属性和方法。
对象创建
下面是我们创建对象的三种基本方式,下面的每一种都会创建一个新的对象:
var newObject = {};// orvar newObject = Object.create( null );// orvar newObject = new Object();
为对象写入键值对
// ECMAScript 3 兼容形式// 1. “点号”法// 设置属性newObject.someKey = "Hello World";// 获取属性var key = newObject.someKey;// 2. “方括号”法// 设置属性newObject["someKey"] = "Hello World";// 获取属性var key = newObject["someKey"];// ECMAScript 5 仅兼容性形式// For more information see: http://kangax.github.com/es5-compat-table/// 3. Object.defineProperty方式// 设置属性Object.defineProperty( newObject, "someKey", {value: "for more control of the property's behavior",writable: true,enumerable: true,configurable: true});// 如果上面的方式你感到难以阅读,可以简短的写成下面这样:var defineProp = function ( obj, key, value ){config.value = value;Object.defineProperty( obj, key, config );};// 为了使用它,我们要创建一个“person”对象var person = Object.create( null );// 用属性构造对象defineProp( person, "car", "Delorean" );defineProp( person, "dateOfBirth", "1981" );defineProp( person, "hasBeard", false );// 4. Object.defineProperties方式// 设置属性Object.defineProperties( newObject, {"someKey": {value: "Hello World",writable: true},"anotherKey": {value: "Foo bar",writable: false}});// 3和4中的读取属性可用1和2中的任意一种
在这本书的后面一点,这些方法会被用于对象的继承,如下:
// 使用:// 创建一个继承与Person的赛车司机var driver = Object.create( person );// 设置司机的属性defineProp(driver, "topSpeed", "100mph");// 获取继承的属性 (1981)console.log( driver.dateOfBirth );// 获取我们设置的属性 (100mph)console.log( driver.topSpeed );
基础构造器
ES6以前,Javascript不支持类的概念,但它有一种与对象一起工作的构造器函数。使用new关键字来调用该函数,我们可以告诉Javascript把这个函数当做一个构造器来用,它可以用自己所定义的成员来初始化一个对象。
在这个构造器内部,关键字this引用到刚被创建的对象。回到对象创建,一个基本的构造函数看起来像这样:
function Car( model, year, miles ) {this.model = model;this.year = year;this.miles = miles;this.toString = function () {return this.model + " has done " + this.miles + " miles";};}// 使用:// 我们可以示例化一个Carvar civic = new Car( "Honda Civic", 2009, 20000 );var mondeo = new Car( "Ford Mondeo", 2010, 5000 );// 打开浏览器控制台查看这些对象toString()方法的输出值// output of the toString() method being called on// these objectsconsole.log( civic.toString() );console.log( mondeo.toString() );
上面这是个简单版本的构造器模式,但它还是有些问题。一个是难以继承,另一个是每个Car构造函数创建的对象中,自己定义的toString()之类的函数都被重新定义。这不是非常好,重复地被定义会占用内存,理想的情况是所有Car类型的对象都应该引用同一个函数。 这要谢谢 ECMAScript3和ECMAScript5-兼容版,对于构造对象他们提供了另外一些选择,解决限制小菜一碟。
使用“原型”的构造器
为解决上述基础构造器的问题,
function Car( model, year, miles ) {this.model = model;this.year = year;this.miles = miles;}// 注意这里我们使用Note here that we are using Object.prototype.newMethod 而不是// Object.prototype ,以避免我们重新定义原型对象Car.prototype.toString = function () {return this.model + " has done " + this.miles + " miles";};// 使用:var civic = new Car( "Honda Civic", 2009, 20000 );var mondeo = new Car( "Ford Mondeo", 2010, 5000 );console.log( civic.toString() );console.log( mondeo.toString() );
# 模块化模式
在JavaScript中,实现模块有几个选项,他们包括:
- 模块化模式
- 对象表示法
- AMD模块
- CommonJS 模块
- ECMAScript Harmony 模块
# 单例模式
单例模式也称为单体模式。规定一个类只有一个实例,并且提供可全局访问点。
补充知识
构造函数中,如果return一个对象,那么在new该构造函数所得到的对象就是永恒这个对象,而不管你构造函数。
为什么需要单例模式
因为有的类比较庞大和复杂,其实例对象的创建和销毁对资源消耗就比较大。如果频繁地创建和销毁这些对象,并且这些对象完全是可以复用的话,那么将会造成不必要的性能浪费。复用是指:我要一直使用new这个类后的实例对象的数据
下面根据代码理解何为具体复用:
// 利用构造函数模拟账户余额function Account() {this.balance = 100}// 查询账户余额let check = new Account()console.log(check);/* Account { balance: 100 } */// 修改账户余额check.balance = 80// 查询账户余额let check2 = new Account()console.log(check2);/* Account { balance: 100 } *//* ---------------------进行修正,使得能正确更新账户余额----------------------- */// 模拟账户余额let ins = {balance: 100}function Account() {return ins}// 查询账户余额let check = new Account()console.log(check);/* { balance: 100 } */// 修改账户余额check.balance = 80// 查询账户余额let check2 = new Account()console.log(check2);/* { balance: 80 } */
↑这样修正后,有问题:就是数据不连贯;而且在ins对象在全局,很容易修改或者被覆盖。
function Account() {this.balance = 100if(Account.instance) {// 如果函数对象的instance属性已经被赋值了,就返回这个属性return Account.instance}// 如果函数对象的instance属性未被赋值(即第一次new时),则为该属性赋值Account.instance = this/* 因为Account构造函数返回undefined,所以Account构造函数返回的是this指向的对象 */}// 查询账户余额let check = new Account()console.log(check);/* Account { balance: 100 } */// 修改账户余额check.balance = 80// 查询账户余额let check2 = new Account()console.log(check2);/* Account { balance: 80 } */
