深入之继承的多种方式和优缺点,JavaScript深入之创建对象的多种方式以及优缺点

JavaScript 深切之继续的有余方法和优缺点

2017/05/28 · JavaScript
· 继承

初稿出处: 冴羽   

写在头里

•借用构造函数 (又叫伪造对象或经典连续)
•组合继承(也叫伪经典接二连三)
•寄生组合式继承

深入之继承的多种方式和优缺点,JavaScript深入之创建对象的多种方式以及优缺点。源于《JavaScript高级程序设计》

写在前面

正文讲解JavaScript种种继承格局和优缺点。

但是注意:

这篇小说更像是笔记,哎,再让本身惊讶一句:《JavaScript高级程序设计》写得真是太好了!

本文讲解JavaScript各样继承格局和优缺点。


  1. 工厂形式

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

问题:

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

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

2.在创建 Child 的实例时,无法向Parent传参

注意:

☞借用构造函数继承

原理:在子类型构造函数中调用超类型构造函数,由于函数自个儿就是可实施的代码块,因而这就和在子类型构造函数中一贯设置属性和方法大多。

优点:不难,能够在子类型中向父类型的构造函数字传送递参数
缺陷:相同方式在差别对象的构造函数中都要定义三次,不只怕达成函数复用。在超类型原型中定义的法门,对子类型是不可知的,由此全部的超类型的原型属性都无法被接二连三。

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

优点:

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

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

缺点:

措施都在构造函数中定义,每一回创制实例都会创制3回方法。

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

☞组合继承

原理:将原型链和借用构造函数的技术结合到一块,发挥两岸之长的一种持续格局。

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

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

4.原型式继承

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 的上行下效完结,将盛传的对象作为创制的目的的原型。

缺点:

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

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 值。

1.原型链继承

    o.getName = function () {

5. 寄生式继承

开创3个仅用于封装继承进程的函数,该函数在其间以某种情势来做拉长对象,最后回来对象。

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

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)

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

在此地,大家又会调用了一次 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);

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高级程序设计》中对寄生组合式继承的歌唱正是:

那种方法的高效能显示它只调用了3次 Parent 构造函数,并且为此制止了在
Parent.prototype
下面创造不供给的、多余的属性。与此同时,原型链还是能保全不变;由此,还是能够够健康使用
instanceof 和
isPrototypeOf。开发职员普遍认为寄生组合式继承是引用类型最杰出的接续范式。

问题:

    };

深入连串

JavaScript深远体系目录地址:。

JavaScript深远连串猜想写十五篇左右,目的在于帮我们捋顺JavaScript底层知识,重点教学如原型、功用域、执行上下文、变量对象、this、闭包、按值传递、call、apply、bind、new、继承等困难概念。

假设有不当也许不审慎的地点,请务必给予指正,10分多谢。假设喜欢依然具有启发,欢迎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

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) {

1.防止了引用类型的性质被有着实例共享

    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

    };

缺点:

}

艺术都在构造函数中定义,每便创制实例都会创建1遍方法。

var person1 = new Person(‘kevin’);

3.结缘继承

优点:实例能够辨别为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();

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.1 构造函数格局优化

亮点:融合原型链继承和构造函数的帮助和益处,是 JavaScript 中最常用的后续形式。

function Person(name) {

4.原型式继承

    this.name = name;

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

    this.getName = getName;

不畏 ES5 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. 寄生式继承

亮点:化解了每个方法都要被再一次创设的题材

始建贰个仅用于封装继承进度的函数,该函数在个中以某种格局来做增加对象,最终回到对象。

缺陷:那叫什么封装……

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 的效仿完毕,其实在那句中,我们会执行:

3.1 原型格局优化

Parent.call(this, name);

function Person(name) {

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

}

为此,在那一个事例中,假如大家打印 child1 对象,咱们会发现 Child.prototype
和 child1 都有五特质量为colors,属性值为[‘red’, ‘blue’, ‘green’]。

Person.prototype = {

那么大家该怎么着改革,幸免那贰遍重复调用呢?

    name: ‘kevin’,

假定我们不利用 Child.prototype = new Parent() ,而是直接的让
Child.prototype 访问到 Parent.prototype 呢?

    getName: function () {

看望哪些完结:

        console.log(this.name);

相关文章