菜单

JavaScript 深远之创造对象的多样措施以及优缺点

2019年3月28日 - 金沙前端

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

那种措施的高功用展现它只调用了2次 Parent 构造函数,并且由此幸免了在
Parent.prototype
上面创建不须求的、多余的性质。与此同时,原型链仍是可以保持不变;由此,仍是能够够健康使用
instanceof 和
isPrototypeOf。开发职员普遍认为寄生组合式继承是引用类型最非凡的继续范式。

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. 不可能初叶化参数

本人的措施

再谈javascript原型继承,javascript原型继承

的确含义上来说Javascript并不是一门面向对象的语言,没有提供古板的一而再形式,可是它提供了一种原型继承的主意,利用自个儿提供的原型属性来完毕持续。

原型与原型链

说原型继承在此之前依旧要先说说原型和原型链,毕竟那是兑现原型继承的底蕴。
在Javascript中,每种函数都有多个原型属性prototype指向本身的原型,而由那么些函数创建的指标也有贰个__proto__属性指向那个原型,而函数的原型是三个目的,所以这几个目的也会有3个__proto__本着本人的原型,那样逐层深切直到Object对象的原型,那样就形成了原型链。上边那张图很好的表达了Javascript中的原型和原型链的关系。

图片 1

每一种函数都以Function函数创制的指标,所以各种函数也有二个__proto__属性指向Function函数的原型。那里必要提议的是,真正形成原型链的是每种对象的__proto__品质,而不是函数的prototype属性,那是很首要的。

原型继承

基本格局

复制代码 代码如下:

