一篇就夠-JS繼承的多種方式和實(shí)現(xiàn)

原型鏈繼承

方法:子構(gòu)造函數(shù)的prototype指向?yàn)楦笜?gòu)造函數(shù)的實(shí)例,因?yàn)樵玩準(zhǔn)?strong>proto的鏈表,父構(gòu)造函數(shù)的實(shí)例的proto指向父構(gòu)造函數(shù)實(shí)例的原型。

function Parent(){
    this.name = 'johe'
}

Parent.prototype.getName = function(){
    console.log(this.name)
}

function Child(){

}

//原型必須是對(duì)象,所以為Parent的實(shí)例
Child.prototype = new Parent()

var child1 = new Child()

child1.getName()

問(wèn)題:

  1. 引用類型的屬性被所有實(shí)例共享
  2. 創(chuàng)建Child的實(shí)例時(shí),不能向parent傳參
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"]

這是因?yàn)镻arent在實(shí)例化之后,成為了Child的原型不狮,原型上的屬性和方法是共享的。

借用構(gòu)造函數(shù)(經(jīng)典繼承)

調(diào)用父構(gòu)造函數(shù)

function Parent(){
    this.names = ['kevin','daisy'];
}

function Child(){
    Parent.call(this);
}

var Child1 = new Child()

Child1.names.push('johe')

var Child2 = new Child()

//['kevin','daisy']
Child2.names

優(yōu)點(diǎn):

  1. 避免了引用類型的屬性被所有實(shí)例共享
  2. 可以在Child中向Parent傳參
    缺點(diǎn):
  3. 方法都在構(gòu)造函數(shù)中定義,每次創(chuàng)建實(shí)例都會(huì)創(chuàng)建一遍方法
  4. instanceof檢驗(yàn)為false
function Parent(name){
    this.name = name;
}

function Child(name){
    Parent.call(this,name)
}

var Child1 = new Child('johe')

//johe 
Child1.name

組合繼承(原型鏈繼承和經(jīng)典繼承)

優(yōu)點(diǎn):借用構(gòu)造函數(shù)繼承解決了傳參問(wèn)題和實(shí)例屬性被共享的問(wèn)題续室,原型鏈繼承能夠滿足共享方法不被重復(fù)創(chuàng)建。
缺點(diǎn):調(diào)用了兩次父構(gòu)造函數(shù)

function Parent(name){
    this.name = name
    this.names = ['johe']
}

Parent.prototype.getName = function(){
    return this.name
}

function Child(name,age){
    //調(diào)用父級(jí)的構(gòu)造方法谒养,實(shí)現(xiàn)實(shí)例屬性
    Parent.call(this,name)
    this.age = age
}

//這里不用參數(shù)是因?yàn)樽訕?gòu)造函數(shù)調(diào)用父構(gòu)造函數(shù)時(shí)已實(shí)現(xiàn)實(shí)例屬性挺狰,有實(shí)例屬性的情況下不會(huì)從原型鏈中查找
Child.prototype = new Parent()

var child1 = new Child('johe',18)
child1.names.push("johe2")
console.log(child1.name);//johe
console.log(child1.age);18
//["johe","johe2"]
console.log(child1.names);

var child2 = new Child('child2',19)
console.log(child2.name);//child2
console.log(child2.age);19
//["johe"]
console.log(child1.names);

原型式繼承(Object.create)

Object.create的模擬實(shí)現(xiàn)

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

缺點(diǎn):
包含引用類型的屬性值始終都會(huì)共享響應(yīng)的值,這點(diǎn)跟原型鏈繼承一樣买窟。

寄生組合式繼承(組合繼承優(yōu)化)

組合繼承的最大缺點(diǎn)就是會(huì)調(diào)用兩次父構(gòu)造函數(shù)
一次是設(shè)置子類型實(shí)例的原型:

Child.protoype = new Parent()

一次是創(chuàng)建子類型實(shí)例的時(shí)候:

function Child(name,age){
    Parent.call(this,name)
}

var child1 = new Child('johe',18)

