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

JavaScript 深远之继续的种种艺术和优缺点

2017/05/28 · JavaScript
· 继承

初稿出处: 冴羽   

写在前面

•借用构造函数 (又叫伪造对象或经典一而再)
•组合继承(也叫伪经典一而再)
•寄生组合式继承

发源《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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

问题:

一.引用类型的属性被全数实例共享,举个例证:

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”]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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"]

二.在开创 Child 的实例时,不能够向Parent传参

注意:

深入理解JavaScript继承的多种方式和优缺点,深入之继承的多种方式和优缺点。☞借用构造函数继承

规律:在子类型构造函数中调用超类型构造函数,由于函数本身就是可实施的代码块,由此那就和在子类型构造函数中央直机关接设置属性和形式大多。

可取:简单,能够在子类型中向父类型的构造函数传递参数
缺点:相同方法在不相同指标的构造函数中都要定义一回,不恐怕兑现函数复用。在超类型原型中定义的不二等秘书诀,对子类型是不可知的,因而有着的超类型的原型属性都不可能被一连。

function createPerson(name) {

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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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"]

优点:

一.幸免了引用类型的性质被全体实例共享

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

缺点:

办法都在构造函数中定义,每一次成立实例都会创设三次方法。

跟《JavaScript深刻之创造对象》壹样,更像是笔记。

☞组合继承

原理:将原型链和借用构造函数的技巧构成到1块,发挥双方之长的1种持续格局。

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true
亮点:通过调用父类构造,继承父类的属性并保存传参的帮助和益处,然后通过将父类实例作为子类原型,完结函数复用,既是子类的实例也是父类的实例
症结:调用一遍父类型构造函数,生成两份实例(覆盖了父类型原型上的性质),消耗内部存款和储蓄器

    var o = new Object();

三.整合继承

原型链继承和经文三番五次双剑合璧。

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’); 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”]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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’);
 
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 中最常用的一而再格局。

咦,再让自个儿惊讶一句:《JavaScript高级程序设计》写得真是太好了!

☞寄生组合式继承

规律:通过寄生情势,砍掉父类的实例属性,那样,在调用五遍父类的结构的时候,就不会开始化三次实例方法/属性,幸免的咬合继承的毛病

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
(function(){
  // 创建一个没有实例方法的类
  var Super = function(){};
  Super.prototype = Animal.prototype;
  //将实例作为子类的原型
  Cat.prototype = new Super();
})();

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat);
Cat.prototype.constructor = Cat; //修复构造函数

尤其周到的完毕了对象的继续

    o.name = name;

四.原型式继承

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

1
2
3
4
5
function createObj(o) {
    function F(){}
    F.prototype = o;
    return new F();
}

不怕 ES5 Object.create 的效仿达成,将盛传的指标作为创造的靶子的原型。

缺点:

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

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”]

1
2
3
4
5
6
7
8
9
10
11
12
13
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的值并未有发出改变,并不是因为person1person2有单独的
name 值,而是因为person1.name = 'person1',给person1添加了 name
值,并非修改了原型上的 name 值。

一.原型链继承

    o.getName = function () {

5. 寄生式继承

创设2个仅用于封装继承进程的函数,该函数在在那之中以某种情势来做增长对象,最后回到对象。

function createObj (o) { var clone = object.create(o); clone.sayName =
function () { console.log(‘hi’); } return clone; }

1
2
3
4
5
6
7
function createObj (o) {
    var clone = object.create(o);
    clone.sayName = function () {
        console.log(‘hi’);
    }
    return clone;
}

症结:跟借用构造函数方式壹样,每便创立对象都会成立二次方法。

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

        console.log(this.name);

陆. 寄生组合式继承

为了便利大家阅读,在那里再次一下构成继承的代码:

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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();

1
Child.prototype = new Parent();

一遍在成立子类型实例的时候:

var child1 = new Child(‘kevin’, ’18’);

1
var child1 = new Child(‘kevin’, ’18’);

追思下 new 的模仿完毕,其实在那句中,大家会履行:

Parent.call(this, name);

1
Parent.call(this, name);

在此处,我们又会调用了1回 Parent 构造函数。

故此,在这么些例子中,即便大家打字与印刷 child一 对象,我们会发觉 Child.prototype
和 child一 都有2个性格为colors,属性值为['red', 'blue', 'green']

那正是说大家该怎么创新,制止那3遍重复调用呢?

假如我们不行使 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 child一 =
new Child(‘kevin’, ‘1八’); console.log(child一);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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。开发人士普遍认为寄生组合式继承是引用类型最美貌的接轨范式。

问题:

    };

