成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

JavaScript 中的類和繼承

rottengeek / 386人閱讀

摘要:因為操作符創(chuàng)建的對象都繼承自構(gòu)造函數(shù)的屬性。繼承的實現(xiàn)中常用的繼承方式是組合繼承,也就是通過構(gòu)造函數(shù)和原型鏈繼承同時來模擬繼承的實現(xiàn)。

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

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

類的實現(xiàn)

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

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

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

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

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

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

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

如果類的實例需要共享類的方法,那么就需要給構(gòu)造函數(shù)的 prototype 屬性添加方法了。因為 new 操作符創(chuàng)建的對象都繼承自構(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.
繼承的實現(xiàn)

JavaScript 中常用的繼承方式是組合繼承,也就是通過構(gòu)造函數(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 會找不到 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 方法。這個是 ES5 中新增的方法,用來創(chuàng)建一個擁有指定原型的對象。如果環(huán)境不兼容,可以用下面這個 Polyfill 來實現(xiàn)(僅實現(xiàn)第一個參數(shù))。

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

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

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

//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;
//另一個子類
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 會輸出什么?

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

ES6 的繼承

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

類的實現(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)鍵字來實現(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.

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

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)鍵字,來實現(xiàn)靜態(tài)方法。靜態(tài)方法可以繼承,但只能由類本身調(diào)用,不能被實例調(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

可以看到,在實例上調(diào)用的時候會直接報錯。

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 中實現(xiàn)繼承有了一個標(biāo)準(zhǔn)的方法。雖然它們只是語法糖,背后的本質(zhì)還是通過原型鏈以及構(gòu)造函數(shù)實現(xiàn)的,不過在寫法上更易于我們理解而且也更加清晰。

參考:

JavaScript繼承方式詳解

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

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

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/79034.html

相關(guān)文章

  • JavaScript || 類和模塊

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

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

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

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

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

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

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

    PrototypeZ 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<