這個(gè)時(shí)候Child.prototype這個(gè)對(duì)象內(nèi)的屬性其實(shí)是沒用的丰泊,因?yàn)樽宇愋蛯?shí)例已經(jīng)調(diào)用了父構(gòu)造函數(shù)進(jìn)行了屬性實(shí)例化。

所以就用到了寄生組合式繼承始绍,讓Child.prototype間接的訪問(wèn)到Parent.prototype

function Parent(name){
    this.name = name
    this.names = ['johe']
}
Parent.prototype.getName = function(){return this.name}

function Child(name,age){
    Parent.call(this,name)
    this.age = age
}

function F(){}

F.prototype = Parent.prototype

Child.prototype = new F()

var child1 = new Child('johe',18)

封裝繼承方法:

function Parent(name){
    this.name = name
    this.names = ['johe']
}
Parent.prototype.getName = function(){return this.name}

function Child(name,age){
    Parent.call(this,name)
    this.age = age
}

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

function setPrototype(child,parent){
    var prototype = createObject(parent)
    prototype.constructor = Child
    Child.protoype = prototype
}

setPrototype(Child,Parent)

這種方式的高效率體現(xiàn)它只調(diào)用了一次 Parent 構(gòu)造函數(shù)瞳购,并且因此避免了在 Parent.prototype 上面創(chuàng)建不必要的、多余的屬性亏推。與此同時(shí)学赛,原型鏈還能保持不變;因此吞杭,還能夠正常使用 instanceof 和 isPrototypeOf盏浇。開發(fā)人員普遍認(rèn)為寄生組合式繼承是引用類型最理想的繼承范式

這里為什么不直接使用Child.prototype=Parent.prototype,是因?yàn)镻arent.constructor應(yīng)該指向Parent,并且如果我們需要給Child實(shí)例的原型設(shè)置方法和屬性時(shí)篇亭,會(huì)影響到Parent的實(shí)例缠捌,這明顯是不合理的。

ES6的繼承

ECMAScript6 引入了一套新的關(guān)鍵字用來(lái)實(shí)現(xiàn) class译蒂。使用基于類語(yǔ)言的開發(fā)人員會(huì)對(duì)這些結(jié)構(gòu)感到熟悉曼月,但它們是不同的。JavaScript 仍然基于原型柔昼。這些新的關(guān)鍵字包括 class, constructor哑芹,static,extends 和 super捕透。

class Fruits{
    constructor(type,size){
        this.type = type;
        this.size = size;
    }
    static sayHello(){
        console.log("hello");
    }
    sayType(){
        console.log('my type is '+this.type);
        return this.type;
    }
}

class Banana extends Fruits{
    constructor(type,size,color){
        super(type,size);
        this.color = color;
    }
    sayColor(){
        console.log('my color is '+ this.color);
        return this.color;
    }
}
let fruit = new Fruits("fruit","small");
let banana = new Banana("banana","small","yellow");
 //parent: Fruits {type:'fruit',size:'small'}
console.log('parent:',fruit);
//child: Banana {type:'banana',size:'small',color:'yellow' }
console.log('child:',banana);

//hello
console.log(Fruits.sayHello());
console.log(Banana.sayHello());

//true
console.log(Fruits.hasOwnProperty("sayHello"));
//false
console.log(Banana.hasOwnProperty("sayHello"));

//false
console.log(fruit.hasOwnProperty("sayType"))
//true
console.log(Fruits.prototype.hasOwnProperty("sayType"));
//my type is banana
console.log(banana.sayType());
//false
console.log(banana.hasOwnProperty("sayType"));
//Fruits {}
console.log(banana.__proto__);

首先查看class語(yǔ)法糖做了什么:

  • 將構(gòu)造函數(shù)內(nèi)的this屬性實(shí)例化
  • 將構(gòu)造函數(shù)外的非static函數(shù)給設(shè)置到構(gòu)造函數(shù)的原型對(duì)象中聪姿,共享屬性
  • static函數(shù)設(shè)置為構(gòu)造函數(shù)的屬性

用ES5實(shí)現(xiàn)就是組合方式來(lái)創(chuàng)建對(duì)象:

