JS创建对象篇
-
Object构造函数创建
var person = new Object();
person.name = "Tom";
person.age = 10;
person.sayName = function(){
alert(this.name);
}
-
对象字面量
var person = {
name : "Tom",//注意这边是以“,"分隔,而不是”;"
age : 10,
sayName : function(){
alert(this.name);
}
这两种创建对象的方式都比较简便,可以用来创建单个对象。但是如果使用一个接口创建多个对象时会产生大量的重复代码。
-
工厂模式
function createPerson(name,age)
{ var o = new Object();//显示的创建一个对象
o.name = name;
o.age = age;
o.sayName = function(){
alert(this.name);
}
return o;//返回对象
}
var person1 = createPerson("Tom",10);
var person2 = createPerson("Jack",9);
工厂模式抽象了创建具体对象的过程这样我们就可以传入参数创建多个对象了。但是没有解决对象的识别问题
-
构造函数模式
function Person(name,age)
{
this.name = name;
this.age = age;
this.sayName = function(){
alert(this.name);
}
}
var person1 = new Person("Tom",10);
var person2 = new Person("Jack",9);
//对象构造函数(constructor属性)都指向Person
console.log(person1.constructor == Person);//true
//用instanceOf来检测类型
console.log(person1 instanceOf Object);//true
console.log(person2 instanceOf Person);//true
构造函数和普通函数的区别只是调用的方式有所差别,任何函数,只要new操作符来调用的话都作为构造函数(this的绑定规则),但是构造函数模式也存在问题:每个方法都要在每个实例上重新创建一遍。
console.log(person1.sayName == person2.sayName);//false
-
原型模式
我们创建的每一个函数都有一个prototype属性,这个属性是一个指针,指向一个对象
function Person(){}
Person.prototype.name = "Tom";
Person.prototype.age = 10;
Person.prototype.sayName = function(){
alert(this.name);
}
var person1 = new Person();
person1.sayName();//Tom
var person2 = new Person();
person2.sayName();//Tom
console.log(person1.sayName == person2.sayName);//true
跟构造函数模式不同,添加在prototype中的所有属性和方法都是共享的,也就是说person1和person2访问的都是同一组属性和同一个方法
每当代码读取某个属性(eg:alert(person1.job))的时候,都会执行一次搜索,目标是具有给定名字的属性,首先从对象实例本身开始,如果找到该属性则返回该属性的值,如果没有找到,则继续在原型链上向上查找,直到找到该属性停止,如果查找到原型链顶部,但是仍然没有找到指定的属性,就会返回 undefined;
对某个属性赋值(修改)(eg:person1.job="teacher")的时候,如果job存在于person1中,便会修改该属性的值。如果不在该person1中,则向原型链上查找但是这时候需要分析:
-
如果在原型链上找到了job,并且没有被标记为(writable:false),那么会在person1上添加一个名为job的新属性并设置它的值,这就是屏蔽属性。
-
如果在原型链上找到了job,但是它被标记为(writable:false),那么无法修改已有属性或是在person1上创建属性屏蔽,并且在严格模式下运行的话,代码会抛出一个错误,在非严格模式下,忽略该条语句。
-
如果在原型链上找到了job并且他是一个setter,那么一定会调用这个setter,job不会添加到person1上,也不会重新定义job这个setter。
如果查找到原型链顶部,但是仍然没有找到指定的属性,那么这条语句(person1.job="teacher"),便会在person1中添加一个新的属性并设置它的值。更简单的原型语法
function Person(){}
Person.prototype = {
name : "Tom",
age : 10,
sayName : function(){
alert(this.name);
}
};
这里我们重写了原型对象,但是constructor属性不在指向Person了(指向Object)
var person3 = new Person();
console.log(person3.constructor == Person);//false
console.log(person3.constructor == Object);//true
所以如果constructor属性的值很重要,那就要特意设置它的值
function Person(){}
Person.prototype = {
name : "Tom",
age : 10,
sayName : function(){
alert(this.name);
}
};
Object.defineProperty(Person.prototype,"constructor",
{
enumerable:false,
value:Person
});
//如果直接在对象中添加,会被枚举出来。
-
构造函数模式和原型模式的组合
构造函数模式用来定义实例属性(非共享),原型模式用来定义共享的属性或方法(共享)
function Person(name,age)
{
this.name = name;
this.age = age;
this.frieds = ["Jack","Sam"];
}
Person.prototype = {
sayName : function(){
alert(this.name);
}
}
Object.defineProperty(Person.prototype,"constructor",
{
enumerable:false,
value:Person
});
-
寄生构造函数模式
如果在前面的集中模式都不适用的情况下,可以使用寄生构造函数模式。
function Person(name,age)
{ var o = new Object();
o.name = name;
o.age = age;
o.sayName = function(){
alert(this.name);
}
return o;
}
var person1 = new Person("Tom",10);
除了调用是用new操作符之外,跟工厂模式一模一样,建议可以使用其他模式的情况,不要使用这种模式
-
稳妥构造函数模式
稳妥,指的是没有公共属性,而且其他方法也不引用this的对象
function Person(name,age)
{ var o = new Object();
//可以在这里定义私有变量和函数
//除了sayName()方法外没有别的方式可以访问其数据成员
o.sayName = function(){
alert(name);
}
return o;
}
var person1 = new Person("Tom",10);