摘要:原型鏈與繼承當談到繼承時,只有一種結(jié)構(gòu)對象。如果對該圖不怎么理解,不要著急,繼續(xù)往下看基于原型鏈的繼承對象是動態(tài)的屬性包指其自己的屬性。當使用操作符來作用這個函數(shù)時,它就可以被稱為構(gòu)造方法構(gòu)造函數(shù)。
原型鏈與繼承
當談到繼承時,JavaScript 只有一種結(jié)構(gòu):對象。每個實例對象(object )都有一個私有屬性(稱之為proto)指向它的原型對象(prototype)。該原型對象也有一個自己的原型對象(proto) ,層層向上直到一個對象的原型對象為 null。根據(jù)定義,null 沒有原型,并作為這個原型鏈中的最后一個環(huán)節(jié)。
新建函數(shù),并創(chuàng)建對象
function Car() { this.name = "BMW" this.price = 95800 } let carBMW = new Car()
這時我們的腦海里應(yīng)該有這樣一張圖:
或許你跟我初次接觸一樣。如果對該圖不怎么理解,不要著急,繼續(xù)往下看!??!
基于原型鏈的繼承JavaScript 對象是動態(tài)的屬性“包”(指其自己的屬性)。JavaScript 對象有一個指向一個原型對象的鏈。當試圖訪問一個對象的屬性時,它不僅僅在該對象上搜尋,還會搜尋該對象的原型,以及該對象的原型的原型,依次層層向上搜索,直到找到一個名字匹配的屬性或到達原型鏈的末尾。
從 ECMAScript 6 開始,[[Prototype]] 可以通過Object.getPrototypeOf()和Object.setPrototypeOf()訪問器來訪問。這個等同于 JavaScript 的非標準但許多瀏覽器實現(xiàn)的屬性 __proto__。
接著上述代碼
console.log(carBMW) // *Car {name: "BMW", price: 95800}*
在 Car() 函數(shù)的原型上定義屬性
Car.prototype.price = 200000 Car.prototype.speed = 300 console.log(carBMW.__proto__) // {price: 200000, speed: 300, constructor: ?} console.log(carBMW.__proto__.__proto__ == Object.prototype) // true console.log(carBMW.__proto__.__proto__.__proto__) // null
綜合上述代碼,可以給出如下原型鏈
{name: "BMW", price: 95800} ---> {price: 200000, speed: 300, constructor: ?} ---> Object.prototype ---> null
繼續(xù)寫代碼
console.log(carBMW.name) // BMW // name 為 carBMW 自身的屬性 console.log(carBMW.price) // 95800 // price 為 carBMW 自身的屬性,原型上也有一個"price"屬性,但是不會被訪問到,這種情況稱為"屬性遮蔽 (property shadowing)" console.log(carBMW.speed) // 300 // speed 不是 carBMW 自身的屬性,但是 speed 位于該原型鏈上,因此我們依然可以取到該值 // 當然如果你試著訪問一個不存在原型鏈上的屬性時,這時候會給你返回一個undefined
當我們給對象設(shè)置一個屬性時,創(chuàng)建的屬性稱為對象的自有屬性。
函數(shù)的繼承與其他的屬性繼承沒有差別,包括上面的“屬性遮蔽”(這種情況相當于其他語言的方法重寫)。
當繼承的函數(shù)被調(diào)用時,this 指向的是當前繼承的對象,而不是繼承的函數(shù)所在的原型對象。
let car = { price: 95800, getPrice: function(){ return this.a } } console.log(car.getPrice()); // 95800 // 當調(diào)用 car.getPrice() 時,"this"指向了car. let bmw = Object.create(car); // bmw 是一個繼承自 car 的對象 bmw.price = 400000; // 創(chuàng)建 bmw 的自身屬性 price console.log(bmw.getPrice()); // 400000 // 調(diào)用 bmw.getPrice() 時, "this"指向 bmw. // 又因為 bmw 繼承 car 的 getPrice 函數(shù) // 此時的"this.price" 即 bmw.a,即 price 的自身屬性 "price"
雖然有點繞,細讀之后邏輯并不是很復(fù)雜
多種方法創(chuàng)建對象 基于字面量創(chuàng)建對象也就是根據(jù)相應(yīng)的語法結(jié)構(gòu)直接進行創(chuàng)建
let car = { price: 95800, getPrice: function(){ return this.a } } // car 為一個對象,因此相應(yīng)的原型鏈應(yīng)該如下 // car ---> Object.prototype ---> null let cars = ["BMW","Audi","WulingHongguang"] // cars 為一個數(shù)組對象,相應(yīng)的原型鏈應(yīng)該如下 // cars ---> Array.prototype ---> Object.prototype ---> null基于構(gòu)造函數(shù)創(chuàng)建對象
在 JavaScript 中,構(gòu)造器其實就是一個普通的函數(shù)。當使用 new 操作符 來作用這個函數(shù)時,它就可以被稱為構(gòu)造方法(構(gòu)造函數(shù))。
function Car() { this.name = "BMW" this.price = 95800 } Car.prototype.speed = 300 let car = new Car() // 可以知道,car 的自身屬性 {name: "BMW", price: 95800}, 位于原型鏈上的屬性有 speed .基于Object.create()創(chuàng)建對象
ECMAScript 5 中引入了一個新方法:Object.create()。可以調(diào)用這個方法來創(chuàng)建一個新對象。新對象的原型就是調(diào)用 create 方法時傳入的第一個參數(shù)
var car = {price: 10000}; // car ---> Object.prototype ---> null var carBMW = Object.create(car); // carBMW ---> car ---> Object.prototype ---> null console.log(carBMW.price); // 10000 (繼承而來)基于 Class 關(guān)鍵字創(chuàng)建對象
ECMAScript6 引入了一套新的關(guān)鍵字用來實現(xiàn) class。實質(zhì)上還是語法糖,底層原理依舊不變
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return "(" + this.x + ", " + this.y + ")"; } } var point = new Point(2, 3); point.toString() // (2, 3) point.hasOwnProperty("x") // true point.hasOwnProperty("y") // true point.hasOwnProperty("toString") // false point.__proto__.hasOwnProperty("toString") // true
以上代碼中,x和y都是實例對象point自身的屬性(因為定義在this變量上),所以hasOwnProperty方法返回true,而toString是原型對象的屬性(因為定義在Point類上),所以hasOwnProperty方法返回false。這些都與ES5的行為保持一致。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/99231.html
摘要:我們用一張圖表示構(gòu)造函數(shù)和實例原型之間的關(guān)系好了構(gòu)造函數(shù)和實例原型之間的關(guān)系我們已經(jīng)梳理清楚了,那我們怎么表示實例與實例原型,也就是或者和之間的關(guān)系呢。 開篇: 在Brendan Eich大神為JavaScript設(shè)計面向?qū)ο笙到y(tǒng)的時候,借鑒了Self 和Smalltalk這兩門基于原型的語言,之所以選擇基于原型的面向?qū)ο笙到y(tǒng),并不是因為時間匆忙,它設(shè)計起來相對簡單,而是因為從一開始B...
摘要:我們用一張圖表示構(gòu)造函數(shù)和實例原型之間的關(guān)系好了構(gòu)造函數(shù)和實例原型之間的關(guān)系我們已經(jīng)梳理清楚了,那我們怎么表示實例與實例原型,也就是或者和之間的關(guān)系呢。 開篇: 在Brendan Eich大神為JavaScript設(shè)計面向?qū)ο笙到y(tǒng)的時候,借鑒了Self 和Smalltalk這兩門基于原型的語言,之所以選擇基于原型的面向?qū)ο笙到y(tǒng),并不是因為時間匆忙,它設(shè)計起來相對簡單,而是因為從一開始B...
摘要:通常有這兩種繼承方式接口繼承和實現(xiàn)繼承。理解繼承的工作是通過調(diào)用函數(shù)實現(xiàn)的,所以是寄生,將繼承工作寄托給別人做,自己只是做增強工作。適用基于某個對象或某些信息來創(chuàng)建對象,而不考慮自定義類型和構(gòu)造函數(shù)。 一、繼承的概念 繼承,是面向?qū)ο笳Z言的一個重要概念。通常有這兩種繼承方式:接口繼承和實現(xiàn)繼承。接口繼承只繼承方法簽名,而實現(xiàn)繼承則繼承實際的方法。 《JS高程》里提到:由于函數(shù)沒有簽名,...
摘要:深入之繼承的多種方式和優(yōu)缺點深入系列第十五篇,講解各種繼承方式和優(yōu)缺點。對于解釋型語言例如來說,通過詞法分析語法分析語法樹,就可以開始解釋執(zhí)行了。 JavaScript深入之繼承的多種方式和優(yōu)缺點 JavaScript深入系列第十五篇,講解JavaScript各種繼承方式和優(yōu)缺點。 寫在前面 本文講解JavaScript各種繼承方式和優(yōu)缺點。 但是注意: 這篇文章更像是筆記,哎,再讓我...
摘要:是完全的面向?qū)ο笳Z言,它們通過類的形式組織函數(shù)和變量,使之不能脫離對象存在。而在基于原型的面向?qū)ο蠓绞街?,對象則是依靠構(gòu)造器利用原型構(gòu)造出來的。 JavaScript 函數(shù)式腳本語言特性以及其看似隨意的編寫風格,導(dǎo)致長期以來人們對這一門語言的誤解,即認為 JavaScript 不是一門面向?qū)ο蟮恼Z言,或者只是部分具備一些面向?qū)ο蟮奶卣鳌1疚膶⒒貧w面向?qū)ο蟊疽?,從對語言感悟的角度闡述為什...
閱讀 1175·2021-11-22 15:24
閱讀 4454·2021-09-23 11:51
閱讀 2316·2021-09-08 09:36
閱讀 3523·2019-08-30 15:43
閱讀 1306·2019-08-30 13:01
閱讀 1125·2019-08-30 12:48
閱讀 546·2019-08-29 12:52
閱讀 3379·2019-08-29 12:41