深远体系

JavaScript深刻体系目录地址:。

JavaScript深刻种类推测写105篇左右,目的在于帮我们捋顺JavaScript底层知识,重点讲解如原型、功用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等难处概念。

假使有荒唐可能不审慎的地点,请务必给予指正,十二分多谢。假诺喜欢恐怕持有启发,欢迎star,对作者也是一种鞭策。

  1. JavaScirpt 长远之从原型到原型链
  2. JavaScript
    深刻之词法作用域和动态作用域
  3. JavaScript 深远之推行上下文栈
  4. JavaScript 深远之变量对象
  5. JavaScript 深刻之效果域链
  6. JavaScript 长远之从 ECMAScript 规范解读
    this
  7. JavaScript 深切之实践上下文
  8. JavaScript 深入之闭包
  9. JavaScript 深远之参数按值传递
  10. JavaScript
    深远之call和apply的模拟完结
  11. JavaScript 深刻之bind的模仿完毕
  12. JavaScript 深刻之new的模拟实现
  13. JavaScript 深刻之类数组对象与
    arguments
  14. JavaScript
    深切之创制对象的三种格局以及优缺点

    1 赞 3 收藏
    评论

图片 1

一.引用类型的习性被抱有实例共享,举个例子:

    return o;

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传参

var person1 = createPerson(‘kevin’);

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. 构造函数情势

优点:

function Person(name) {

壹.制止了引用类型的品质被抱有实例共享

    this.name = name;

2.可以在 Child 中向 Parent 传参

    this.getName = function () {

举个例子:

        console.log(this.name);

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

    };

缺点:

}

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

var person1 = new Person(‘kevin’);

三.整合继承

亮点:实例能够辨别为二个特定的种类

原型链继承和经文一连双剑合璧。

症结:每便创立实例时,种种方法都要被成立1回

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');

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"]

2.一 构造函数方式优化

可取:融合原型链继承和构造函数的帮助和益处,是 JavaScript 中最常用的延续情势。

function Person(name) {

4.原型式继承

    this.name = name;

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

    this.getName = getName;

不畏 ES⑤ Object.create 的里丑捧心达成,将盛传的目的作为成立的目的的原型。

}

缺点:

function getName() {

涵盖引用类型的属性值始终都会共享相应的值,那点跟原型链继承壹样。

    console.log(this.name);

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的值并未有发出转移,并不是因为person1person2有独立的
name 值,而是因为person1.name = 'person1',给person1添加了 name
值,并非修改了原型上的 name 值。

var person1 = new Person(‘kevin’);

5. 寄生式继承

可取:消除了各种方法都要被再一次创制的难题

开创3个仅用于封装继承进度的函数,该函数在其间以某种格局来做拉长对象,最终回到对象。

缺点:那叫什么封装……

function createObj (o) {
  var clone = object.create(o);
  clone.sayName = function () {
    console.log('hi');
  }
  return clone;
}
  1. 原型方式

缺陷:跟借用构造函数情势一样,每回创造对象都会创建3遍方法。

function Person(name) {

6. 寄生组合式继承

}

为了便利大家阅读,在此间再度一下组合继承的代码:

Person.prototype.name = ‘keivn’;

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)

Person.prototype.getName = function () {

结缘继承最大的后天不足是会调用五遍父构造函数。

    console.log(this.name);

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

};

Child.prototype = new Parent();

var person1 = new Person();

贰遍在开创子类型实例的时候:

亮点:方法不会另行创制

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

症结:1. 有所的习性和章程都共享 2. 不能够初叶化参数

回想下 new 的效仿达成,其实在那句中,大家会执行:

三.一 原型形式优化

Parent.call(this, name);

function Person(name) {

在此处,我们又会调用了二回 Parent 构造函数。

}

因此,在那么些事例中,借使大家打印 child一 目的,大家会发觉 Child.prototype
和 child一 都有一性子质为colors,属性值为[‘red’, ‘blue’, ‘green’]。

Person.prototype = {

那么我们该如何革新,制止那二次重复调用呢?

    name: ‘kevin’,

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

    getName: function () {

看望怎么样达成:

        console.log(this.name);

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图