成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JavaScript 中的類和繼承

rottengeek / 356人閱讀

摘要:因?yàn)椴僮鞣麆?chuàng)建的對(duì)象都繼承自構(gòu)造函數(shù)的屬性。繼承的實(shí)現(xiàn)中常用的繼承方式是組合繼承,也就是通過構(gòu)造函數(shù)和原型鏈繼承同時(shí)來模擬繼承的實(shí)現(xiàn)。

原文發(fā)布在我的博客

我們都知道 JavaScript 是一門基于原型的語言。當(dāng)我們調(diào)用一個(gè)對(duì)象本身沒有的屬性時(shí),JavaScript 就會(huì)從對(duì)象的原型對(duì)象上去找該屬性,如果原型上也沒有該屬性,那就去找原型的原型,一直找原型鏈的末端也就是 Object.prototype 的原型 null。這種屬性查找的方式我們稱之為原型鏈。

類的實(shí)現(xiàn)

由于 JavaScript 本身是沒有的類的感念的。所以我們?nèi)绻獙?shí)現(xiàn)一個(gè)類,一般是通過構(gòu)造函數(shù)來模擬類的實(shí)現(xiàn):

function Person(name,age){  //實(shí)現(xiàn)一個(gè)類
    this.name = name;
    this.age = age;
}
var you = new Person("you",23); //通過 new 來新建實(shí)例

首先新建一個(gè) Person 的構(gòu)造函數(shù),為了和一般的函數(shù)區(qū)別,我們會(huì)使用 CamelCase 方式來命名構(gòu)造函數(shù)。
然后通過 new 操作符來創(chuàng)建實(shí)例,new 操作符其實(shí)干了這么幾件事:

創(chuàng)建一個(gè)繼承自 Person.prototype 的新對(duì)象

構(gòu)造函數(shù) Person 執(zhí)行時(shí),相應(yīng)的參數(shù)傳入,同時(shí)上下文被指定為這個(gè)新建的對(duì)象。

如果構(gòu)造函數(shù)返回了一個(gè)對(duì)象,那么這個(gè)對(duì)象會(huì)取代 new 的結(jié)果。如果構(gòu)造函數(shù)返回的不是對(duì)象,則會(huì)忽略這個(gè)返回值。

返回值不是對(duì)象
function Person(name){
    this.name = name;
    return "person"
}
var you = new Person("you");
//  you 的值: Person {name: "you"}
返回值是對(duì)象
function Person(name){
    this.name = name;
    return [1,2,3]
}
var you = new Person("you");
//  you的值: [1,2,3]

如果類的實(shí)例需要共享類的方法,那么就需要給構(gòu)造函數(shù)的 prototype 屬性添加方法了。因?yàn)?new 操作符創(chuàng)建的對(duì)象都繼承自構(gòu)造函數(shù)的 prototype 屬性。他們可以共享定義在類 prototype 上的方法和屬性。

function Person(name,age){  
    this.name = name;
    this.age = age;
}
Person.prototype = {
    sayName: function(){
        console.log("My name is",this.name);
    }
}
var you = new Person("you",23);
var me = new Person("me",23);
you.sayName()   // My name is you.
me.sayName()    // My name is me.
繼承的實(shí)現(xiàn)

JavaScript 中常用的繼承方式是組合繼承,也就是通過構(gòu)造函數(shù)和原型鏈繼承同時(shí)來模擬繼承的實(shí)現(xiàn)。

//Person 構(gòu)造函數(shù)如上
function Student(name,age,clas){
    Person.call(this,name,age)
    this.clas = clas;
}
Student.prototype = Object.create(Person.prototype);        // Mark 1
Student.constructor = Student;      //如果不指明,則 Student 會(huì)找不到 constructor
Student.prototype.study = function(){
    console.log("I study in class",this.clas)
};
var liming = new Student("liming",23,7);
liming instanceof Person    //true
liming instanceof Student   //true
liming.sayName();       // My name is liming
liming.study();         // I study in class 7

代碼中 Mark 1 用到了 Object.create 方法。這個(gè)是 ES5 中新增的方法,用來創(chuàng)建一個(gè)擁有指定原型的對(duì)象。如果環(huán)境不兼容,可以用下面這個(gè) Polyfill 來實(shí)現(xiàn)(僅實(shí)現(xiàn)第一個(gè)參數(shù))。

if(!Object.create){
    Object.create = function(obj){
        function F(){};
        F.prototype = obj;
        return new F();
    }
}

