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

JavaScript 深切之继续的有余格局和优劣势

2017/05/28 · JavaScript
· 继承

原来的小讲出处: 冴羽   

JavaScript 浓重之创建对象的多种格局以致优短处

2017/05/28 · 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

问题:

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

1. 工厂方式

新澳门萄京娱乐场官网,function createPerson(name) { var o = new Object(); o.name = name;
o.getName = function () { console.log(this.name); }; return o; } var
person1 = createPerson(‘kevin’);

1
2
3
4
5
6
7
8
9
10
11
function createPerson(name) {
    var o = new Object();
    o.name = name;
    o.getName = function () {
        console.log(this.name);
    };
 
    return o;
}
 
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
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

缺点:

主意都在结构函数中定义,每便创造实例都会创造三次方法。

2. 构造函数方式

function Person(name) { this.name = name; this.getName = function () {
console.log(this.name); }; } var person1 = new Person(‘kevin’);

1
2
3
4
5
6
7
8
function Person(name) {
    this.name = name;
    this.getName = function () {
        console.log(this.name);
    };
}
 
var person1 = new Person(‘kevin’);

亮点:实例可以识别为贰个一定的品种

症结:每便创造实例时,各种方法都要被创设叁遍

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 中最常用的继续格局。

2.1 构造函数方式优化

function Person(name) { this.name = name; this.getName = getName; }
function getName() { console.log(this.name); } var person1 = new
Person(‘kevin’);

1
2
3
4
5
6
7
8
9
10
function Person(name) {
    this.name = name;
    this.getName = getName;
}
 
function getName() {
    console.log(this.name);
}
 
var person1 = new Person(‘kevin’);

亮点:清除了各类方法都要被重复创制的主题素材

症结:那叫什么封装……

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

3. 原型方式

function Person(name) { } Person.prototype.name = ‘keivn’;
Person.prototype.getName = function () { console.log(this.name); }; var
person1 = new Person();

1
2
3
4
5
6
7
8
9
10
function Person(name) {
 
}
 
Person.prototype.name = ‘keivn’;
Person.prototype.getName = function () {
    console.log(this.name);
};
 
var person1 = new Person();

可取:方法不会另行创造

缺欠:1. 颇负的天性和章程都分享 2. 无法起初化参数

5. 寄生式世襲

开创三个仅用于封装世襲进度的函数,该函数在里头以某种格局来做增加对象,最后回来对象。

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

缺欠:跟借用布局函数格局同样,每一遍创立对象都会创设一次方法。

3.1 原型方式优化

function Person(name) { } Person.prototype = { name: ‘kevin’, getName:
function () { console.log(this.name); } }; var person1 = new Person();

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name) {
 
}
 
Person.prototype = {
    name: ‘kevin’,
    getName: function () {
        console.log(this.name);
    }
};
 
var person1 = new Person();

可取:封装性好了一点

缺点:重写了原型,错过了constructor属性

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(卡塔尔(英语:State of Qatar) ,而是直接的让
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(卡塔尔(英语:State of Qatar); }
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。开荒人士普及以为寄生组合式继承是援用类型最优异的存在延续范式。

3.2 原型形式优化

function Person(name) { } Person.prototype = { constructor: Person,
name: ‘kevin’, getName: function () { console.log(this.name); } }; var
person1 = new Person();

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name) {
 
}
 
Person.prototype = {
    constructor: Person,
    name: ‘kevin’,
    getName: function () {
        console.log(this.name);
    }
};
 
var person1 = new Person();

亮点:实例能够通过constructor属性找到所属布局函数

症结:原型形式该有的败笔照旧有

深切类别

JavaScript浓厚种类目录地址:。

JavaScript深刻类别估算写十四篇左右,意在帮大家捋顺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

4. 组合格局

布局函数情势与原型情势双剑合璧。

function Person(name) { this.name = name; } Person.prototype = {
constructor: Person, getName: function () { console.log(this.name); } };
var person1 = new Person();

1
2
3
4
5
6
7
8
9
10
11
12
function Person(name) {
    this.name = name;
}
 
Person.prototype = {
    constructor: Person,
    getName: function () {
        console.log(this.name);
    }
};
 
var person1 = new Person();

亮点:该分享的分享,该民用的私人民居房,使用最广泛的方法

症结:有的人正是意在任何都写在协同,即更加好的封装性

4.1 动态原型格局

function Person(name) { this.name = name; if (typeof this.getName !=
“function”) { Person.prototype.getName = function () {
console.log(this.name); } } } var person1 = new Person();

1
2
3
4
5
6
7
8
9
10
function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype.getName = function () {
            console.log(this.name);
        }
    }
}
 
var person1 = new Person();

瞩目:使用动态原型形式时,无法用对象字面量重写原型

表达下为啥:

function Person(name) { this.name = name; if (typeof this.getName !=
“function”卡塔尔国 { Person.prototype = { constructor: Person, getName:
function (卡塔尔 { console.log(this.name卡塔尔国; } } } } var person1 = new
Person(‘kevin’卡塔尔(قطر‎; var person2 = new Person(‘daisy’卡塔尔; // 报错 并从未该办法
person1.getName(卡塔尔; // 注释掉上面的代码,那句是足以执行的。
person2.getName(卡塔尔(英语:State of Qatar);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
    }
}
 
var person1 = new Person(‘kevin’);
var person2 = new Person(‘daisy’);
 
// 报错 并没有该方法
person1.getName();
 
// 注释掉上面的代码,这句是可以执行的。
person2.getName();

为了然说这几个难点,尽管开端推行var person1 = new Person('kevin')

借使对 new 和 apply
的平底试行进程不是很熟习,能够阅读尾部相关链接中的作品。

咱俩记忆下 new 的达成步骤:

  1. 第后生可畏新建多个对象
  2. 接下来将目的的原型指向 Person.prototype
  3. 然后 Person.apply(obj)
  4. 回到这几个目的

细心此时,回顾下 apply 的兑现步骤,会施行 obj.Person
方法,当时就能试行 if 语句里的剧情,注意结构函数的 prototype
属性指向了实例的原型,使用字面量格局直接覆盖
Person.prototype,并不会改造实例的原型的值,person1
长久以来是指向了原先的原型,并不是 Person.prototype。而以前的原型是从未
getName 方法的,所以就报错了!

万风流倜傥你尽管想用字面量方式写代码,可以品味下这种:

function Person(name) { this.name = name; if (typeof this.getName !=
“function”) { Person.prototype = { constructor: Person, getName:
function () { console.log(this.name); } } return new Person(name); } }
var person1 = new Person(‘kevin’); var person2 = new Person(‘daisy’);
person1.getName(); // kevin person2.getName(); // daisy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Person(name) {
    this.name = name;
    if (typeof this.getName != "function") {
        Person.prototype = {
            constructor: Person,
            getName: function () {
                console.log(this.name);
            }
        }
 
        return new Person(name);
    }
}
 
var person1 = new Person(‘kevin’);
var person2 = new Person(‘daisy’);
 
person1.getName(); // kevin
person2.getName();  // daisy

5.1 寄生布局函数情势

function Person(name) { var o = new Object(); o.name = name; o.getName =
function () { console.log(this.name); }; return o; } var person1 = new
Person(‘kevin’); console.log(person1 instanceof Person) // false
console.log(person1 instanceof Object) // true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Person(name) {
 
    var o = new Object();
    o.name = name;
    o.getName = function () {
        console.log(this.name);
    };
 
    return o;
 
}
 
var person1 = new Person(‘kevin’);
console.log(person1 instanceof Person) // false
console.log(person1 instanceof Object)  // true

寄生结构函数情势,笔者个人感到应该那样读:

寄生-布局函数-形式,也等于说寄生在布局函数的大器晚成种艺术。

约等于说打着布局函数的金字金牌徒有虚名,你看成立的实例使用 instanceof
都力不从心指向布局函数!

如此方法能够在至极情状下选取。比方大家想成立一个装有额外措施的诡异数组,可是又不想一直改善Array构造函数,大家得以那样写:

function SpecialArray() { var values = new Array(); for (var i = 0, len
= arguments.length; i len; i++) { values.push(arguments[i]); }
values.toPipedString = function () { return this.join(“|”); }; return
values; } var colors = new SpecialArray(‘red’, ‘blue’, ‘green’); var
colors2 = SpecialArray(‘red2’, ‘blue2’, ‘green2’); console.log(colors);
console.log(colors.toPipedString()); // red|blue|green
console.log(colors2); console.log(colors2.toPipedString()); //
red2|blue2|green2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function SpecialArray() {
    var values = new Array();
 
    for (var i = 0, len = arguments.length; i  len; i++) {
        values.push(arguments[i]);
    }
 
    values.toPipedString = function () {
        return this.join("|");
    };
    return values;
}
 
var colors = new SpecialArray(‘red’, ‘blue’, ‘green’);
var colors2 = SpecialArray(‘red2’, ‘blue2’, ‘green2’);
 
 
console.log(colors);
console.log(colors.toPipedString()); // red|blue|green
 
console.log(colors2);
console.log(colors2.toPipedString()); // red2|blue2|green2

你会开掘,其实所谓的寄生构造函数格局就是比厂子情势在成立对象的时候,多选用了二个new,实际上两个的结果是相像的。

不过作者只怕是期望能像使用普通 Array 相通使用 特略Array,即使把
SpecialArray 当成函数也如出后生可畏辙能用,然而那实际不是我的原意,也变得不美观。

在能够使用其它情势的境况下,不要选用这种方式。

只是值得后生可畏提的是,下边例子中的循环:

for (var i = 0, len = arguments.length; i len; i++) {
values.push(arguments[i]); }

1
2
3
for (var i = 0, len = arguments.length; i  len; i++) {
    values.push(arguments[i]);
}

能够替换来:

values.push.apply(values, arguments);

1
values.push.apply(values, arguments);

5.2 稳当布局函数形式

function person(name){ var o = new Object(); o.sayName = function(){
console.log(name); }; return o; } var person1 = person(‘kevin’);
person1.sayName(); // kevin person1.name = “daisy”; person1.sayName();
// kevin console.log(person1.name); // daisy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function person(name){
    var o = new Object();
    o.sayName = function(){
        console.log(name);
    };
    return o;
}
 
var person1 = person(‘kevin’);
 
person1.sayName(); // kevin
 
person1.name = "daisy";
 
person1.sayName(); // kevin
 
console.log(person1.name); // daisy

所谓稳当对象,指的是尚未国有属性,并且其方式也不援引 this 的靶子。

与寄生布局函数形式有两点不相同:

  1. 新创建的实例方法不引用 this
  2. 不行使 new 操作符调用布局函数

伏贴对象最符合在某个平安的条件中。

稳妥布局函数形式也跟工厂方式同样,不可能辨识对象所属类型。

深深系列

JavaScript深刻体系目录地址:。

JavaScript深远种类推测写十三篇左右,目的在于帮我们捋顺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

    1 赞 收藏
    评论

新澳门萄京娱乐场官网 1

发表评论

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