JavaScript深入之继承的多种方式和优缺点

栏目: IT技术 · 发布时间: 4年前

内容简介:本文讲解JavaScript各种继承方式和优缺点。但是注意:

JavaScript深入之继承的多种方式和优缺点

写在前面

本文讲解JavaScript各种继承方式和优缺点。

但是注意:

这篇文章更像是笔记,哎,再让我感叹一句:《JavaScript高级程序设计》写得真是太好了!

1、原型链继承

function Parent () {  
    this.name = 'kevin';  
}  
Parent.prototype.getName = function () {  
    console.log(this.name);  
}  
function Child () {  
}  
Child.prototype = new Parent();   
var child1 = new Child();  
console.log(child1.getName()) // kevin 

问题:

1、引用类型的属性被所有实例共享,举个例子:

function Parent () {  
    this.names = ['kevin', 'daisy'];  
}  
function Child () {  
}  
Child.prototype = new Parent();  
var child1 = new Child();  
child1.names.push('yayu');  
console.log(child1.names); // ["kevin", "daisy", "yayu"]  
var child2 = new Child();  
console.log(child2.names); // ["kevin", "daisy", "yayu"] 

2、在创建 Child 的实例时,不能向Parent传参

2、借用构造函数(经典继承)

function Parent () {  
    this.names = ['kevin', 'daisy'];  
}  
function Child () {  
    Parent.call(this);  
}  
var child1 = new Child();  
child1.names.push('yayu');  
console.log(child1.names); // ["kevin", "daisy", "yayu"]  
var child2 = new Child();  
console.log(child2.names); // ["kevin", "daisy"] 

优点:

1、避免了引用类型的属性被所有实例共享

2、可以在 Child 中向 Parent 传参

举个例子:

function Parent (name) {  
    this.name = name;  
}  
function Child (name) {  
    Parent.call(this, name);  
}  
var child1 = new Child('kevin');  
console.log(child1.name); // kevin  
var child2 = new Child('daisy');  
console.log(child2.name); // daisy 

缺点:

方法都在构造函数中定义,每次创建实例都会创建一遍方法。

3、组合继承

原型链继承和经典继承双剑合璧。

function Parent (name) {  
    this.name = name;  
    this.colors = ['red', 'blue', 'green'];  
}  
Parent.prototype.getName = function () {  
    console.log(this.name)  
}  
function Child (name, age) {  
    Parent.call(this, name);    
    this.age = age;  
}  
Child.prototype = new Parent();  
ChildChild.prototype.constructor = Child;  
var child1 = new Child('kevin', '18');  
child1.colors.push('black');  
console.log(child1.name); // kevin  
console.log(child1.age); // 18  
console.log(child1.colors); // ["red", "blue", "green", "black"]  
var child2 = new Child('daisy', '20');  
console.log(child2.name); // daisy  
console.log(child2.age); // 20  
console.log(child2.colors); // ["red", "blue", "green"] 

优点:融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。

4、原型式继承

function createObj(o) {  
    function F(){}  
    F.prototype = o;  
    return new F();  
} 

就是 ES5 Object.create 的模拟实现,将传入的对象作为创建的对象的原型。

缺点:

包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。

var person = {  
    name: 'kevin',  
    friends: ['daisy', 'kelly']  
}  
var person1 = createObj(person);  
var person2 = createObj(person);  
person1.name = 'person1';  
console.log(person2.name); // kevin  
person1.firends.push('taylor');  
console.log(person2.friends); // ["daisy", "kelly", "taylor"] 

注意:修改person1.name的值,person2.name的值并未发生改变,并不是因为person1和person2有独立的 name 值,而是因为person1.name = 'person1',给person1添加了 name 值,并非修改了原型上的 name 值。

5、寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象。

function createObj (o) {  
    var clone = Object.create(o);  
    clone.sayName = function () {  
        console.log('hi');  
    }  
    return clone;  
} 

缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。

6、寄生组合式继承

为了方便大家阅读,在这里重复一下组合继承的代码:

function Parent (name) {  
    this.name = name;  
    this.colors = ['red', 'blue', 'green'];  
}  
Parent.prototype.getName = function () {  
    console.log(this.name)  
}  
function Child (name, age) {  
    Parent.call(this, name);  
    this.age = age;  
}  
Child.prototype = new Parent();  
var child1 = new Child('kevin', '18');  
console.log(child1) 

组合继承最大的缺点是会调用两次父构造函数。

一次是设置子类型实例的原型的时候:

Child.prototype = new Parent(); 

一次在创建子类型实例的时候:

var child1 = new Child('kevin', '18'); 

回想下 new 的模拟实现,其实在这句中,我们会执行:

Parent.call(this, name); 

在这里,我们又会调用了一次 Parent 构造函数。

所以,在这个例子中,如果我们打印 child1 对象,我们会发现 Child.prototype 和 child1 都有一个属性为colors,属性值为['red', 'blue', 'green']。

那么我们该如何精益求精,避免这一次重复调用呢?

如果我们不使用 Child.prototype = new Parent() ,而是间接的让 Child.prototype 访问到 Parent.prototype 呢?

看看如何实现:

function Parent (name) {  
    this.name = name;  
    this.colors = ['red', 'blue', 'green'];  
}  
Parent.prototype.getName = function () {  
    console.log(this.name)  
}  
function Child (name, age) {  
    Parent.call(this, name);  
    this.age = age;  
}  
// 关键的三步  
var F = function () {};  
F.prototype = Parent.prototype;  
Child.prototype = new F();  
var child1 = new Child('kevin', '18');  
console.log(child1); 

最后我们封装一下这个继承方法:

function object(o) {  
    function F() {}  
    F.prototype = o;  
    return new F();  
}  
function prototype(child, parent) {  
    var prototype = object(parent.prototype);  
    prototype.constructor = child;  
    child.prototype = prototype;  
}  
// 当我们使用的时候:  
prototype(Child, Parent); 

引用《JavaScript高级程序设计》中对寄生组合式继承的夸赞就是:

这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

【责任编辑:庞桂玉 TEL:(010)68476606】


以上所述就是小编给大家介绍的《JavaScript深入之继承的多种方式和优缺点》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

轻量级Django

轻量级Django

茱莉亚·埃尔曼 (Julia Elman)、马克·拉温 (Mark Lavin) / 侯荣涛、吴磊 / 中国电力出版社; 第1版 / 2016-11-1 / 35.6

自Django 创建以来,各种各样的开源社区已经构建了很多Web 框架,比如JavaScript 社区创建的Angular.js 、Ember.js 和Backbone.js 之类面向前端的Web 框架,它们是现代Web 开发中的先驱。Django 从哪里入手来适应这些框架呢?我们如何将客户端MVC 框架整合成为当前的Django 基础架构? 本书讲述如何利用Django 强大的“自支持”功......一起来看看 《轻量级Django》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具