其實(shí)就是把 obj 賦值給臨時(shí)函數(shù) F ,然后返回一個(gè) F 的實(shí)例。這樣通過代碼 Mark 1 Student 就得到了 Person.prototype 上的所有屬性。有人會(huì)問了,那么為什么不干脆把 Person.prototype 直接賦值給 Student.prototype 呢?

是的,直接賦值是可以達(dá)到子類共享父類 prototype 的目的,但是它破壞了原型鏈。即:子類和父類共用了同一個(gè) prototype,這樣當(dāng)某一個(gè)子類修改 prototype 的時(shí)候,其實(shí)同時(shí)也修改了父類的 prototype,那么就會(huì)影響到所有基于這個(gè)父類創(chuàng)建的子類,這并不是我們想要的結(jié)果??蠢樱?/p>

//Person 同上
//Student 同上
Student.prototype = Person.prototype;
Student.prototype.sayName = function(){
    console.log("My name is",this.name,"my class is",this.clas)
}
var liming = new Student("liming",23,7)
liming.sayName()        //My name is liming,my class is 7;
//另一個(gè)子類
function Employee(name,age,salary){
    Person.call(name,age);
    this.salary = salary;
}
Employee.prototype = Person.prototype;
var emp = new Employee("emp",23,10000);
emp.sayName()       //Mark 2

你們猜 Mark 2 會(huì)輸出什么?

我們期望的 Mark 2 應(yīng)該會(huì)輸出 "My name is emp". 但實(shí)際上報(bào)錯(cuò),為什么呢?因?yàn)槲覀兏膶?Student.prototype 的時(shí)候,也同時(shí)修改了 Person.prototype,最終導(dǎo)致 emp 繼承的 prototype 是我們所不期望的,它的 sayName 方法是 My name is",this.name,"my class is",this.clas,這樣自然是會(huì)報(bào)錯(cuò)的。

ES6 的繼承

隨著 ECMAScript 6 的發(fā)布,我們有了新的方法來實(shí)現(xiàn)繼承。也就是通過 class 關(guān)鍵字。

類的實(shí)現(xiàn)
class Person {
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    sayHello(){
        console.log(`My name is ${this.name},i"m ${this.age} years old`)
    }
}
var you = new Person("you",23);
you.sayHello()      //My name is you,i"m 23 years old.
繼承

ES6 里面的繼承也很方便,通過 extends 關(guān)鍵字來實(shí)現(xiàn)。

class Student extends Person{
    constructor(name,age,cla){
        super(name,age);
        this.class = cla;
    }
    study(){
        console.log(`I"m study in class ${this.class}`)
    }
}
var liming = new Student("liming",23,7)
liming.study()      // I"m study in class 7.

這個(gè)繼承相比上面的 ES5 里面實(shí)現(xiàn)的繼承要方便了很多,但其實(shí)原理是一樣的,提供的這些關(guān)鍵字方法只是語法糖而已,并沒有改變 Js 是基于原型這么一個(gè)事實(shí)。不過 extends 這樣實(shí)現(xiàn)的繼承有一個(gè)限制,就是不能定義屬性,只能定義方法。要新添屬性,還是得通過修改 prototype 來達(dá)到目的。

Student.prototype.teacher = "Mr.Li"
var liming = new Student("liming",23,7)
var hanmeimei = new Student("hanmeimei",23,7)
liming.teacher          //Mr.Li
hanmeimei.teacher       //Mr.Li
靜態(tài)方法

ES6 還提供了 static 關(guān)鍵字,來實(shí)現(xiàn)靜態(tài)方法。靜態(tài)方法可以繼承,但只能由類本身調(diào)用,不能被實(shí)例調(diào)用。

class Person{
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    static say(){
        console.log("Static")
    }
}
class Student extends Person{}
Person.say()        // Static
Student.say()       // Static
var you = new Person("you",23);
you.say()           // TypeError: liming.say is not a function

可以看到,在實(shí)例上調(diào)用的時(shí)候會(huì)直接報(bào)錯(cuò)。

Super關(guān)鍵字

在子類中可以通過 super 來調(diào)用父類,根據(jù)調(diào)用位置的不同,行為也不同。在 constructor 中調(diào)用,相當(dāng)于調(diào)用父類的 constructor 方法,而在普通方法里面調(diào)用則相當(dāng)與調(diào)用父類本身。

