[译] 为何原型继续很主要

五天之前我写了一个关于ES6规范中Class的文章。在里面我引见了怎样用现有的Javascript来模仿类而且引见了ES6中类的用法,着实它只是一个语法糖。谢谢Om Shakar以及Javascript Room中的列位,我的编程作风从那时刻最先发生了转变;就像Dougla Crockford2006年做的一样,我也进修了许多来完整明白基于原型的编程体式格局。

Javascript是一个多样化的编程言语。它具有面向对象和函数式的编程特征,你可以运用任何一种作风来编写代码。然则这两个编程作风并不能很好的融会。比方,你不没法同时运用new(典范的面向对象的特征)和apply(函数式编程的特征).原型继续一向都作为衔接这两种作风的桥梁。

基于类继续的题目

大部分Javascript顺序员会通知你基于类的继续不好。然则它们中只要很少一部分晓得个中的缘由。现实实际上是基于类的基础并没有什么不好。Python是基于类继续的,而且它是一门很好的编程言语。然则,基于类的继续并不合适用于Javascript。Python准确的运用了类,它们只要简朴的工场要领不能当作组织函数运用。而在Javascript中任何函数都可以被当作组织函数运用。

Javascript中的题目是由于每一个函数都可以被当作组织函数运用,所以我们须要辨别平常的函数挪用和组织函数挪用;我们平常运用new关键字来举行区分。然则,如许就破坏了Javascript中的函数式特征,由于new是一个关键字而不是函数。因而函数式的特征没法和对象实例化一同运用。

function Person(firstname,lastname){
    this.firstname = firstname ;
    this.lastname = lastname ;
}

斟酌上面这段顺序。你可以经由历程new关键字来挪用Person要领来建立一个函数Person的实例:

var author = new Person('Aadit','Shah') ;

然则,没有任何方法来运用apply要领来为组织函数指定参数列表:

var author = new Person.apply(null,['Aadit','Shah']);//error

然则,假如new是一个要领那末上面的需求就可以经由历程下面这类体式格局完成了:

var author = Person.new.apply(Person,['Aadit','Shah']) ;

荣幸的是,由于Javascript有原型继续,所以我们可以完成一个new的函数:

Function.prototype.new = function () {
    function functor() { return constructor.apply(this, args); }
    var args = Array.prototype.slice.call(arguments);
    functor.prototype = this.prototype;
    var constructor = this;
    return new functor;
};

在像Java如许对象只能经由历程new关键字来实例化的言语中,上面这类体式格局是不可以完成的。

下面这张表列出了原型继续比拟于基于类的基础的长处:

基于类的继续原型继续
类是不可变的。在运行时,你没法修正或许增加新的要领原型是天真的。它们可以是不可变的也可以是可变的
类可以会不支撑多重继续对象可以继续多个原型对象
基于类的继续比较庞杂。你须要运用抽象类,接口和final类等等原型继续比较简约。你只要对象,你只须要对对象举行扩大就可以了

不要再运用关键词new了

到如今你应当晓得为何我以为new关键字是不会的了吧—你不能把它和函数式特征夹杂运用。然后,这并不代表你应当停止运用它。new关键字有合理的用途。然则我依然发起你不要再运用它了。new关键字掩盖了Javascript中真正的原型继续,使得它更像是基于类的继续。就像Raynos说的:

new是Javascript在为了取得盛行度而到场与Java类似的语法时代留下来的一个残留物

Javascript是一个源于Self的基于原型的言语。然则,为了市场需求,Brendan Eich把它当作Java的小兄弟推出:

而且我们当时把Javascript当作Java的一个小兄弟,就像在微软言语家庭中Visual Basic相关于C++一样。

这个设想决议计划致使了new的题目。当人们看到Javascript中的new关键字,他们就想到类,然后当他们运用继续时就遇到了傻了。就像Douglas Crockford说的:

这个间接的行动是为了使传统的顺序员对这门言语更熟习,然则却失利了,就像我们看到的很少Java顺序员挑选了Javascript。Javascript的组织形式并没有吸收传统的人群。它也掩盖了Javascript基于原型的实质。效果就是,很少的顺序员晓得怎样高效的运用这门言语

因而我发起停止运用new关键字。Javascript在传统面向对象假象下面有着越发壮大的原型体系。然大部分顺序员并没有瞥见这些还处于黑暗中。

明白原型继续

