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

注意:

☞借用构造函数承继

原理:在子类型构造函数中调用超类型构造函数,由于函数本人正是可实践的代码块,由此那就和在子类型构造函数中央直机关接设置属性和艺术好些个。

优点:不难,能够在子类型中向父类型的构造函数字传送递参数
缺点:一样方法在不一致目的的构造函数中都要定义壹回,不能兑现函数复用。在超类型原型中定义的情势,对子类型是不可知的,因而有着的超类型的原型属性都无法被三番五次。

function createPerson(name) {

二.借出构造函数(美貌一而再)

function Parent () { this.names = [‘kevin’, ‘daisy’]cabet999亚洲城,; } 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深入之创建对象的多种方式以及优缺点,JavaScript几种继承方式及其优缺点总结。方法都在构造函数中定义,每一次创立实例都会创立三次方法。

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

三.整合承接

原型链继承和卓绝几次三番双剑合璧。

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

不怕 ES伍 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 值。

一.原型链承袭

    o.getName = function () {

伍. 寄生式承接

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

陆. 寄生组合式承继

为了方便我们阅读,在此处再一次一下组合继承的代码:

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

3回在创制子类型实例的时候:

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

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

忆起下 new 的效仿完毕,其实在那句中,大家会实践:

Parent.call(this, name);

1
Parent.call(this, name);

在此间,大家又会调用了一回 Parent 构造函数。

之所以,在那几个事例中,假诺我们打字与印刷 child一 目标,大家会意识 Child.prototype
和 child1 都有1性情质为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 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 收藏
    评论

cabet999亚洲城 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’);

贰.借出构造函数(杰出接二连三)

缺点:对象不大概分辨,因为全体的实例都指向一个原型

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

    };

缺点:

}

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

var person1 = new Person(‘kevin’);

三.结合承继

可取:实例能够辨以为一个特定的品类

原型链承袭和经文几次三番双剑合璧。

缺陷:每回创制实例时,每一种方法都要被创制三次

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 中最常用的持续情势。

function Person(name) {

四.原型式承袭

    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. 寄生式承接

可取:消除了种种方法都要被重复创立的主题素材

创建一个仅用于封装继承进度的函数,该函数在其间以某种方式来做增长对象,最终回到对象。

缺点:那叫什么封装……

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

缺陷:跟借用构造函数情势一样,每一回创设对象都会成立一次方法。

function Person(name) {

陆. 寄生组合式承继

}

为了便于大家阅读,在那里再一次一下整合承继的代码:

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

1回在创制子类型实例的时候:

亮点:方法不会另行创建

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

缺陷:1. 富有的性子和章程都共享 贰. 无法初步化参数

追忆下 new 的效仿达成,其实在那句中,我们会施行:

三.一 原型格局优化

Parent.call(this, name);

function Person(name) {

在此间,大家又会调用了一遍 Parent 构造函数。

}

由此,在那个例子中,如若大家打字与印刷 child1 对象,大家会发觉 Child.prototype
和 child1 都有三个本性为colors,属性值为[‘red’, ‘blue’, ‘green’]。

Person.prototype = {

那么大家该如何改良,幸免那1回重复调用呢?

    name: ‘kevin’,

借使大家不选取 Child.prototype = new Parent() ,而是直接的让
Child.prototype 访问到 Parent.prototype 呢?

    getName: function () {

探望哪些兑现:

        console.log(this.name);

网站地图xml地图