var Parent = function(){
    this.name = ‘parent’ ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(){
    this.name = ‘child’ ;
} ;
Child.prototype = new Parent() ;

var parent = new Parent() ;
var child = new Child() ;

console.log(parent.getName()) ; //parent
console.log(child.getName()) ; //child

那种是最简便易行完结原型继承的法门,直接把父类的对象赋值给子类构造函数的原型,那样子类的目的就可以访问到父类以及父类构造函数的prototype中的属性。
那种办法的原型继承图如下:

图片 2

那种办法的长处很显然,实现丰裕大致,不须求别的特殊的操作;同时缺点也很显明,如若子类必要做跟父类构造函数中平等的初阶化动作,那么就得在子类构造函数中再重新三回父类中的操作:

复制代码 代码如下:

var Parent = function(name){
    this.name = name || ‘parent’ ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(name){
    this.name = name || ‘child’ ;
} ;
Child.prototype = new Parent() ;

var parent = new Parent(‘myParent’) ;
var child = new Child(‘myChild’) ;

console.log(parent.getName()) ; //myParent
console.log(child.getName()) ; //myChild

地点那种场馆还只是急需开头化name属性,假如初阶化工作持续追加,那种方式是很不便于的。因而就有了下边一种革新的主意。

借用构造函数

复制代码 代码如下:

var Parent = function(name){
    this.name = name || ‘parent’ ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
Child.prototype = new Parent() ;

var parent = new Parent(‘myParent’) ;
var child = new Child(‘myChild’) ;

console.log(parent.getName()) ; //myParent
console.log(child.getName()) ; //myChild

上边那种办法在子类构造函数中经过apply调用父类的构造函数来开始展览相同的初叶化学工业作,那样不管父类中做了略微伊始化学工业作,子类也足以执行同样的起头化学工业作。但是地点那种完毕还存在一个题材,父类构造函数被实施了一遍,一次是在子类构造函数中,1次在赋值子类原型时,那是很多余的,所以大家还供给做二个更上一层楼:

复制代码 代码如下:

var Parent = function(name){
    this.name = name || ‘parent’ ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
Child.prototype = Parent.prototype ;

var parent = new Parent(‘myParent’) ;
var child = new Child(‘myChild’) ;

console.log(parent.getName()) ; //myParent
console.log(child.getName()) ; //myChild

诸如此类大家就只须要在子类构造函数中履行二回父类的构造函数,同时又有什么不可继续父类原型中的属性,那也正如相符原型的初衷,正是把要求复用的始末放在原型中,我们也只是继承了原型中可复用的剧情。上面那种格局的原型图如下:

图片 3

方今构造函数格局(圣杯形式)

上边借用构造函数方式最后革新的本子照旧存在难点,它把父类的原型直接赋值给子类的原型,那就会造成三个难点,便是假如对子类的原型做了改动,那么这些修改同时也会影响到父类的原型,进而影响父类对象,这几个一定不是豪门所梦想见到的。为了解决这一个标题就有了权且构造函数方式。

复制代码 代码如下:

var Parent = function(name){
    this.name = name || ‘parent’ ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
var F = new Function(){} ;
F.prototype = Parent.prototype ;
Child.prototype = new F() ;

var parent = new Parent(‘myParent’) ;
var child = new Child(‘myChild’) ;

console.log(parent.getName()) ; //myParent
console.log(child.getName()) ; //myChild

该措施的原型继承图如下:

图片 4

很简单能够观望,通过在父类原型和子类原型之间投入3个权且的构造函数F,切断了子类原型和父类原型之间的牵连,那样当子类原型做修改时就不会影响到父类原型。

本人的措施

《Javascript情势》中到圣杯格局就得了了,然则无论上边哪个种类方式都有叁个不易于被发觉的题目。大家可以观察自己在’Parent’的prototype属性中参加了1个obj对象字面量属性,然则一贯都尚未用。我们在圣杯形式的功底上来看看下边这种景况:

复制代码 代码如下:

var Parent = function(name){
    this.name = name || ‘parent’ ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
var F = new Function(){} ;
F.prototype = Parent.prototype ;
Child.prototype = new F() ;

var parent = new Parent(‘myParent’) ;
var child = new Child(‘myChild’) ;

console.log(child.obj.a) ; //1
console.log(parent.obj.a) ; //1
child.obj.a = 2 ;
console.log(child.obj.a) ; //2
console.log(parent.obj.a) ; //2

在地点那种景况中,当自家修改child对象obj.a的时候,同时父类的原型中的obj.a也会被改动,那就发生了和共享原型同样的难点。出现这些情状是因为当访问child.obj.a的时候,大家会沿着原型链一贯找到父类的prototype中,然后找到了obj属性,然后对obj.a举行改动。再看看上面那种景观:

复制代码 代码如下:

var Parent = function(name){
    this.name = name || ‘parent’ ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
var F = new Function(){} ;
F.prototype = Parent.prototype ;
Child.prototype = new F() ;

var parent = new Parent(‘myParent’) ;
var child = new Child(‘myChild’) ;

console.log(child.obj.a) ; //1
console.log(parent.obj.a) ; //1
child.obj.a = 2 ;
console.log(child.obj.a) ; //2
console.log(parent.obj.a) ; //2

那边有二个首要的标题,当指标访问原型中的属性时,原型中的属性对于指标的话是只读的,也正是说child对象足以读取obj对象,但是无法修改原型中obj对象引用,所以当child修改obj的时候并不会对原型中的obj发生潜移默化,它只是在自己对象添加了贰个obj属性,覆盖了父类原型中的obj属性。而当child对象修改obj.a时,它先读取了原型中obj的引用,那时候child.obj和Parent.prototype.obj是指向同一个指标的,所以child对obj.a的修改会影响到Parent.prototype.obj.a的值,进而影响父类的对象。AngularJS中关于$scope嵌套的接轨格局正是模范Javasript中的原型继承来促成的。
据说地点的描述,只要子类对象中做客到的原型跟父类原型是同一个指标,那么就会见世下边那种情状,所以大家能够对父类原型举办拷贝然后再赋值给子类原型,那样当子类修改原型中的属性时就只是修改父类原型的1个正片,并不会潜移默化到父类原型。具体落到实处如下:

复制代码 代码如下:

var deepClone = function(source,target){
    source = source || {} ;
    var toStr = Object.prototype.toString ,
        arrStr = ‘[object array]’ ;
    for(var i in source){
        if(source.hasOwnProperty(i)){
            var item = source[i] ;
            if(typeof item === ‘object’){
                target[i] = (toStr.apply(item).toLowerCase() ===
arrStr) : [] ? {} ;
                deepClone(item,target[i]) ;   
            }else{
                deepClone(item,target[i]) ;
            }
        }
    }
    return target ;
} ;
var Parent = function(name){
    this.name = name || ‘parent’ ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : ‘1’} ;

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
Child.prototype = deepClone(Parent.prototype) ;

var child = new Child(‘child’) ;
var parent = new Parent(‘parent’) ;

console.log(child.obj.a) ; //1
console.log(parent.obj.a) ; //1
child.obj.a = ‘2’ ;
console.log(child.obj.a) ; //2
console.log(parent.obj.a) ; //1

归咎上面装有的考虑,Javascript继承的求实落成如下,那里只考虑了Child和Parent都是函数的气象下:

复制代码 代码如下:

var deepClone = function(source,target){
    source = source || {} ;
    var toStr = Object.prototype.toString ,
        arrStr = ‘[object array]’ ;
    for(var i in source){
        if(source.hasOwnProperty(i)){
            var item = source[i] ;
            if(typeof item === ‘object’){
                target[i] = (toStr.apply(item).toLowerCase() ===
arrStr) : [] ? {} ;
                deepClone(item,target[i]) ;   
            }else{
                deepClone(item,target[i]) ;
            }
        }
    }
    return target ;
} ;

var extend = function(Parent,Child){
    Child = Child || function(){} ;
    if(Parent === undefined)
        return Child ;
    //借用父类构造函数
    Child = function(){
        Parent.apply(this,argument) ;
    } ;
    //通过深拷贝继承父类原型   
    Child.prototype = deepClone(Parent.prototype) ;
    //重置constructor属性
    Child.prototype.constructor = Child ;
} ;

总结

说了如此多,其实Javascript中贯彻持续是充足灵活各样的,并从未一种最棒的章程,须求依照区别的需要实现分歧方式的一连,最珍视的是要驾驭Javascript中完成持续的法则,也正是原型和原型链的标题,只要明白了那个,自身达成持续就能够百发百中。

真正含义上来说Javascript并不是一门面向对象的语言,没有提供古板的继承方式,不过它提供了一种…

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

缺点:

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

写在前方

那篇小说讲解创制对象的各个方法,以及优缺点。

不过注意:

那篇小说更像是笔记,因为《JavaScript高级程序设计》写得真是太好了!

var deepClone = function(source,target){
    source = source || {} ;
    var toStr = Object.prototype.toString ,
        arrStr = ‘[object array]’ ;
    for(var i in source){
        if(source.hasOwnProperty(i)){
            var item = source[i] ;
            if(typeof item === ‘object’){
                target[i] = (toStr.apply(item).toLowerCase() ===
arrStr) : [] ? {} ;
                deepClone(item,target[i]) ;   
            }else{
                deepClone(item,target[i]) ;
            }
        }
    }
    return target ;
} ;
var Parent = function(name){
    this.name = name || ‘parent’ ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : ‘1’} ;

深远种类

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 收藏
    评论

图片 5

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

缺陷:对象不可能辨识,因为兼具的实例都针对二个原型

var parent = new Parent(‘myParent’) ;
var child = new Child(‘myChild’) ;

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

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

亮点:该共享的共享,该民用的个体,使用最广大的章程

缺点:有的人正是希望全部都写在共同,即更好的封装性

console.log(child.obj.a) ; //1
console.log(parent.obj.a) ; //1
child.obj.a = ‘2’ ;
console.log(child.obj.a) ; //2
console.log(parent.obj.a) ; //1

3.结缘继承

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

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 中最常用的后续方式。

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 操作符调用构造函数

安妥对象最符合在局部安然无恙的环境中。

安妥构造函数模式也跟工厂方式一样,不可能分辨对象所属类型。

var parent = new Parent(‘myParent’) ;
var child = new Child(‘myChild’) ;

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

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

优点:实例可以辨认为二个一定的种类

缺陷:每一次创设实例时,各类方法都要被创设一次

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
Child.prototype = deepClone(Parent.prototype) ;

JavaScript 深切之继续的三种主意和优缺点

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

可取:化解了种种方法都要被重新创制的题材

缺陷:那叫什么封装……

复制代码 代码如下:

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

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

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
都无法指向构造函数!

如此那般方法能够在拾壹分情况下使用。比如大家想创立3个负有额外措施的奇特数组,但是又不想直接修改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

您会意识,其实所谓的寄生构造函数方式就是比工厂方式在成立对象的时候,多选用了3个new,实际上两者的结果是同一的。

但是小编也许是指望能像使用普通 Array 一样选用 SpecialArray,固然把
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);

你恐怕感兴趣的篇章:

写在前面

本文讲解JavaScript各样继承情势和优缺点。

唯独注意:

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

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

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个目的
  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

var Parent = function(name){
    this.name = name || ‘parent’ ;
} ;
Parent.prototype.getName = function(){
    return this.name ;
} ;
Parent.prototype.obj = {a : 1} ;

JavaScript 深切之创立对象的有余主意以及优缺点

2017/05/28 · JavaScript
· 对象

原稿出处: 冴羽   

var Child = function(name){
    Parent.apply(this,arguments) ;
} ;
var F = new Function(){} ;
F.prototype = Parent.prototype ;
Child.prototype = new F() ;

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属性

var Child = function(name){
    this.name = name || ‘child’ ;
} ;
Child.prototype = new Parent() ;

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、继承等难点概念。

一旦有荒唐大概不小心翼翼的地点,请务必给予指正,12分多谢。假使喜欢依然持有启发,欢迎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 赞 收藏
    评论

图片 6

基本格局

图片 7

var parent = new Parent(‘myParent’) ;
var child = new Child(‘myChild’) ;

在上头那种景色中,当我修改child对象obj.a的时候,同时父类的原型中的obj.a也会被涂改,那就发生了和共享原型同样的题材。出现那么些情景是因为当访问child.obj.a的时候,大家会沿着原型链一向找到父类的prototype中,然后找到了obj属性,然后对obj.a举办修改。再看看上面这种场馆:

该办法的原型继承图如下:

说了如此多,其实Javascript中贯彻一而再是充足灵活各个的,并从未一种最棒的措施,供给依据差别的供给实现差异措施的接轨,最要紧的是要掌握Javascript中落到实处一而再的原理,也便是原型和原型链的题材,只要通晓了那个,本身完毕延续就能够游刃有余。

相关文章

发表评论

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

网站地图xml地图