function Fruits(type,size){
    this.type = type;
    this.size = size;
}
Fruits.sayHello = function(){
    console.log('Hello');
}
Fruits.prototype = {
    constructor:Fruits,
    sayType:function(){
        console.log('my type is' +this.type);
        return this.type;
    }
}

再看看extends做了什么:

  • 繼承父類的實(shí)例屬性(調(diào)用父類的構(gòu)造函數(shù))
  • 繼承父類的共享屬性(原型鏈上有父類的原型對(duì)象)
  • 子類構(gòu)造函數(shù)的proto指向父類構(gòu)造函數(shù)(兩個(gè)都是對(duì)象)(繼承靜態(tài)函數(shù))
//true
console.log(Banana.__proto === Fruits);
//true
console.log(banana instance of Fruits);

es6繼承的es5實(shí)現(xiàn)

知道extends做了什么之后碴萧,我們可以知道其轉(zhuǎn)化成es5就是寄生組合式繼承(組合=原型鏈繼承+借用構(gòu)造函數(shù)),并且設(shè)置子類構(gòu)造函數(shù)的proto為父類構(gòu)造函數(shù).


function Fruits(type,size){
    this.type = type;
    this.size = size;
}
Fruits.sayHello = function(){
    console.log('Hello');
}
Fruits.prototype = {
    constructor:Fruits,
    sayType:function(){
        console.log('my type is' +this.type);
        return this.type;
    }
}

function Banana(type,size,color){
    Fruits.call(this,type,size);
    this.color = color;
}

function createObject(Parent){
    function Empty(){};
    Empty.prototype = Parent.prototype;
    return new Empty();
}

Banana.prototype = createObject(Fruits);
Banana.prototype.constructor = Banana;
Banana.prototype.sayColor = function(){
    console.log(this.color);
}

Banana.__proto__ = Fruits;

通過(guò)babeljs轉(zhuǎn)碼成ES5來(lái)查看末购,更嚴(yán)謹(jǐn)?shù)膶?shí)現(xiàn)破喻。

//es6版本
class Parent{
    constructor(name){
        this.name = name;
    }
    static sayHello(){
        console.log('hello');
    }
    sayName(){
        console.log('my name is ' + this.name);
        return this.name;
    }
}
class Child extends Parent{
    constructor(name, age){
        super(name);
        this.age = age;
    }
    sayAge(){
        console.log('my age is ' + this.age);
        return this.age;
    }
}
// 對(duì)轉(zhuǎn)換后的代碼進(jìn)行了簡(jiǎn)要的注釋
"use strict";
// 主要是對(duì)當(dāng)前環(huán)境支持Symbol和不支持Symbol的typeof處理
function _typeof(obj) {
    if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
        _typeof = function _typeof(obj) {
            return typeof obj;
        };
    } else {
        _typeof = function _typeof(obj) {
            return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
        };
    }
    return _typeof(obj);
}
// _possibleConstructorReturn 判斷Parent。call(this, name)函數(shù)返回值 是否為null或者函數(shù)或者對(duì)象盟榴。
function _possibleConstructorReturn(self, call) {
    if (call && (_typeof(call) === "object" || typeof call === "function")) {
        return call;
    }
    return _assertThisInitialized(self);
}
// 如何 self 是void 0 (undefined) 則報(bào)錯(cuò)
function _assertThisInitialized(self) {
    if (self === void 0) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }
    return self;
}
// 獲取__proto__
function _getPrototypeOf(o) {
    _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
        return o.__proto__ || Object.getPrototypeOf(o);
    };
    return _getPrototypeOf(o);
}
// 寄生組合式繼承的核心
function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function");
    }
    // Object.create()方法創(chuàng)建一個(gè)新對(duì)象,使用現(xiàn)有的對(duì)象來(lái)提供新創(chuàng)建的對(duì)象的__proto__擎场。 
    // 也就是說(shuō)執(zhí)行后 subClass.prototype.__proto__ === superClass.prototype; 這條語(yǔ)句為true
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            writable: true,
            configurable: true
        }
    });
    if (superClass) _setPrototypeOf(subClass, superClass);
}
// 設(shè)置__proto__
function _setPrototypeOf(o, p) {
    _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
    };
    return _setPrototypeOf(o, p);
}
// instanceof操作符包含對(duì)Symbol的處理
function _instanceof(left, right) {
    if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
        return right[Symbol.hasInstance](left);
    } else {
        return left instanceof right;
    }
}