原型继续很简朴。在基于原型的言语中你只要对象。没有类。有两种体式格局来建立一个新对象—“无中生有”对象建立法或许经由历程现有对象建立。在Javascript中Object.create要领用来建立新的对象。新的对象以后会经由历程新的属性举行扩大。

“无中生有”对象建立法

Javascript中的Object.create要领用来从0最先建立一个对象,像下面如许:

var object = Object.create(null) ;

上面例子中新建立的object没有任何属性。

克隆一个现有的对象

Object.create要领也可以克隆一个现有的对象,像下面如许:

var rectangle = {
    area : function(){
        return this.width * this.height ;
    }
} ;
var rect = Object.create(rectangle) ;

上面例子中rectrectangle中继续了area要领。同时注意到rectangle是一个对象字面量。对象字面量是一个简约的要领用来建立一个Object.prototype的克隆然后用新的属性来扩大它。它等价于:

var rectangle = Object.create(Object.prototype) ;
rectangle.area = function(){
    return this.width * this.height ;
} ;

扩大一个新建立的对象

上面的例子中我们克隆了rectangle对象命名为rect,然则在我们运用rectarea要领之前我们须要扩大它的widthheight属性,像下面如许:

rect.width = 5 ;
rect.height = 10 ;
alert(rect.area()) ;

然则这类体式格局来建立一个对象的克隆然后扩大它是一个异常傻缺的要领。我们须要在每一个rectangle对象的克隆上手动定义widthheight属性。假如有一个要领可以为我们来完成这些事情就很好了。是不是是听起来有点熟习?确切是。我要来说说组织函数。我们把这个函数叫做create然后在rectangle对象上定义它:

var rectangle = {
    create : function(width,height){
        var self = Object.create(this) ;
        self.height = height ;
        self.width = width ;
        return self ;
    } ,
    area : function(){
        return this.width * this.height ;
    }
} ;
var rect = rectangle.create(5,10) ;
alert(rect.area()) ;

组织函数 VS 原型

等等。这看起来很像Javascript中的一般组织形式:

function Rectangle(width, height) {
    this.height = height;
    this.width = width;
} ;

Rectangle.prototype.area = function () {
    return this.width * this.height;
};

var rect = new Rectangle(5, 10);
 
alert(rect.area());

是的,确切很像。为了使得Javascript看起来更像Java原型形式被迫屈服于组织形式。因而每一个Javascript中的函数都有一个prototype对象然后可以用来作为组织器(这里组织器的意义应当是说新的对象是在prototype对象的基础上举行组织的)。new关键字许可我们把函数当作组织函数运用。它会克隆组织函数的prototype属性然后把它绑定到this对象中,假如没有显式返回对象则会返回this

原型形式和组织形式都是同等的。因而你或许会疑心为何有人会搅扰于是不是应当运用原型形式而不是组织形式。毕竟组织形式比原型形式越发简约。然则原型形式比拟组织形式有许多上风。详细以下:

组织形式原型形式
函数式特征没法与new关键字一同运用函数式特征可以与create连系运用
遗忘运用new会致使没法预期的bug而且会污染全局变量由于create是一个函数,所以顺序总是会依据预期事情
运用组织函数的原型继续比较庞杂而且杂沓运用原型的原型继续简约易懂

末了一点可以须要诠释一下。运用组织函数的原型继续比拟运用原型的原型继续越发庞杂,我们先看看运用原型的原型继续:

var square = Object.create(rectangle);
square.create = function (side) {
    return rectangle.create.call(this, side, side);
} ;
var sq = square.create(5) ;
alert(sq.area()) ;

上面的代码很轻易明白。起首我们建立一个rectangle的克隆然后命名为square。接着我们用新的create要领重写square对象的create要领。终究我们重新的create要领中挪用rectanglecreate函数而且返回对象。相反的,运用组织函数的原型继续像下面如许:

function Square(){
    Rectangle.call(this,side,side) ;
} ;

Square.prototype = Object.create(Rectangle.prototype) ;

Square.prototype.constructor = Square ;

var sq = new Square(5) ;

alert(sq.area()) ;

固然,组织函数的体式格局更简朴。然后如许的话,向一个不相识状况的人诠释原型继续就变得异常难题。假如想一个相识类继续的人诠释则会越发难题。

当运用原型形式时一个对象继续自另一个对象就变得很显著。当运用要领组织形式时就没有这么显著,由于你须要依据其他组织函数来斟酌组织继续。

