创建对象
- 工厂模式:
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person2 = createPerson("Greg", 27, "Doctor");
- 解析:虽然能创造对象,但是无法从constructor和instanceOf获取更多关于构造函数的信息,因为这里生成的对象的constructor都指向Object,而instanceOf只有Object为true.
- 正常构造函数模式:
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person2 = new Person("Greg", 27, "Doctor");
- 解析:函数里面没有return语句,必须要用new才能创建对象,如果直接调用Person,则里面的this会指向window,造成的结果是会在window上生成全局变量。
- 使用new可以生成新对象,具体步骤如下:
- 开辟内存,创建新对象
- 将构造函数的作用域赋给新对象(因此this指向这个新对象)
- 将新对象的proto指向构造函数默认的prototype指针指向的对象
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象。
-
特殊比较——寄生构造函数模式:
function Person(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}var friend = new Person("Nicholas", 29, "Software Engineer"); friend.sayName(); //"Nicholas"
- 解析:这里想要强调的是,当使用new操作符来对一个函数进行操作时,系统默认会创建新对象,此时还要根据函数中是否存在return值,来判断。如果没有返回值,则返回系统后台新创建的对象,如果有返回值而且是一个对象,则程序猿指定的返回值会代替新创建对象返回。
- 所以,上面new之后返回给friend的是o,与工厂模式中一样,不能依赖instanceof和constructor来确定对象的具体类型。
-
原型模式:
function Person(){
}Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); }; var person1 = new Person(); var person2 = new Person(); person1.name = "Greg"; alert(person1.name); //"Greg" – from instance alert(person2.name); //"Nicholas" – from prototype
对象继承
-
原型继承:
function SuperType(){
this.property = true;
}SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } //inherit from SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function (){ return this.subproperty; }; var instance = new SubType(); alert(instance.getSuperValue()); //true alert(instance instanceof Object); //true alert(instance instanceof SuperType); //true alert(instance instanceof SubType); //true alert(Object.prototype.isPrototypeOf(instance)); //true alert(SuperType.prototype.isPrototypeOf(instance)); //true alert(SubType.prototype.isPrototypeOf(instance)); //true
- 缺点:对象实例共享所有继承的属性和方法。
-
借用构造函数:
function SuperType(){
this.colors = ["red", "blue", "green"];
}function SubType(){ //inherit from SuperType SuperType.call(this); } var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green"
- 解析:这里实际上是在(未来将要)新创建的SubType实例的环境下调用了SuperType构造函数,这样就会在新SubType对象上执行SuperType()函数中定义的所有对象初始化代码,这样每个SubType实例都会具有自己的colors属性。
- 解决了上一方法中所有对象共享同一实例的缺点,每个对象都会调用SubType(),因此也都有自己的环境,会形成自己的实例属性。
-
组合继承(原型链+借用构造函数)
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ SuperType.call(this, name); //第二次调用超类构造函数 this.age = age; } SubType.prototype = new SuperType(); //第一次调用超类构造函数 SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function(){ alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" instance1.sayName(); //"Nicholas"; instance1.sayAge(); //29 var instance2 = new SubType("Greg", 27); alert(instance2.colors); //"red,blue,green" instance2.sayName(); //"Greg"; instance2.sayAge(); //27
- 解析:这种方法通过使用原型链实现对原型属性和方法的继承(需共享的数据),通过借用构造函数来实现对实例属性的继承(需要每个实例独立的数据)。
- 最终,子类会包含超类的全部实例属性。子类的原型与超类的原型会分开,是不同的对象,更改不会相互影响,但通过子类原型的proto可以取得超类的原型。
- 组合继承最大的问题:无论什么情况下都会两次调用超类构造函数,一次是在创建子类原型的时候,第二次是在子类构造函数内部call。
- instanceof和isPrototypeOf()也能够用于识别基于组合继承创建的对象。
- 原型式继承:
function object(o){
function F(){};
F.prototype = o;
return new F();
}
- 这意味着被传入的对象o成为了新对象的原型,新对象的proto直接指向o,并且当新对象对实例中不存在但原型上存在的属性操作时,对象o也会被随之改变。
- 原型式继承还可以使用ES5中的
Object.create()
,如下:
Square.prototype = Object.create(Rect.prototype,{
constructor:{
configure:true,
enumerable:true,
writable:true,
value:Square
}
}) - 解析:这样就实现了Square继承Rectangle的操作,还免去了生成中间构造函数F()的操作。
- 原型式继承进阶版——寄生式继承
function createAnother(original){
var clone = object(original); //这里沿用上面的object()函数
clone.sayHi = function(){
alert("hi");
}
return clone;
}
var person = {
name: "json",
age: 23
}
var newPerson = createAnother(person);
newPerson.sayHi(); //"hi"
- 解析:其实就是在上面基础上,增加了可以对继承后的对象添加实例方法或实例属性,然后这里继承以后,newPerson的proto指向person,两者修改其一,另一都会受到影响。
- 寄生组合式继承:
-
完整版代码:
function object(o){
function F(){}
F.prototype = o;
return new F();
}function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); //返回一个新对象,新对象的__proto__指向superType.prototype subType.prototype = prototype; //修改子类的原型,之后,subType的实例的__proto__会指向这个原型,而这个原型的__proto__会指向父类的原型superType.prototype,由此实现继承 subType.prototype.constructor = subType; //修正constructor } function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert(this.name); }; function SubType(name, age){ SuperType.call(this, name); this.age = age; } inheritPrototype(SubType, SuperType); SubType.prototype.sayAge = function(){ alert(this.age); }; var instance1 = new SubType("Nicholas", 29); instance1.colors.push("black"); alert(instance1.colors); //"red,blue,green,black" instance1.sayName(); //"Nicholas"; instance1.sayAge(); //29 var instance2 = new SubType("Greg", 27); alert(instance2.colors); //"red,blue,green" instance2.sayName(); //"Greg"; instance2.sayAge(); //27
解析: 与组合继承相比的高效率在于,只调用一次SuperType构造函数,并且避免了在SuperType.prototype上面创建不必要的多余属性。与此同时,原型链保持不变,能够用instanceof和isPrototypeOf()来判断。是引用类型最理想的继承范式。
这里说只调用一次SuperType构造函数,被省略掉那那一次是new SuperType(),因为这里如果直接new一个出来的话,虽然可以得到一个proto指向SuperType.prototype对象的对象,但是会存在冗余的SuperType实例属性,而本模式中的做法是,另用一个空函数,使它的prototype也指向SuperType.prototype,这样除了实例属性为空以外,其他都与SuperType()一样,此时new一个新函数的实例对象,就能取得一个proto指向SuperType.prototype对象的对象,最后将subType的prototype指向这个新new的实例对象,此时subType.prototype.proto = SuperType.prototype, 就实现了subType.prototype到SuperType.prototype的链接,形成了继承链,而subType.prototype里也没有冗余的值为空的SubperType的实例属性,Perfect!!!
-
代码注释:
- var prototype = object(superType.prototype); //返回一个新对象,新对象的proto指向superType.prototype
- subType.prototype = prototype; //修改子类的原型,之后,subType的实例的proto会指向这个原型,而这个原型的proto会指向父类的原型superType.prototype,由此实现继承