Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JavaScript] 红宝书整理--继承 #16

Open
zgfang1993 opened this issue Mar 12, 2017 · 0 comments
Open

[JavaScript] 红宝书整理--继承 #16

zgfang1993 opened this issue Mar 12, 2017 · 0 comments

Comments

@zgfang1993
Copy link
Owner

zgfang1993 commented Mar 12, 2017

继承
#1 原型链的详细分析 可看此章节
接下来说说其他类型的继承

OO语言
|- 接口继承: 只继承方法签名
|- 实现继承: 继承实际的方法

由于函数没有签名,在ECMAScript中无法实现接口继承,只支持实现继承,而实现继承主要是依靠原型链来实现。

继承:

  • 原型链
  • 借用构造函数
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承

JavaScript主要通过原型链实现继承。使用最多的是组合继承。
原型链:
通过将一个类型的实例赋值给另一个构造函数的原型实现。(问题对象实例共享所有继承的属性和方法,不适宜单独使用)
借用构造函数:
在子类型构造函数的内部调用超类型构造函数。(每个实例都具有自己的属性)

组合继承:
使用原型链继承共享的属性和方法,通过借用构造函数继承实例属性。

原型式继承:
可以不必预先定义构造函数的情况下继承(执行对给定对象的浅复制,复制得到的副本还可以进一步改造)

寄生式继承:
与原型式继承非常相似,也是基于某个对象或某些信息创建一个对象,然后增强对象,返回对象。(为了解决组合继承由于多次调用超类构造函数而导致低效率问题,可将这个模式与组合继承一起使用。)

寄生组合式继承:
集寄生式继承和组合继承的优点与一身,是实现基于类型继承的最有效方式。

原型链

详情看之前的文章

缺点:

  1. 包含引用类型值的原型。
  2. 在创建子类型的实例时,不能向超类型的构造函数中传递参数。(没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数)

借用构造函数

优点

  1. 解决原型中包含引用类型值带来的问题。
  2. 可以在子类型构造函数中向超类型构造函数传递参数。

思想
在子类构造函数内部调用超类构造函数。
通过apply()和call()方法可以在新建的对象上执行构造函数。

function Animal(){
    this.colors = ["red","blue"];
}

function Dog(){
    //继承Animal
    Animal.call(this);
}

var xiaohuang = new Dog();
xiaohuang.colors.push("yellow");
console.log(xiaohuang.colors): // ["red","blue","yellow"]

var xiaohei = new Dog();
xiaohei .colors.push("black");
console.log(xiaohei .colors); // ["red","blue","black"]

//Dog的每个实例都会有自己的colors属性副本
//传递参数
//优点:在子类构造函数中向父类构造函数传递参数(相比于原型链的优点)

function Animal(name){
    this.name = name;
}

function Dog(){
    //继承Animal,同时还传递了参数
    Animal.call(this,"xiaohuang");
    this.age = 10; //实例属性
}

var xiaohuang = new Dog();
console.log(xiaohuang .name);  //"xiaohuang”
console.log(xiaohuang .age);    //10

缺点

  1. 构造函数模式存在的问题—方法都在构造函数中定义,函数复用无从谈起。
  2. 在超类型的原型中定义的方法,在子类型是不可见的。
  3. 结果所有的类型都只能使用构造函数模式
    借用构造函数很少单独使用

组合继承

组合继承/伪经典继承--最常用的继承模式

优点

  1. 将原型链和借用构造函数组合到一起,发挥两者之长。

思想
使用原型链实现对原型属性和方法的继承,(实现了函数的复用)
通过借用构造函数实现对实例属性的继承 (保证每个实例都有自己的属性)

function Animal(name){
	this.name = name;
	this.colors = ["red","blue"];
}

Animal.prototype.getName = function(){
	console.log(this.name);
}

function Dog(name,age){
	//继承属性
	Animal.call(this,name);
	this.age = age;
}

Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;
Dog.prototype.getAge = function(){
	console.log(this.age);
}