对象建立和扩大相连系

在上面的例子中我们建立一个rectangle的克隆然后命名为square。然后我们运用新的create属性扩大它,重写继续自rectangle对象的create要领。假如把这两个操纵兼并成一个就很好了,就像对象字面量是用来建立Object.prototype的克隆然后用新的属性扩大它。这个操纵叫做extend,可以像下面如许完成:

Object.prototype.extend = function(extension){
    var hasOwnProperty = Object.hasOwnProperty ;
    var object = Object.create(this) ;
    
    for(var property in extension){
        if(hasOwnProperty.call(extension,property) ||
            typeof obejct[property] === 'undefined')
            //这段代码有题目,依据文章意义,这里应当运用深复制,而不是简朴的浅复制,deepClone(extension[property],object[property]),deepClone的完成可以看我之前关于继续的博客
            object[properyty] = extension[property] ;
    }
    return object ;
} ;

译者注:我以为博主这里的完成有点不符合逻辑,一般extend的完成应当是可以设置当被扩大对象和用来扩大的对象属性反复时是不是掩盖原有属性,而博主的完成就只是简朴的掩盖。同时博主的完成在if推断中的做法个人以为是值得进修的,起首推断extension属性是不是是对象自身的,假如是就直接复制到object上,不然再推断object上是不是有这个属性,假如没有那末也会把属性复制到object上,这类完成的效果就使得被扩大的对象不仅仅只扩大了extension中的属性,还包含了extension原型中的属性。不难明白,extension原型中的属性会在extension中表现出来,所以它们也应当作为extension所具有的特征而被用来扩大object。所以我对这个要领举行了改写:

    Object.prototype.extend = function(extension,override){
    var hasOwnProperty = Object.hasOwnProperty ;
    var object = Object.create(this) ;
    for(var property in extension){
        if(hasOwnProperty.call(extension,property) || 
            typeof object[property] === 'undefined'){
            if(object[property] !== 'undefined'){
                if(override){
                    deepClone(extension[property],object[property]) ;
                }
            }else{
                deepClone(extension[property],object[property]) ;
            }    
        }
    }
}; 

运用上面的extend要领,我们可以重写square的代码:

var square = rectangle.extend({
    create : function(side){
        return rectangle.create.call(this,side,side) ;
    }
}) ;

var sq = square.create(5) ;
alert(sq.area()) ;

extend要领是原型继续中唯一须要的操纵。它是Object.create函数的超集,因而它可以用在对象的建立和扩大上。因而我们可以用extend来重写rectangle,使得create函数越发结构化看起来就像模块形式

var rectangle = {
    create : function(width,height){
        return this.extend({
            height : height ,
            width : width
        }) ;
    }
} ;

var rect = rectangle.create(5,10) ;
alert(rect.area()) ;

原型继续的两种要领

一些人可以已注意到extend函数返回的对象实际上是继续了两个对象的属性,一个是被扩大的对象,另一个是用来扩大的对象。别的从两个对象继续属性的体式格局也不一样。第一种状况下是经由历程委派来继续属性(也就是运用Object.create()来继续属性),第二种状况下运用兼并属性的体式格局来继续属性。

委派(差异化继续)

许多Javascript顺序员关于差异继续比较熟习。维基百科是这么诠释的:

大部分对象是从其他更平常的对象中获得的,只是在一些很小的处所举行了修正。每一个对象一般在内部保护一个指向其他对象的援用列表,这些对象就是该对象自身举行差异化继续的对象。

Javascript中的原型继续是基于差异化继续的。每一个对象都有个内部指针叫做[[proto]] (在大部分浏览器中可以经由历程__proto__属性接见),这个指针指向对象的原型。多个对象之间经由历程内部[[proto]]属性链接起来形成了原型链,链的末了指向null

当你试图猎取一个对象的属性时Javascript引擎会起首查找对象自身的属性。假如在对象上没找到该属性,那末它就会去对象的原型中去查找。以此类推,它会沿着原型链一向查找晓得找到或许到原型链的末端。

function get(object,property){
    if(!Object.hasOwnProperty.call(object,property)){
        var prototype = Object.getPrototypeOf(object) ;
        if(prototype) return get(prototype,property) ;
    }else{
        return object[property] ;
    }
} ;

Javascript中属性查找的历程就像上面的顺序那样。

克隆(兼并式继续)

