标签:
继承主要依靠原型链来实现。
基本概念:
1)每个构造函数都有一个原型对象,原型对象有一个指向构造函数的指针,实例有一个指向原型对象的指针。
2)访问某个实例属性时, 先在实例中搜索,如果没有,则沿着原型链继续搜索,若找不到,则直到原型链末端(Object的原型)才会消停。
简单的实现如下:
function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } //继承了 SuperType SubType.prototype = new SuperType(); SubType.prototype.getSubValue = function (){ return this.subproperty; }; var instance = new SubType(); alert(instance.getSuperValue()); //true
其本质就是子类的原型是父类的实例,这样,子类原型就有了父类实例所拥有的所有方法和属性(虽然最好还是别拥有属性),并且还有指向父类原型的指针。而instance.constructor已经是父类构造方法了。像这样的关系:
再说说这种方式的问题。
原型中有了属性,所有的实例都会共享该属性,而如果该属性是引用类型,那么一旦某个实例改了该属性,所有实例都会受牵连。
还有,创建子类型实例时,不能向父类的构造函数中传参。
鉴于此,这种方式还是不用的好。
简单说,就是在子类构造函数内调用父类构造函数。
至于调用方式,通过apply()和call()来实现。像这样:
function SuperType(name){ this.name = name; } function SubType(){ //继承了 SuperType,同时还传递了参数 SuperType.call(this, "Nicholas"); //实例属性 this.age = 29; } var instance = new SubType(); alert(instance.name); //"Nicholas"; alert(instance.age); //29
如果所有方法在构造函数中定义,那么这种方式再好不过了,但,基本不可能的。
前两种方式各有利弊,扬长避短合成一种最常用的继承方式:使用原型链实现对原型属性和方法的继承,借用构造函数实现对实例属性的继承。
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
其实,子类的实例中都有两组colors和name属性,一组是调用SuperType 构造函数时,SubType.prototype 会得到两个属性colors和name(都是SuperType的实例属性),另一组是在SubType的构造函数中使用call()调用的SuperType构造函数,这次在新对象上增加了两个实例属性colors和name。这两个实例属性会屏蔽原型中的同名属性。像这样:
说白了,就是浅复制, 类似于Object.create(),这样,每个复制体的基本类型相互独立,引用类型共享。
主要函数如下:
function object(o){ function F(){} F.prototype = o; return new F(); }
eg:
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = object(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); var yetAnotherPerson = object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
就是创建一个 用于封装过程的函数,如同工厂模式。
function createAnother(original){ var clone = object(original); //通过调用函数创建一个新对象 clone.sayHi = function(){ //以某种方式来增强这个对象 alert("hi"); }; return clone; //返回这个对象 } var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = createAnother(person); anotherPerson.sayHi(); //"hi"
个人觉得这种方式用处不大,就是为了说明下面这种方式的。
组合继承的问题在于创建子类实例时,会两次调用父类构造函数(创建子类原型和子类构造函数内部使用call()时)。
根本就无需为了指定子类型的原型而去调用父类型的构造方法,只要有一个父类原型的副本就可以了。如下方式:
function inheritPrototype(subType, superType){ var prototype = object(superType.prototype); //创建对象 prototype.constructor = subType; //增强对象 subType.prototype = prototype; //指定对象 }
这样,就可以使用inheritPrototype(SubType, SuperType)替换SubType.prototype = new SuperType()了。
当然,在object()函数内部也执行了一次new操作。
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); };
这种方式避免了在子类原型中创建多余的属性。
标签:
原文地址:http://www.cnblogs.com/sduzhangxin/p/4355952.html