创建对象
1.1 通过Object
对象创建
var person = new Object();
person.name = "Albert";
person.sayName = function(){
console.log(this.name);
};
1.2 通过字面量创建
var person = {
name : "Albert",
sayName : function(){
console.log(this.name)
}
};
Object
和字面量创建的问题:
创建很多对象的时候会产生大量重复代码。
1.3 工厂模式
function createPerson(name){
var o = new Object();
o.name = name;
o.sayName = function(){
console.log(this.name)
}
return o;
}
var person = createPerson("Albert");
工厂模式的问题:
无法识别对象类型,即无法通过instanceof
和constructor
来识别对象类型:
person instanceof ???;
person.constructor == ???;
1.4 构造函数
function Person(name){
this.name = name;
this.sayName = function(){
console.log(this.name)
}
}
var person = new Person("Albert");
console.log(person.constructor == Person)//true
console.log(person instanceof Person)//true
构造函数的问题:
每个方法都要在每个实例上重新创建一次,尤其是函数,这样每个Person
的实例都包含了一个不同的sayName
的函数实例。
注意1
构造函数没有return
语句。要创建Person
的新实例,必须采用new
操作符,new
操作符大体上完成了一下4件事情:
创建一个新的对象(本例中
Person
创建的新对象,记为person);将构造函数的作用域赋给新对象(this=>person);
执行构造函数中的代码(Person中的
this.name=name;this.say.....
);返回新对象
注意2
构造函数也是函数,如果不通过new
操作符调用,则作用环境为全局(浏览器中为windows
,node环境中为global
)
function Person(name){
this.name = name;
this.sayName = function(){
console.log(this.name)
}
}
Person("BB");
global.sayName()//BB
console.log(global.name)//BB
1.5 原型模式
function Person(name){}
Person.prototype.name = "Albert";
Person.prototype.sayName = function(){
console.log(this.name)
}
var person = new Person();
console.log(Object.getPrototypeOf(person)==Person.prototype);//true
浏览器支持:IE9+,这样所有的Person
实例共享name
属性及sayName
函数
注意1
对象的某个属性是否来自实例,可通过
hasOwnProperty()
来确定,如果是在原型中,则返回false
。判断对象是否具备属性,可以通过
in
操作符,例如console.log("name" in person)//true
来判断,不论是在原型还是实例中,都返回true
,通过for-in
循环时,实例及原型中均会被枚举。
注意2
在定义原型时,如果用字面量代替为prototype
属性定义,则原型的constructor
属性不会指向Person
。因为通过字面量定义,完全重写了默认的prototype
对象。但是此时instanceof
还是能够返回正确的结果。
function Person(name){};
Person.prototype={
name : "Albert",
sayName : function(){
console.log(this.name);
}
};
var person = new Person();
console.log(person instanceof Person);//true
console.log(person.constructor == Person);//false
console.log(person.constructor == Object);//true
所以可以再补充定义:
Object.defineProperty(Person.prototype,"constructor",{
enumerable:false,
value:Person
})
注意3
在重定义原型前,不能创建对象实例,否则会造成实例的原型指向错误
function Person(name){};
var person = new Person();
Person.prototype={
name : "Albert",
sayName : function(){
console.log(this.name);
}
};
person.sayName(); //error
此例中person
的原型被指向了Person
的默认原型,固调用sayName
函数会发生错误。
原型模式的问题:
小问题:为了省略构造函数传递初始化参数,所有的实例在默认情况下都会去的想通的属性值
原型属性被所有实例共享(适合
function
类型的值),而通常情况下,引用类型(Array
、Object
)属性值一般不希望对所有实例共享。
1.6 构造函数与原型组合
利用构造函数都是实例属性和原型的共享特性,分别定义对应的内容,组合共同完成对象创建,而且该模式还支持想构造函数传递参数。
function Person(name){
this.name = name;
this.friends = ["Bob","Harry"];//引用类型为实例属性
};
Person.prototype.sayName = function(){
console.log(this.name);
};
1.7 动态原型模式
将1.6中的组合封装在一个构造函数中的模式。具体方法为:检查某个应该存在的方法是否有效来决定是否需要初始化原型。
function Person(name){
this.name = name;
this.friends = ["Bob","Harry"];//引用类型为实例属性
//****ProtoType****
if(typeof this.sayName != "function"){
Person.prototype.sayName = function(){
console.log(this.name);
};
}
};
1.8 寄生构造模式
构造函数在不反回值的情况下,默认会返回新对象实例。
而通过在函数末尾添加return
语句,可以重写new
后调用函数时的返回值。
function Person(name){
var o = new Object();
o.name = name;
o.sayName = function(){
console.log(this.name);
};
return o;
};
var person = new Person("Albert");
console.log(person instanceof Person);//false
该函数除了使用new
操作符和把包装函数取名叫“构造函数”以外,和工厂模式其实是一模一样的。
该模式属于比较特殊的构造模式,可用于不允许修改原对象的情况。
function SpecialArray(){
var values = new Array();
values.push.apply(values,arguments);
values.toPipedString = function(){
return this.join("|");
};
return values
}
1.9 稳妥(durable
)构造函数模式
该模式构造出来的对象没有公共属性,不适用this
对象,不适用new
操作符,适用于在一些安全环境中,可防止数据被其它应用(如Mashup
)改动(利用闭包特性),类似于寄生构造函数模式,单不适用this
和new
。
function Person(name){
var o = new Object();
//****定义私有变量和函数****
var _name = name;
o.sayName = function(){
console.log(_name);
};
return o;
};
var person = Person("Albert");
这种模式中,除了sayName()
方法以外,没有其它办法访问_name
的值。