js继承

Daisy ·
更新时间:2024-11-10
· 973 次阅读

js继承原型链(很少单独使用)原型链的问题借用构造函数(很少单独使用)组合继承(最常用的继承模式)原型式继承寄生式继承寄生组合式继承(引用类型最理想的继承范式) 原型链(很少单独使用) function SuperType(){ this.property = true; } SuperType.prototype.getSuperValue = function(){ return this.property; }; function SubType(){ this.subproperty = false; } //继承SuperType SubType.prototype = new SuperType(); //给子类添加方法, //一定要放在SubType.prototype = new SuperType();语句之后 SubType.prototype.getSubValue = function (){ return this.subproperty; }; //实例化一个对象,具有SubType和SuperType的属性和方法 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

代码分析:
原型链

SuperType的实例当成SubType的原型,这样新原型就具有SuperType的所有属性和方法。

getSuperValue()方法仍然在superType.prototype中,但property则位于SubType.prototype中,为什么?
因为property是实例属性,而getSuperValue()是一个原型方法。SubType.prototypeSuperType的实例,所以property就位于该实例中了。

为什么instanceconstructor指向的是SuperType?
因为SubType的原型指向了另一个对象SuperType的原型,而这个原型对象的constructor指向SuperType

原型链的问题

一、包含引用类型值。
SuperType有一个引用类型值的属性colors,给SubType.prototype赋值new SuperType(),相当于给SubType.prototype添加了colors属性,那么使用SubType创建的实例就会共享原型属性,就会造成其中一个实例对其进行修改,另一个实例也会受到影响。

function SuperType(){ this.colors = ["red", "blue", "green"]; } function SubType(){ } //继承SuperType SubType.prototype = new SuperType(); 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,black"

二、没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。

借用构造函数(很少单独使用)

思想:在子类型构造函数的内部调用超类型的构造函数。

依旧上代码:

function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } function SubType(){ //继承了SuperType,同时还传递了参数 SuperType.call(this, "Nicholas"); //instance property this.age = 29; } var instance1 = new SubType(); instance1.colors.push("black"); alert(instance1.name); //"Nicholas"; alert(instance1.age); //29 alert(instance1.colors); //"red,blue,green,black" var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green"

分析:

使用call()apply(),可以在将来新创建的SubType实例的环境下调用SuperType构造函数,这样就会在新SubType对象上执行SuperType()函数中定义的所有对象初始化代码。SubType的每个实例就都会有自己的实例属性副本了。 SuperType还可以接收参数 注意:在调用超类型构造函数后,再添加应该在子类型中定义的属性。

借用构造函数的问题: 方法都在构造函数中定义,函数复用就无从谈起了。

组合继承(最常用的继承模式)

可以看如何创建一个对象–组合使用构造函数模式和原型模式。

文章里有说:构造函数模式用于定义实例属性,原型模式用于定义方法和共享的属性。这样每个实例都会有自己的一份实例属性的副本,但同时又共享着对方法的引用,最大限度的节省了内存。还支持传递参数。

上代码:

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);//第二次调用SuperType() this.age = age; } SubType.prototype = new SuperType();//第一次调用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()SubType.prototype会得到两个属性:namecolors

当调用SubType构造函数时会第二次调用SuperType构造函数,这一次又在新对象上创建了实例属性namecolors

这样导致有两组namecolors属性,一组在SubType原型中,一组在实例上。解决办法采用寄生组合式继承

属性在构造方法SuperType里定义,方法定义在原型SuperType.prototype上,这样不同实例instance1instance2既分别有自己的属性,又可以使用相同的方法。

原型式继承

道格拉斯·克罗克福德在2006年写过一篇文章,叫做JavaScript中的原型式继承。他的想法是借住原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。

//道格拉斯·克罗克福德给出的object函数 function object(o){ function F(){} //将传进来的参数作为原型的值 F.prototype = o; return new F(); } //已有对象 var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; //person作为原型的值,创造出anotherPerson实例,具有name和friends属性 //实际上相当于创建了一个person的副本 var anotherPerson = object(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); console.log(anotherPerson.name);//Greg console.log(anotherPerson.friends);//["Shelby", "Court", "Van", "Rob"] console.log(person.friends);//["Shelby", "Court", "Van", "Rob"] var yetAnotherPerson = object(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); console.log(anotherPerson.friends);//["Shelby", "Court", "Van", "Rob","Barbie"] alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

person作为原型的值,创造出anotherPersonyetAnotherPerson实例,具有namefriends属性, 实际上相当于创建了两个person的副本。

ECMAScript 5新增了Object.create()方法,规范了原型式继承。这个方法接收两个参数:

用作新对象原型的对象 为新对象定义额外属性的对象(可选),与Object.defineProperties()方法的第二个参数格式相同:每个属性都是通过自己的描述符定义的。以这种方式指定的任意属性都会覆盖原型对象上的同名属性

在只传入一个参数的时候和object()方法的行为相同。
包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样。

var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; var anotherPerson = Object.create(person, { name: { value: "Greg" } }); alert(anotherPerson.name); //"Greg"

在只想让一个对象与另一个对象保持类似的情况下,可以使用原型式继承。

寄生式继承

寄生式继承是与原型式继承紧密相关的一种思路,也是由克罗克福德提出的。
寄生式继承的思路与寄生构造函数和工厂模式类似,即:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像是它真的做了所有工作一样返回对象。

function object(o){ function F(){} //将传进来的参数作为原型的值 F.prototype = o; return new F(); } //继承original function createAnother(original){ //original是将要作为 新对象基础 的对象 //然后将这个对象传递给object(),返回值赋值给clone var clone = object(original);//通过调用函数创建一个新对象 //给clone添加新方法sayHi() clone.sayHi = function(){ //以某种凡是来增强这个对象 alert("Hi"); } //返回clone对象 return clone; } //作为基础对象,可以理解为父类 var person = { name:"Nicholas", friends:["Shelby","Court","Van"] }; //继承person,拥有person的属性和方法 var anotherPerson = createAnother(person); //还有自己的方法 anotherPerson.sayHi(); console.log(anotherPerson.name);//Nicholas console.log(anotherPerson.friends);// ["Shelby", "Court", "Van"]

object()函数不是必须的,任何能够返回新对象的函数都适用于此模式。

使用寄生模式来为对象添加函数,会由于不能做到函数复用而降低效率,这个特点与构造函数模式类似。

寄生组合式继承(引用类型最理想的继承范式)

寄生组合式继承:即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。
其背后思路:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型的一个副本而已。
本质:就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。

改造后的,就不会像组合继承中,会调用两次SuperType构造函数。

function object(o){ function F(){} F.prototype = o; return new F(); } //该方法接收两个参数,一个是子类型构造函数,一个是超类型构造函数 function inheritPrototype(subType, superType){ //第一步创建超类型原型的一个副本 var prototype = object(superType.prototype); //创建对象 //因为上一步重新原型,导致prototype失去了默认的constructor属性,所以这一步手动指定 prototype.constructor = subType; //增强对象 //将创建的对象副本复制给子类型的原型 subType.prototype = prototype; //指定对象 } //超类型构造函数 function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } //超类型原型方法 SuperType.prototype.sayName = function(){ alert(this.name); }; //子类型构造函数 function SubType(name, age){ //调用SuperType构造函数 SuperType.call(this, name); this.age = age; } //让SubType继承SuperType原型,inheritPrototype函数内部不会再次调用SuperType构造函数 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

YAHOO.lang.extend()方法采用了继承组合继承。。。。。。。。。。


作者:蘑菇李



js继承 js

需要 登录 后方可回复, 如果你还没有账号请 注册新账号