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

代码分析:
原型链

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

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

  3. 为什么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()方法采用了继承组合继承。。。。。。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值