大多数Javascript顺序员会以为复制一个对象的属性到另一个对象上并非一个准确的继续的体式格局,由于任何对原始对象的修正都不会反应在克隆的对象上。五天前我会赞同这个看法。然则如今我置信兼并式继续是原型继续的一种准确体式格局。关于原始对象的修正可以发送到它的副原本完成真正的原型继续。

兼并式继续和代办有他们的长处和瑕玷。下表列出了它们的优瑕玷:

代办兼并
任何关于原型的修正都邑反应在一切副本上任何关于原型的修正都须要手动更新到副本中
属性查找效力较低由于须要举行原型链查找属性查找更搞笑由于继续的属性是经由历程复制的体式格局附加在对象自身的
运用Object.create()要领只能继续单一对象对象可以从恣意数目的对象中经由历程复制继续属性

从多个原型继续

上表中末了一点通知我们对象可以经由历程兼并的体式格局从多个原型中继续属性。这是一个重要的特征由于这证实原型继续比Java中的类继续更壮大而且与C++中的类继续一样壮大。为了完成多重继续,你只须要修正extend要领来从多个原型中复制属性。

Object.prototype.extend = function(){
    var hasOwnProperty = Object.hasOwnProperty ;
    var object = Object.create(this) ;
    var length = arguments.length ;
    var index = length ;
    
    while(index){
        var extension = arguments[length - (index--)] ;
        for(var property in extension){
            if(hasOwnProperty.call(extension,property)||
                typeof object[property] === 'undefined'){
                //这里一样应当运用深复制
                object[property] = extension[property] ;
            }
        }
    }
    return object;
} ;

多重继续是异常有效的由于它提高了代码的可重用性和模块化。对象经由历程委派继续一个原型对象然后经由历程兼并继续其他属性。比如说你有一个事宜发射器的原型,像下面如许:

var eventEmitter = {
    on : function(event,listener){
        if(typeof this[event] !== 'undefined')
            this[event].push(listener) ;
        else
            this[event] = [listener] ;
    } ,
    emit : function(event){
        if(typeof this[event] !== 'undefined'){
            var listeners = this[event] ;
            var length = listeners.length,index = length ;
            var args = Array.prototype.slice.call(arguments,1) ;
            
            while(index){
                var listener = listeners[length - (index--)] ;
                listener.apply(this,args) ;
            }
        }
    }
} ;

如今你愿望square表现得像一个事宜发射器。由于square已经由历程委派的体式格局继续了rectangle,所以它必需经由历程兼并的体式格局继续eventEmitter。这个修正可以很轻易地经由历程运用extend要领完成:

var square = rectangle.extend(eventEmitter,{
    create : function(side){
        return rectangle.create.call(this,side,side) ;
    } ,
    resize : function(newSize){
        var oldSize = this.width ;
        this.width = this.height = newSize ;
        this.emit('resize',oldSize,newSize) ;
    }
}) ;
var sq = square.create(5) ;
sq.on('resize',function(oldSize,newSize){
    alert('sq resized from ' + oldSize + 'to' + newSize + '.') ;
}) ;

sq.resize(10) ;
alert(sq.area()) ;

在Java中是不可以完成上面的顺序的,由于它不支撑多重继续。响应的你必需别的再建立一个EventEmitter类或许运用一个EventEmitter接口而且在每一个完成该接口的类中离别完成onemit要领。固然你在C++中不须要面临这个题目。我们都晓得Java sucks(呵呵呵)。

Mixin的蓝图(Buleprint)

在上面的例子中你一定注意到eventEmitter原型并没有一个create要领。这是由于你不应当直接建立一个eventEmitter对象。相反eventEmitter是用来作为其他原型的原型。这类原型称为mixin。它们等价于抽象类。mixin用来经由历程供应一系列可重用的要领来扩大对象的功用。

然则有时刻mixin须要私有的状况。比方eventEmitter假如可以把它的事宜监听者列表放在私有变量中而不是放在this对象上会平安很多。然则mixin没有create要领来封装私有状况。因而我们须要为mixin建立一个蓝图(blueprint)来建立闭包。蓝图(blueprint)看起来会像是组织函数然则它们并不必像组织函数那样运用。比方:

function eventEmitter(){
    var evnets = Object.create(null) ;
    
    this.on = function(event,listener){
        if(typeof events[event] !== 'undefined')
            events[event].push(listener) ;
        else
            events[event] = [listener] ;
    } ;
    this.emit = function(event){
        if(typeof events[event] !== 'undefined'){
            var listeners = events[event] ;
            var length = listeners.length ,index = length ;
            var args = Array.prototype.slice.call(arguments,1) ;
        }
    } ;
} ;