function _classCallCheck(instance, Constructor) {
    if (!_instanceof(instance, Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}
// 設(shè)置共享屬性和靜態(tài)屬性到不同的對(duì)象上
function _defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
    }
}
//將共享屬性設(shè)置到原型對(duì)象上,靜態(tài)屬性設(shè)置到構(gòu)造函數(shù)上
function _createClass(Constructor, protoProps, staticProps) {
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
}

// ES6
var Parent = function () {
    function Parent(name) {
        _classCallCheck(this, Parent);
        this.name = name;
    }
    _createClass(Parent, [{
        key: "sayName",
        value: function sayName() {
            console.log('my name is ' + this.name);
            return this.name;
        }
    }], [{
        key: "sayHello",
        value: function sayHello() {
            console.log('hello');
        }
    }]);
    return Parent;
}();

var Child = function (_Parent) {
    _inherits(Child, _Parent);
    function Child(name, age) {
        var _this;
        _classCallCheck(this, Child);
        // Child.__proto__ => Parent
        // 所以也就是相當(dāng)于Parent.call(this, name); 是super(name)的一種轉(zhuǎn)換
        // _possibleConstructorReturn 判斷Parent.call(this, name)函數(shù)返回值 是否為null或者函數(shù)或者對(duì)象姨夹。
        _this = _possibleConstructorReturn(this, _getPrototypeOf(Child).call(this, name));
        _this.age = age;
        return _this;
    }
    _createClass(Child, [{
        key: "sayAge",
        value: function sayAge() {
            console.log('my age is ' + this.age);
            return this.age;
        }
    }]);
    return Child;
}(Parent);

var parent = new Parent('Parent');
var child = new Child('Child', 18);
console.log('parent: ', parent); // parent:  Parent {name: "Parent"}
Parent.sayHello(); // hello
parent.sayName(); // my name is Parent
console.log('child: ', child); // child:  Child {name: "Child", age: 18}
Child.sayHello(); // hello
child.sayName(); // my name is Child
child.sayAge(); // my age is 18


從babel轉(zhuǎn)譯我們可以認(rèn)識(shí)到幾點(diǎn):

  • 盡量使用Object.create來(lái)創(chuàng)造父構(gòu)造函數(shù)的實(shí)例
//創(chuàng)建一個(gè)superClass的實(shí)例,constructor屬性指向subClass
subClass.prototype = Object.create(superClass&&superClass.prototype,{
    constructor:{
        value:subClass,
        writable:true,
        configurable:trues
    }
});
//替代方案
function Empty(){};
Empty.prototype = superClass.prototype;
subClass.protoype = new Empty();
subClass.protoype.constructor = subClass;
  • 盡量使用Object.setPrototypeOf而不是proto
Object.setPrototypeOf(subClass,superClass);
//替代方案榄鉴,不推薦
subClass.__proto__ = superClass.__proto__

模擬一下轉(zhuǎn)換實(shí)現(xiàn):

class Fruit{
    constructor(name){
        this.name = name;
    }
    static sayHello(){
        console.log("hello");
    }
    sayName(){
        console.log(this.name);
    }
}
class Banana extends Fruit{
    constructor(name,color){
        super(name);
        this.color = color;
    }
    sayColor(){
        console.log(this.color);
    }
}

//轉(zhuǎn)換實(shí)現(xiàn):
function createPropertiesByObject(target,props){
    for(var i =0;i<props.length;i++){
        let descriptor = props[i];
        //省略
        Object.defineProperties(target,descriptor.key,descriptor);
    }
}
function createProperties(target,protoProps,staticProps){
    createPropertiesByObject(target.prototype,protoProps);
    createPropertiesByObject(target,staticProps);
}

