对象的创建除了通过字面量,常用模式还包括:
- 工厂模式
- 构造函数模式
- 原型模式
- 构造函数与原型组合模式
- 动态原型模式
- 寄生构造函数模式
- 稳妥构造函数模式
我们逐一来看下:
工厂模式
function createPerson(name, age, job) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.job = job;
return obj;
}
var p1 = createPerson('wang', 28, 'programmer');
console.log(p1.name); //wang
优点:可以通过函数可以创建多个类似对象
缺点:无法识别对象,什么意思呢?看一下代码:
var str = new String();
console.log(str instanceof Object); //true
console.log(str instanceof String); //true
console.log(p1 instanceof Object); //true
console.log(p1 instanceof createPerson); //false
通过new String()创建的str,不仅可以识别出是Object,还可以识别出是String
而p1却不行
构造函数模式
function Person(name) {
this.name = name;
this.sayName = function () {
console.log(this.name);
}
}
var p2 = new Person('wang');
console.log(p2.name); //wang
console.log(p2 instanceof Object); //true
console.log(p2 instanceof Person); //true
优点:创建的对象可以很好的识别
缺点:定义的方法在多次实例化时会重复创建,耗费资源
验证一下:
var p3 = new Person('wang');
console.log(p2.name == p3.name); //true
console.log(p2.sayName == p3.sayName); //false
创建同样属性的对象,尽管属性值相同,但方法并不相同
我们知道函数名只是指向函数的指针,所以可以通过指针指向同一个函数
function Person(name){
this.name = name;
this.sayName = sayName;
}
function sayName(){
console.log(this.name);
}
var p2 = new Person('wang');
var p3 = new Person('wang');
console.log(p2.sayName == p3.sayName); //true
这样将方法提到全局,构造函数内的方法名只赋值函数名指针即可
但这样如果需要的方法较多,会造成全局函数滥用
原型模式
每个函数都有prototype(原型)属性,这个属性是一个指针,指向一个对象,叫原型对象
原型对象包含着由特定类型的所有实例共享的属性和方法
根据定义可以将共用的属性和方法放在原型对象中,实现共享
function Person() { }
Person.prototype.name = 'wang'
Person.prototype.sayName = function () {
console.log(this.name);
}
var p1 = new Person();
p1.sayName(); //wang
var p2 = new Person();
p2.name = 'zhang';
p2.sayName(); //zhang
console.log(p1.sayName == p2.sayName); //true
这样即可以灵活定义,方法又指向同一个函数
优点:很好的解决了方法函数的共享问题
缺点:对象的属性都指定了默认值(小问题),对于引用类型的属性共享后,在某个对象中修改会,会影响到其他对象的同名属性(大问题)
验证一下:
function Person() { }
Person.prototype.name = 'wang'
Person.prototype.friends = ['zhang', 'liu'];
var p1 = new Person();
console.log(p1.friends); // ["zhang", "liu"]
var p2 = new Person();
p2.friends.push('li');
console.log(p1.friends); // ["zhang", "liu", "li"]
p1.friends的两次输出不一致,受到p2修改属性的影响了
构造函数与原型组合模式
function Person(name,friends) {
this.name = name;
this.friends = friends;
}
var p1 = new Person('wang', ['zhang', 'liu']);
console.log(p1.friends); //["zhang", "liu"]
var p2 = new Person('wang', ['zhang', 'liu']);
p2.friends.push('li');
console.log(p1.friends); //["zhang", "liu"]
通过构造函数与原型的组合,就可以避免上面的问题
这种组合模式,是目前使用最广泛、认同度最高的一种创建自定义类型的方法,也可以说是用来定义引用类型的一种默认模式。
动态原型模式
对于将构造函数与原型分开独立的写法,不够直观
function Person(name, friends) {
this.name = name;
this.friends = friends;
if (typeof this.sayName != 'function') {
Person.prototype.sayName = function () {
console.log(this.name);
}
}
}
在每次调用执行构造函数时,会判断原型中sayName 是否存在,如果存在则证明函数的原型对象已经定义过,所以方法定义部分只会执行一次。
寄生构造函数模式
function Person(name) {
var obj = new Object();
obj.name = name;
obj.sayName = function () {
console.log(this.name);
}
return obj;
}
var p1 = new Person('wang');
p1.sayName(); //wang
寄生构造与普通工厂很类似,不同之处在于实例化对象时用到了new
寄生构造与构造函数很类似,不同之处在于返回的对象并不是函数的this,而是与函数没有关系的另一个对象
基本思想:创建一个函数,作用仅仅是封装创建对象的代码,然后返回新创建的对象
用途:在特殊情况下用来为对象创建构造函数
假设想创建一个具有额外方法的特殊数组,但不能直接修改Array构造函数
function SpecialArray(){
var values = new Array();
values.push.apply(values,arguments);
values.toPipedString=function(){
return this.join('-');
}
return values;
}
var nums = new SpecialArray(1,2,3,4,5);
console.log(nums.toPipedString()); //1-2-3-4-5
这种模式下返回的对象与构造函数没有关系,也就不能用instanceof确定类型
所以可用其他模式情况下,优先使用其他模式
稳妥构造函数模式
稳妥对象,指没有公共属性,而且其方法也不引用this的对象
适用于某些安全环境(禁用this和new),或者防止数据被其他程序改动时使用
function Person(name) {
var _age;
var obj = new Object();
obj.age = function(age){
_age = age
};
obj.sayAge = function () {
console.log(_age);
}
obj.sayName = function () {
console.log(name);
}
return obj;
}
var p1 = Person('wang');
p1.sayName(); //wang
p1.age(18);
p1.sayAge(); //18
也就是说该模式下不用this和new,所以内部属性都用私有变量,更新属性需通过访问方法来操作
网友评论