深入理解 js 之继承与原型链
原型鏈與繼承
當談到繼承時,JavaScript 只有一種結構:對象。每個實例對象(object )都有一個私有屬性(稱之為proto)指向它的原型對象(prototype)。該原型對象也有一個自己的原型對象(proto) ,層層向上直到一個對象的原型對象為 null。根據定義,null 沒有原型,并作為這個原型鏈中的最后一個環節。
新建函數,并創建對象
function Car() {this.name = 'BMW'this.price = 95800 } let carBMW = new Car()這時我們的腦海里應該有這樣一張圖:
或許你跟我初次接觸一樣。如果對該圖不怎么理解,不要著急,繼續往下看!!!
基于原型鏈的繼承
JavaScript 對象是動態的屬性“包”(指其自己的屬性)。JavaScript 對象有一個指向一個原型對象的鏈。當試圖訪問一個對象的屬性時,它不僅僅在該對象上搜尋,還會搜尋該對象的原型,以及該對象的原型的原型,依次層層向上搜索,直到找到一個名字匹配的屬性或到達原型鏈的末尾。
從 ECMAScript 6 開始,[[Prototype]] 可以通過Object.getPrototypeOf()和Object.setPrototypeOf()訪問器來訪問。這個等同于 JavaScript 的非標準但許多瀏覽器實現的屬性 __proto__。接著上述代碼
console.log(carBMW) // *Car {name: "BMW", price: 95800}*在 Car() 函數的原型上定義屬性
Car.prototype.price = 200000 Car.prototype.speed = 300console.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繼續寫代碼
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當我們給對象設置一個屬性時,創建的屬性稱為對象的自有屬性。
函數的繼承與其他的屬性繼承沒有差別,包括上面的“屬性遮蔽”(這種情況相當于其他語言的方法重寫)。
當繼承的函數被調用時,this 指向的是當前繼承的對象,而不是繼承的函數所在的原型對象。 let car = {price: 95800,getPrice: function(){return this.a} }console.log(car.getPrice()); // 95800 // 當調用 car.getPrice() 時,'this'指向了car.let bmw = Object.create(car); // bmw 是一個繼承自 car 的對象bmw.price = 400000; // 創建 bmw 的自身屬性 price console.log(bmw.getPrice()); // 400000 // 調用 bmw.getPrice() 時, 'this'指向 bmw. // 又因為 bmw 繼承 car 的 getPrice 函數 // 此時的'this.price' 即 bmw.a,即 price 的自身屬性 'price'雖然有點繞,細讀之后邏輯并不是很復雜
多種方法創建對象
基于字面量創建對象
也就是根據相應的語法結構直接進行創建 let car = {price: 95800,getPrice: function(){return this.a} } // car 為一個對象,因此相應的原型鏈應該如下 // car ---> Object.prototype ---> nulllet cars = ['BMW','Audi','WulingHongguang'] // cars 為一個數組對象,相應的原型鏈應該如下 // cars ---> Array.prototype ---> Object.prototype ---> null基于構造函數創建對象
在 JavaScript 中,構造器其實就是一個普通的函數。當使用 new 操作符 來作用這個函數時,它就可以被稱為構造方法(構造函數)。 function Car() {this.name = 'BMW'this.price = 95800 } Car.prototype.speed = 300 let car = new Car() // 可以知道,car 的自身屬性 {name: "BMW", price: 95800}, 位于原型鏈上的屬性有 speed .基于Object.create()創建對象
ECMAScript 5 中引入了一個新方法:Object.create()??梢哉{用這個方法來創建一個新對象。新對象的原型就是調用 create 方法時傳入的第一個參數 var car = {price: 10000}; // car ---> Object.prototype ---> nullvar carBMW = Object.create(car); // carBMW ---> car ---> Object.prototype ---> null console.log(carBMW.price); // 10000 (繼承而來)基于 Class 關鍵字創建對象
ECMAScript6 引入了一套新的關鍵字用來實現 class。實質上還是語法糖,底層原理依舊不變 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的行為保持一致。
總結
以上是生活随笔為你收集整理的深入理解 js 之继承与原型链的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求环形数组的最大子数组的和
- 下一篇: Spring Boot 2.1.0 已发