您的位置:首页 > Web前端 > JavaScript

javascript继承

2016-08-19 16:23 183 查看
javascript继承

JavaScript的继承方法有六种:原型链、借用构造函数、组合继承、原型式继承、寄生式继承、寄生组合式继承。

1、原型链继承

原型链继承的本质是:重写原型对象。

function Person (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
console.log('hello, my name is ' + this.name);
};
function Man() {
}
Man.prototype = new Person('pursue');
var man1 = new Man();
man1.say(); //hello, my name is pursue
var man2 = new Man();
console.log(man1.say === man2.say);//true
console.log(man1.name === man2.name);//true


这种继承方式很直接,为了获取Person的所有属性方法(实例上的和原型上的),直接将父类的实例new Person(‘pursue’)赋给了子类的原型,其实子类的实例man1,man2本身是一个完全空的对象,所有的属性和方法都得去原型链上去找,因而找到的属性方法都是同一个。

所以直接利用原型链继承是不现实的。

存在的问题:引用类型值的原型属性会被所有实例共享。并且子类型还无法给超类型传递参数。

2、借用构造函数继承

本质:在子类构造函数内部调用超类构造函数。严格的借用构造函数继承并不是很常见,一般都是组合着用

function Person (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
console.log('hello, my name is ' + this.name);
};
function Man(name, age) {
Person.apply(this, arguments);
}
//Man.prototype = new Person('pursue');
var man1 = new Man('joe');
var man2 = new Man('david');
console.log(man1.name === man2.name);//false
man1.say(); //say is not a function


这里子类的在构造函数里利用了apply去调用父类的构造函数,从而达到继承父类属性的效果,比直接利用原型链要好的多,至少每个实例都有自己那一份资源,但是这种办法只能继承父类的实例属性,因而找不到say方法,为了继承父类所有的属性和方法,则就要修改原型链,从而引入了组合继承方式。

优点: 1、可以向父类传递参数; 2、解决了原型链继承引用类型值的原型属性会被所有实例共享的问题。

缺点: 构造函数的模式存在问题,方法都在构造函数中定义,复用性差;父类定义的方法在子类中不可见。

所以我们需要原型链+借用构造函数的模式,这种模式称为组合继承。

3、组合继承

本质:原型链继承和借用构造函数继承的结合。组合继承时最常用的一种继承方式。

原型链:对原型属性和方法的继承。

借用构造函数继承:父类定义的方法,对子类而言不可见。

function Person (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
console.log('hello, my name is ' + this.name);
};
function Man(name, age) {
Person.apply(this, arguments);
}
Man.prototype = new Person();
var man1 = new Man('joe');
var man2 = new Man('david');
console.log(man1.name === man2.name);//false
console.log(man1.say === man2.say);//true
man1.say(); //hello, my name is joe


需要注意的是man1和man2的实例属性其实是覆盖了原型属性,但是并没要覆盖掉原型上的say方法(因为它们没有),所以这里man1.say === man2.say依然返回true,因而需要十分小心没有覆盖掉的原型属性,因为它是所有实例共有的。

组合式继承是比较常用的一种继承方法,其背后的思路是:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又保证每个实例都有它自己的属性。

组合继承虽然实现了需求:共享函数,但不共享属性,可是它是有不足之处:我们在独立属性时只是希望实例有各自的属性就好了,不需要原型中也存在属性,这就多余了。

4、原型式继承

本质:借助原型可以基于已有的对象创建新对象,同时不必创建自定义类型。

function object(o){
function F(){}
F.prototype =o;
return new F();
}
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"


原型式继承首先在object ()函数内部创建一个临时性的构造函数 ,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。

5、寄生式继承

这种继承方式是把原型式+工厂模式结合起来,目的是为了封装创建的过程。即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。

function object(o){
function F(){}
F.prototype = o;
return new F();
}
function createAnother(original){
var f = object(original); //通过调用函数创建一个新对象
f.sayHi = function(){ //以某种方式来增强这个对象
alert("hi");
};
return f;
}
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"


在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。前面示范继承模式时使用的object()函数不是必需的;任何能够返回新对象的函数都适用于此模式。

6、寄生组合式继承

组合继承是javascript中最常用的继承模式,但是仍存在不足之处,继承的最大问题是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。下面来介绍寄生组合式继承方法。

寄生组合式继承:通过借用构造函数来继承属性,通过原型链的混合形式来继承方法。基本思想是:不必为了指定子类的原型而调用超类型的构造函数,我们需要的仅是超类型的副本,使用寄生式来继承超类型的原型,然后再将结果指定给子类型的原型。

function Person (name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function(){
console.log('hello, my name is ' + this.name);
};
function Man(name, age) {
Person.apply(this, arguments);
}
Man.prototype = Object.create(Person.prototype);//a.
Man.prototype.constructor = Man;//b.
var man1 = new Man('pursue');
var man2 = new Man('joe');
console.log(man1.say == man2.say);
console.log(man1.name == man2.name);


其实寄生组合继承和上面的组合继承区别仅在于构造子类原型对象的方式上(a.和b.),这里用到了Object.creat(obj)方法,该方法会对传入的obj对象进行浅拷贝,类似于:

function create(obj){
function T(){};
T.prototype = obj;
return new T();
}


因此,a.会将子类的原型对象与父类的原型对象进行很好的连接,而并不像一般的组合继承那样直接对子类的原型进行复制(如Man.prototype = new Person();),这样只是很暴力的在对属性进行覆盖。而寄生组合继承方式则对实例属性和原型属性分别进行了继承,在实现上更加合理。

注意:代码b.并不会改变instanceof的结果,但是对于需要用到construcor的场景,这么做更加严谨。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  javascript 继承