function createPolyFill(Parent){
    function Empty(){};
    Empty.protoype = Parent.prototype;
    return new Empty();
}

function inherit(Child,Parent){
    if(typeof Child!=='function'||typeof Parent!=='function'){
        throw new Error("");
    }
    Child.prototype = Object.create ? Object.create(Parent&&Parent.prototype,{
        constructor:{
            value:Child,
            writable:true,
            configurable:true
        }
    }) : createPolyFill(Parent);
    if(!Object.create){
        Child.prototype.constructor = Child;
    }
}

function setPrototypeOf(Child,Parent){
    setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf : function(C,P){
        C.__proto__ = P
    }
    setPrototypeOf(Child,Parent); 
}

var Fruit = function(){
    function Fruit(){
        this.name = name;
    }
    createProperties(Fruit,[{
        key:"sayName",
        value:function(){
            console.log(this.name)
        }
    }],[{
        key:"sayHello",
        value:function(){
            console.log("hello");
        }
    }])
    return Fruit;
}()

var Banana = function(Parent){
    inherit(Banana,Parent);
    function Banana(name,color){
        Fruit.call(name);
        this.color = color;
    }
    createProperties(Banana,[{
        key:"sayColor",
        value:function(){
            console.log(this.color)
        }
    }])
    setPrototypeOf(Banana,Parent);
    return Banana;
}(Fruit)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末笑跛,一起剝皮案震驚了整個(gè)濱河市几苍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌圣拄,老刑警劉巖售担,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡栅葡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門被因,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人如贷,你說(shuō)我怎么就攤上這事窝稿。” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)糠涛。 經(jīng)常有香客問(wèn)我具篇,道長(zhǎng)侨舆,這世上最難降的妖魔是什么挨下? 我笑而不...
    開封第一講書人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任愁铺,我火速辦了婚禮瓶竭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘次氨。我一直安慰自己谷朝,他們只是感情好洲押,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著圆凰,像睡著了一般杈帐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上专钉,一...
    開封第一講書人閱讀 49,985評(píng)論 1 291
  • 那天挑童,我揣著相機(jī)與錄音,去河邊找鬼跃须。 笑死站叼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的菇民。 我是一名探鬼主播尽楔,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼第练!你這毒婦竟也來(lái)了阔馋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤娇掏,失蹤者是張志新(化名)和其女友劉穎呕寝,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體婴梧,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡下梢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年客蹋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怔球。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嚼酝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出竟坛,到底是詐尸還是另有隱情闽巩,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布担汤,位于F島的核電站涎跨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏崭歧。R本人自食惡果不足惜隅很,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望率碾。 院中可真熱鬧叔营,春花似錦、人聲如沸所宰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)仔粥。三九已至婴谱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間躯泰,已是汗流浹背谭羔。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留麦向,地道東北人瘟裸。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像诵竭,于是被迫代替她去往敵國(guó)和親景描。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • 繼承6種套餐 參照紅皮書秀撇,JS繼承一共6種 1.原型鏈繼承 核心思想:子類的原型指向父類的一個(gè)實(shí)例 Son.pro...
    燈不梨喵閱讀 3,128評(píng)論 1 2
  • 繼承 Javascript中繼承都基于兩種方式:1.通過(guò)原型鏈繼承,通過(guò)修改子類原型的指向向族,使得子類實(shí)例通過(guò)原型鏈...
    LeoCong閱讀 299評(píng)論 0 0
  • 原文鏈接:zhuanlan.zhihu.com (一) 原型鏈繼承: function Parent(name) ...
    越努力越幸運(yùn)_952c閱讀 294評(píng)論 0 2
  • 知道你在呵燕,一直都在,就在這片熟悉的土地上件相。就如同水底靜臥無(wú)數(shù)的石頭再扭,卻不知要挪動(dòng)哪一塊氧苍,才能找到你藏身的地方? 城...
    布谷鳥_ee85閱讀 320評(píng)論 2 8
  • 新的言雨的言語(yǔ) 我不想選擇 我愿意判斷 執(zhí)行
    言雨的言語(yǔ)閱讀 113評(píng)論 0 0