class Person {
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    sayHello(){
        console.log(`My name is ${this.name},i"m ${this.age} years old`)
    }
}
class Student extends Person{
    constructor(name,age,cla){
        super(name,age);        // 必須在子類調(diào)用 this 前執(zhí)行,調(diào)用了父類的 constructor
        this.class = cla;       
    }
    sayHello(){
        super.sayHello;         // 調(diào)用父類方法
        console.log("Student say")
    }
}
var liming = new Student("liming",23,7);
liming.say()        // My name is liming,i"m 23 years old.
 Student say.
總結(jié)

至此,我們可以看到:在 ES6 發(fā)布以后,JavaScript 中實(shí)現(xiàn)繼承有了一個(gè)標(biāo)準(zhǔn)的方法。雖然它們只是語法糖,背后的本質(zhì)還是通過原型鏈以及構(gòu)造函數(shù)實(shí)現(xiàn)的,不過在寫法上更易于我們理解而且也更加清晰。

參考:

JavaScript繼承方式詳解

JavaScript 原型系統(tǒng)的變遷,以及 ES6 class

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/79034.html

相關(guān)文章

  • JavaScript || 類和模塊

    摘要:屬性每個(gè)函數(shù)默認(rèn)有屬性方法返回的函數(shù)除外,其值為構(gòu)造函數(shù)創(chuàng)建對(duì)象繼承的對(duì)象。其思路使用原型鏈實(shí)現(xiàn)原型屬性和方法的繼承通過借用構(gòu)造函數(shù)實(shí)現(xiàn)實(shí)例屬性繼承。 1 類和模塊 每個(gè)獨(dú)立的JavaScript對(duì)象都是一個(gè)屬性的集合,獨(dú)立對(duì)象間沒有任何關(guān)系 ES5中的類是基于原型繼承實(shí)現(xiàn)的:如果兩個(gè)對(duì)象從同一個(gè)原型對(duì)象繼承屬性,稱兩個(gè)對(duì)象為同一個(gè)類的實(shí)例。r instanceof Range.pr...

    CoorChice 評(píng)論0 收藏0
  • JavaScript 工作原理之十五-類和繼承及 Babel 和 TypeScript 代碼轉(zhuǎn)換探秘

    摘要:使用新的易用的類定義,歸根結(jié)底也是要?jiǎng)?chuàng)建構(gòu)造函數(shù)和修改原型。首先,它把構(gòu)造函數(shù)當(dāng)成單獨(dú)的函數(shù)且包含類屬性集。該節(jié)點(diǎn)還儲(chǔ)存了指向父類的指針引用,該父類也并儲(chǔ)存了構(gòu)造函數(shù),屬性集和及父類引用,依次類推。 原文請(qǐng)查閱這里,略有刪減,本文采用知識(shí)共享署名 4.0 國(guó)際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第...

    GeekGhc 評(píng)論0 收藏0
  • JavaScript 工作原理之十五-類和繼承及 Babel 和 TypeScript 代碼轉(zhuǎn)換探秘

    摘要:使用新的易用的類定義,歸根結(jié)底也是要?jiǎng)?chuàng)建構(gòu)造函數(shù)和修改原型。首先,它把構(gòu)造函數(shù)當(dāng)成單獨(dú)的函數(shù)且包含類屬性集。該節(jié)點(diǎn)還儲(chǔ)存了指向父類的指針引用,該父類也并儲(chǔ)存了構(gòu)造函數(shù),屬性集和及父類引用,依次類推。 原文請(qǐng)查閱這里,略有刪減,本文采用知識(shí)共享署名 4.0 國(guó)際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第...

    BigNerdCoding 評(píng)論0 收藏0
  • JavaScript是如何工作的:深入類和繼承內(nèi)部原理+Babel和 TypeScript 之間轉(zhuǎn)換

    摘要:下面是用實(shí)現(xiàn)轉(zhuǎn)成抽象語法樹如下還支持繼承以下是轉(zhuǎn)換結(jié)果最終的結(jié)果還是代碼,其中包含庫(kù)中的一些函數(shù)。可以使用新的易于使用的類定義,但是它仍然會(huì)創(chuàng)建構(gòu)造函數(shù)和分配原型。 這是專門探索 JavaScript 及其所構(gòu)建的組件的系列文章的第 15 篇。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 如果你錯(cuò)過了前面的章節(jié),可以在這里找到它們: JavaScript 是...

    PrototypeZ 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<