深刻理解JavaScript系列(26):设计模式之构造函数模式

介绍

构造函数你们都很熟悉了,不过若是你是新手,仍是有必要来了解一下什么叫构造函数的。构造函数用于建立特定类型的对象——不只声明了使用的对象,构造函数还能够接受参数以便第一次建立对象的时候设置对象的成员值。你能够自定义本身的构造函数,而后在里面声明自定义类型对象的属性或方法。javascript

基本用法

在JavaScript里,构造函数一般是认为用来实现实例的,JavaScript没有类的概念,可是有特殊的构造函数。经过new关键字来调用定义的否早函数,你能够告诉JavaScript你要建立一个新对象而且新对象的成员声明都是构造函数里定义的。在构造函数内部,this关键字引用的是新建立的对象。基本用法以下:java

function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
this.output= function () {
return this.model + "走了" + this.miles + "千米";
};
}

var tom= new Car("大叔", 2009, 20000);
var dudu= new Car("Dudu", 2010, 5000);

console.log(tom.output());
console.log(dudu.output());

上面的例子是个很是简单的构造函数模式,可是有点小问题。首先是使用继承很麻烦了,其次output()在每次建立对象的时候都从新定义了,最好的方法是让全部Car类型的实例都共享这个output()方法,这样若是有大批量的实例的话,就会节约不少内存。函数

解决这个问题,咱们可使用以下方式:this

function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
this.output= formatCar;
}

function formatCar() {
return this.model + "走了" + this.miles + "千米";
}

这个方式虽然可用,可是咱们有以下更好的方式。spa

构造函数与原型

JavaScript里函数有个原型属性叫prototype,当调用构造函数建立对象的时候,全部该构造函数原型的属性在新建立对象上均可用。按照这样,多个Car对象实例能够共享同一个原型,咱们再扩展一下上例的代码:prototype

function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
}

/*
注意:这里咱们使用了Object.prototype.方法名,而不是Object.prototype
主要是用来避免重写定义原型prototype对象
*/
Car.prototype.output= function () {
return this.model + "走了" + this.miles + "千米";
};

var tom = new Car("大叔", 2009, 20000);
var dudu = new Car("Dudu", 2010, 5000);

console.log(tom.output());
console.log(dudu.output());

这里,output()单实例能够在全部Car对象实例里共享使用。code

另外:咱们推荐构造函数以大写字母开头,以便区分普通的函数。orm

只能用new吗?

上面的例子对函数car都是用new来建立对象的,只有这一种方式么?其实还有别的方式,咱们列举两种:对象

function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
// 自定义一个output输出内容
this.output = function () {
return this.model + "走了" + this.miles + "千米";
}
}

//方法1:做为函数调用
Car("大叔", 2009, 20000); //添加到window对象上
console.log(window.output());

//方法2:在另一个对象的做用域内调用
var o = new Object();
Car.call(o, "Dudu", 2010, 5000);
console.log(o.output());

该代码的方法1有点特殊,若是不适用new直接调用函数的话,this指向的是全局对象window,咱们来验证一下:blog

//做为函数调用
var tom = Car("大叔", 2009, 20000);
console.log(typeof tom); // "undefined"
console.log(window.output()); // "大叔走了20000千米"

这时候对象tom是undefined,而window.output()会正确输出结果,而若是使用new关键字则没有这个问题,验证以下:

//使用new 关键字
var tom = new Car("大叔", 2009, 20000);
console.log(typeof tom); // "object"
console.log(tom.output()); // "大叔走了20000千米"

强制使用new

上述的例子展现了不使用new的问题,那么咱们有没有办法让构造函数强制使用new关键字呢,答案是确定的,上代码:

function Car(model, year, miles) {
if (!(this instanceof Car)) {
return new Car(model, year, miles);
}
this.model = model;
this.year = year;
this.miles = miles;
this.output = function () {
return this.model + "走了" + this.miles + "千米";
}
}

var tom = new Car("大叔", 2009, 20000);
var dudu = Car("Dudu", 2010, 5000);

console.log(typeof tom); // "object"
console.log(tom.output()); // "大叔走了20000千米"
console.log(typeof dudu); // "object"
console.log(dudu.output()); // "Dudu走了5000千米"

经过判断this的instanceof是否是Car来决定返回new Car仍是继续执行代码,若是使用的是new关键字,则(this instanceof Car)为真,会继续执行下面的参数赋值,若是没有用new,(this instanceof Car)就为假,就会从新new一个实例返回。

原始包装函数

JavaScript里有3中原始包装函数:number, string, boolean,有时候两种都用:

// 使用原始包装函数
var s = new String("my string");
var n = new Number(101);
var b = new Boolean(true);


// 推荐这种
var s = "my string";
var n = 101;
var b = true;

推荐,只有在想保留数值状态的时候使用这些包装函数,关于区别能够参考下面的代码:

// 原始string
var greet = "Hello there";
// 使用split()方法分割
greet.split(' ')[0]; // "Hello"
// 给原始类型添加新属性不会报错
greet.smile = true;
// 单无法获取这个值(18章ECMAScript实现里咱们讲了为何)
console.log(typeof greet.smile); // "undefined"

// 原始string
var greet = new String("Hello there");
// 使用split()方法分割
greet.split(' ')[0]; // "Hello"
// 给包装函数类型添加新属性不会报错
greet.smile = true;
// 能够正常访问新属性
console.log(typeof greet.smile); // "boolean"

总结

本章主要讲解了构造函数模式的使用方法、调用方法以及new关键字的区别,但愿你们在使用的时候有所注意。

参考:http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#constructorpatternjavascript

相关文章
相关标签/搜索