一个蓝图用来在一个对象建立以后经由历程兼并来扩大它(我以为有点像装潢者形式)。Eric Elliot把它们叫做闭包原型。我们可以运用蓝图版本的eventEmitter来重写square的代码,以下:

var square = rectangle.extend({
    create : function(side){
        var self = rectangle.create.call(this,side,side) ;
        eventEmitter.call(self) ;
        return self ;
    } ,
    resize : function(newSize){
        var oldSize = this.width ;
        this.width = this.height = newSize ;
        this.emit('resize',oldSize,newSize) ;
    }
}) ;
var sq = square.create(5) ;

sq.on('resize',function(oldSize,newSize){
    alert('sq resized from ' + oldSize + 'to' + newSize + '.') ;
}) ;

sq.resize(10) ;

alert(sq.area()) ;

蓝图在Javascript中是举世无双的。它是一个很壮大的特征。然则它们也有本身的瑕玷。下表列出了mixin和蓝图的优瑕玷:

Mixin蓝图
它们用来扩大对象的原型。因而对象同享同一个原型它们用来扩大新建立的对象。因而每一个对象都是在本身对象自身举行修正
由于缺乏封装要领所以不存在私有状况它们是函数,所以可以封装私有状况
它们是静态原型而且不能被自定义它们可以通报参数来自定义对象,可以向蓝图函数通报一些用来自定义的参数

修复instanceof操纵

许多Javascript顺序员会以为运用原型形式来继续违犯了言语的精华。他们更倾向于组织形式由于他们以为经由历程组织函数建立的对象才是真正的实例,由于instanceof操纵会返回true。然则,这个争辩是没有意义的,由于instanceof操纵可以像下面如许完成:

Object.prototype.instanceof = function(prototype){
    var object = this ;
    do{
        if(object === prototype) return true ;
        var object = Object.getPrototypeOf(object) ;
    }while(object) ;
    return false ;
}

这个instanceof要领如今可以被用来测试一个对象是不是是经由历程委派从一个原型继续的。比方:

sq.instanceof(square) ;

然则照样没有方法推断一个对象是不是是经由历程兼并的体式格局从一个原型继续的,由于实例的关联信息丧失了。为相识决这个题目我们将一个原型的一切克隆的援用保存在原型自身中,然后运用这个信息来推断一个对象是不是是一个原型的实例。这个可以经由历程修正extend要领来完成:

Object.prototype.extend = function(){
    var hasOwnProperty = Object.hasOwnProperty ; 
    var object = Object.create(this) ;
    var length = arguments.lenght ;
    var index = length ;

    while(index){
        var extension = arguments[length - (index--)] ;

        for(var property in extension){
            if(property !== 'clones' &&
                hasOwnProperty.call(extension,property) ||
                typeof object[property] === 'undefined')
                object[property] = extension[property] ;

        if(hasOwnProperty.call(extension,'clones')})
            extension.clones.unshift(object) ;
        else
            extension.clones = [object] ;
        }
    }
    return object;
} ;

经由历程兼并继续自原型的对象形成了一个克隆树,这些树从根对象最先然后向下一向到恭弘=叶 恭弘子对象。一个克隆链是一个从根对象到恭弘=叶 恭弘子对象的单一途径,这跟遍历原型链很类似。我们可以运用这个信息来推断一个对象是不是是经由历程兼并继续自一个原型。

Object.prototype.instanceof = function(prototype){
    if (Object.hasOwnProperty.call(prototype, "clones"))
        var clones = prototype.clones;
    var object = this;
    
    do {
        if (object === prototype ||
            clones && clones.indexOf(object) >= 0)
            return true;
        var object = Object.getPrototypeOf(o  bject);
    } while (object);

    return false;
} ;

这个instanceof要领如今可以用来推断一个对象是不是是经由历程兼并继续自一个原型。比方:

sq.instanceof(eventEmitter);

在上面的顺序中instanceof会返回true假如我妈运用mixin版本的eventEmitter。然则假如我们运用蓝图版本的eventEmitter它会返回false。为相识决这个题目我建立了一个蓝图函数,这个函数吸收一个蓝图作为参数,向它增加一个clones属性然后返回一个记录了它的克隆的新蓝图:

function blueprint(f){
    var g = function(){
        f.apply(this,arguments) ;
        g.clones.unshift(this) ;
    } ;
    g.clones = [] ;
    return g ;
} ;
var eventEmitter = blueprint(function(){
    var events = Object.create(null);
    this.on = function (event, listener) {
        if (typeof events[event] !== "undefined")
            events[event].push(listener);
        else events[event] = [listener];
    };

    this.emit = function (event) {
        if (typeof events[event] !== "undefined") {
            var listeners = events[event];
            var length = listeners.length, index = length;
            var args = Array.prototype.slice.call(arguments, 1);

            while (index) {
                var listener = listeners[length - (index--)];
                listener.apply(this, args);
            }
        }
    };
}) ;

向原型发送变化

上面例子中的clones属性有两重作用。它可以用来推断一个对象是不是是经由历程兼并继续自一个原型的,然后他可以用来发送原型转变给一切它的克隆。原型继续比拟类继续最大的上风就是你可以修正一个原型在它建立以后。为了使克隆可以继续关于原型的修正,我们建立了一个叫做define的函数:

Object.prototype.define = function (property, value) {
    this[property] = value;

    if (Object.hasOwnProperty.call(this, "clones")) {
        var clones = this.clones;
        var length = clones.length;

        while (length) {
            var clone = clones[--length];
            if (typeof clone[property] === "undefined")
                clone.define(property, value);
        }
    }
};

如今我们可以修正原型然后这个修正会反应在一切的克隆上。比方我们可以建立建立一个别号addEventListener针对eventEmitter上的on要领:

var square = rectangle.extend(eventEmitter, {
    create: function (side) {
        return rectangle.create.call(this, side, side);
    },
    resize: function (newSize) {
        var oldSize = this.width;
        this.width = this.height = newSize;
        this.emit("resize", oldSize, newSize);
    }
});

var sq = square.create(5);

eventEmitter.define("addEventListener", eventEmitter.on);

sq.addEventListener("resize", function (oldSize, newSize) {
    alert("sq resized from " + oldSize + " to " + newSize + ".");
});

sq.resize(10);
 
alert(sq.area());

蓝图须要特别注意。只管关于蓝图的修正会被发送到它的克隆,然则蓝图的新的克隆并不会反应这些修正。荣幸的是这个题目的处理要领很简朴。我们只须要对blueprint要领举行小小的修正,然后任何关于蓝图的修正就会反应在克隆上了。

function blueprint(f) {
    var g = function () {
        f.apply(this, arguments);
        g.clones.unshift(this);

        var hasOwnProperty = Object.hasOwnProperty;

        for (var property in g)
            if (property !== "clones" &&
                hasOwnProperty.call(g, property))
                    this[property] = g[property];
    };

    g.clones = [];

    return g;
};

结论

祝贺你。假如你读完了整篇文章而且明白了我所说的东西,你如今就相识了 原型继续而且为何它很重要。很谢谢你们看完了这篇文章。我愿望这个博客能帮到你们。原型继续是壮大的而且值得更多的信托。然后大部分人历来不明白这个由于Javascript中的原型继续被组织形式所掩盖了。

译者注

这篇文章针对几种继续体式格局举行了对照。文章中说到的几种扩大的要领我以为是比较有效的。蓝图(blueprint,这个着实不晓得该怎样翻译)的扩大体式格局比较像设想形式中的装潢者形式,经由历程函数对对象举行扩大,这个是一种比较好玩的扩大体式格局,可以跟原型继续合营运用。别的文中提到了new关键字的弊病,个人以为重要的缘由照样new关键字的涌现掩盖了Javascript自身原型继续的特征,人们自然则然就会想到传统的类继续,如许就没法发挥原型继续的最大威力。末了说到的属性修正流传的题目也挺有意义的,应当会有响应的运用场景。总之,我以为原型继续比拟于传统的类继续供应了更大的天真性,可以给我们开发者供应很大的发挥空间,不过不管怎样,到末了照样要涉及到基础的原型继续的道理上,所以控制了原型继续的道理就可以依据差别的运用场景运用林林总总的扩大体式格局。

原文地点:http://aaditmshah.github.io/why-prototypal-inheritance-matters/

末了,安利下我的个人博客,迎接接见: http://bin-playground.top

    原文作者:lakb248
    原文地址: https://segmentfault.com/a/1190000002596600
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