var xiaohuang = new Dog("xiaohuang",10);
xiaohuang.colors.push("yellow");
console.log(xiaohuang.colors); //"red","blue","yellow"
xiaohuang.getAge(); //10
xiaohuang.getName(); //"xiaohuang"

var xiaohei = new dog("xioahei",3);
console.log(xiaohei.colors); //"red","blue"
xiaohei.getAge(); //3
xiaohei.getName(); //"xioahei"

解释
animal的构造函数定义了name和colors两个属性

dog的原型定义了getName()方法。
dog构造函数在调用animal构造函数时传入了name参数,
又定义了自己的属性age

将animal实例的赋值给dog的原型,又在新原型上添加方法getAge(),两个不同的实例可以分别拥有自己的属性name,colors,又可以使用相同的方法getName()

instanceof,isPrototypeOf()也能够识别基于组合继承创建的对象。

缺点

  1. 无论什么情况下,都会调用两次超类型构造函数。
    (1.创建子类原型的时候 2.子类型构造函数内部) 看 寄生组合式继承
    有两组 name和colors属性,dog的原型中有,dog的实例中有,实例中会屏蔽原型的的两个同名属性。

原型式继承

思想
没有严格意义上的构造函数。
借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。
必须要有一个对象可以作为另一个对象的基础。

function object(o){
	function F(){};
	F.prototype = o;
	return new F();
};
//object对传入的对象进行了一次浅复制

var xiaoming = {
	name : "xiaoming",
	friends : ["f1","f2"]
};

var xiaohong = object(xiaoming);

xiaohong.__proto__ === xiaoming; //true
xiaohong.name = "xiaohong";
xiaohong.friends.push("f3");

var xiaohua = "xiaohua";
xiaohua.friends.push("f4");

console.log(xiaoming.friends);//["f1","f2","f3","f4"]

缺点 :

  1. 和原型模式一样,包含引用类型值的属性始终都会共享相应的值

寄生式继承

思路
与寄生构造函数和工厂模式类似,
创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象。

function createAnother(o){
	var clone = object(o);  //通过调用函数创建一个新对象
	clone.sayHi = function(){ //以某种方式来增强这个对象
		alert("hi");
	};
	return clone;  //返回这个对象
}

var person = {
	name : "fangfang",
	friends : ["f1","f2"]
};

var anotherperson = createAnother(person);
anotherperson.sayhi(); //"hi"

//anotherperson具有person的所有属性和方法,还有自己的sayhi方法

缺点
适用场景:在主要考虑对象,而不是自定义类型和构造函数的情况

寄生组合式继承

最理想的继承方式

优点

  1. 不必为了指定子类型的原型而调用父类型的构造函数(解决了组合继承至少两次调用超类构造函数)
    (只调用了一次animal构造函数,避免了dog.prototype上面创建多余的属性)
    (还能正常使用instanceof和isPrototypeOf())

思想
通过借用构造函数来继承属性,
通过原型链的混成形式来继承方法

不必为了指定子类型的原型而调用父类型的构造函数,
我们所需的只是父类原型的一个副本。
就是使用寄生式继承来继承父类的原型,然后将结果指定给子类型的原型

function inheritPrototype(Dog,Animal){
	var prototype = Object(Animal.prototype); //创建父类原型的一个副本
	prototype.constructor = Dog; //副本添加constructor属性
	Dog.prototype = prototype;  //把副本赋值给子类原型
}

function Animal(name){
	this.name = name;
	this.colors = ["red","blue"];
}
Animal.prototype.getName = function(){
	console.log(this.name);
}

function Dog(name,age){
	Animal.call(this,name);
	this.age = age;
}

inheritPrototype(Dog,Animal);

Dog.prototype.getAge = function(){
	console.log(this.age);
}

//此时 Animal.prototype === Dog.prototype
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant