摘要:中的繼承并不是明確規定的,而是通過模仿實現的。繼承中的繼承又稱模擬類繼承。將函數抽離到全局對象中,函數內部直接通過作用域鏈查找函數。這種范式編程是基于作用域鏈,與前面講的繼承是基于原型鏈的本質區別是屬性查找方式的不同。
這一節梳理對象的繼承。
我們主要使用繼承來實現代碼的抽象和代碼的復用,在應用層實現功能的封裝。
javascript 的對象繼承方式真的是百花齊放,屬性繼承、原型繼承、call/aplly繼承、原型鏈繼承、對象繼承、構造函數繼承、組合繼承、類繼承... 十幾種,每一種都細講需要花很多時間,這里大致梳理常用的幾種。 javascript 中的繼承并不是明確規定的,而是通過模仿實現的。下面我們簡單梳理幾種有代表性的繼承。
原型繼承ECMAScript 5 中引入了一個新方法: Object.create。可以調用這個方法來創建一個新對象。新對象的原型就是調用 create 方法時傳入的參數:
let too = { a: 1 } let foo = Object.create(too) console.log(foo.a) // 1
通過使用Object.create方法, 對象 too 會被自動加入到 foo 的原型上。我們可以手動模擬實現一個Object.create相同功能的函數:
let too = { a: 1 } function create (prot) { let o = function () {} o.prototype = prot return new o() } let foo = create(too) console.log(foo.a) // 1
或者用更簡單直白的方式來寫:
function Foo() {} Foo.prototype = { a: 1 } let too = new Foo() console.log(too.a) // 1
原型繼承是基于函數的prototype屬性
原型鏈的繼承function Foo (id) { this.a = 1234 this.b = id || 0 } Foo.prototype.showData = function () { console.log(`${this.a}, id: ${this.b}`) } function Too (id) { Foo.apply(this, arguments) } Too.prototype = new Foo() let bar = new Too(999) bar.showData() // 1234, id: 999
上面構造函數TOO 通過重新指定prototype屬性,指向了構造函數Foo的一個實例,然后在Too構造函數中調用Foo的構造函數,從而完成對構造函數Foo功能的繼承。實例bar 通過屬性__proto__來訪問原型鏈上的共享屬性和方法。
class繼承javascript 中的 class繼承又稱模擬類繼承。ES6中正式引入了 class 關鍵字來實現類語言方式創建對象。從此我們也可以使用抽象類的方式來實現繼承。
// 父類 class Polygon { constructor(height, width) { this.height = height; this.width = width; } } // 子類 class Square extends Polygon { constructor(sideLength) { super(sideLength, sideLength); // 調用父對象的搞糟函數 } get area() { return this.height * this.width; } set sideLength(newLength) { this.height = newLength; this.width = newLength; } } var square = new Square(2);
在JavaScript中沒有類的概念,只有對象。雖然我們使用class關鍵字,這讓 JavaScript 看起來似乎是擁有了”類”,可表面看到的不一定是本質,class只是語法糖,實質還是原型鏈那一套。因此,JavaScript中的繼承只是對象與對象之間的繼承。反觀繼承的本質,繼承便是讓子類擁有父類的一些屬性和方法,在JavaScript中便是讓一個對象擁有另一個對象的屬性和方法。
繼承的實現是有很多種,這里不一一列舉。需要注意的是 javascript 引擎在原型鏈上查找屬性是比較耗時的,對性能有副作用。與此同時我們遍歷對象時,原型上的屬性也會被枚舉出來。要識別屬性是在對象上還是從原型上繼承的,我們可以使用對象上的hasOwnProperty方法:
let foo = { a: 1 } foo.hasOwnProperty("a") // true foo.hasOwnProperty("toString") // false
使用hasOwnProperty方法檢測屬性是否直接存在于該對象上并不會遍歷原型鏈。
javascript 支持的是實現繼承,不支持接口繼承,實現繼承主要依賴的是原型鏈。
思考前面我們講到的基本是 javascript 怎么實現面向對象編程的一些知識點。
不從概念來講,簡單來說當我們有屬性和方法需要被重復使用,或者屬性需要被多個對象共享時就需要去考慮繼承的問題。在函數層面,大家通常的做法是使用作用域鏈來實現內層作用域對外層作用域屬性或函數的共享訪問。舉個栗子吧~~
function car (userName) { let color = "red" let wheelNumber = 4 let user = userName let driving = function () { console.log(`${user} 的汽車,${wheelNumber}個輪子滾啊滾...`) } let stop = function () { console.log(`${user} 的汽車,${wheelNumber}個輪子滾不動了,嘎。。。`) } return { driving: driving, stop: stop } } var maruko = car("小丸子") maruko.driving() // 小丸子 的汽車,4個輪子滾啊滾... maruko.stop() // 小丸子 的汽車,4個輪子滾不動了,嘎。。。 var nobita = car("大雄") nobita.driving() // 大雄 的汽車,4個輪子滾啊滾... nobita.stop() // 大雄 的汽車,4個輪子滾不動了,嘎。。。
這。。。什么鬼。是不是有種似曾相識的感覺,這其實就是經典的閉包 ,jquery 年代很多插件 js 庫都采用這種方式去封裝獨立的功能。說閉包也是繼承是不是有點勉強,但是 javascript 里函數也是對象,閉包利用函數的作用域鏈來訪問上層作用域的屬性和函數。當然像閉包這樣不使用this去實現私有屬性比較麻煩, 閉包只適合單實例的場景。再舉一個栗子:
function GoToBed (name) { console.log(`${name}, 睡覺了...`) } function maruko () { let name = "小丸子" function dinner () { console.log(`${name}, 吃完晚餐`) GoToBed(name) } dinner() } function nobita () { let name = "大雄" function homework () { console.log(`${name}, 做完作業`) GoToBed(name) } homework() } maruko() nobita() // 小丸子, 吃完晚餐 // 小丸子, 睡覺了... // 大雄, 做完作業 // 大雄, 睡覺了...
像上面栗子中這樣,以面向過程的方式將公共方法抽離到上層作用域的用法比較常見, 至少我很長時間都是這么干的。將GoToBed函數抽離到全局對象中,函數maruko、nobita 內部直接通過作用域鏈查找GoToBed函數。這種松散結構的代碼塊組織其實跟上面閉包含義是差不多的。
所以依據作用域鏈來進行公共屬性、方法的管理嚴格意義上不能算是繼承, 只能算是 javascript 面向過程的一種代碼抽象分解的方式,一種編程范式。這種范式編程是基于作用域鏈,與前面講的繼承是基于原型鏈的本質區別是屬性查找方式的不同。
全局對象 window 形成一個閉合上下文,如果我們將整個 window 對象假設為一個全局函數,所有創建的局部函數都是該函數的內部函數。當我們使用這個假設時很多問題就要清晰多了,全局函數在頁面被關閉前是一直存在的,且在存活期間為內嵌函數提供執行環境,所有內嵌函數都共享對全局環境的讀寫權限。
這種函數調用時命令式的,函數組織是嵌套的,使用閉包(函數嵌套)的方式來組織代碼流是無模式的一種常態。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/107939.html
摘要:構造函數和實例都通過屬性指向了原形。代碼示例是構造函數的實例的屬性與的屬性保存的值相等,即他們指向同一個對象原形。 講清楚之javascript原型 標簽: javascript javascript 中原形是一個比較難于理解的概念。javascript 權威指南在原形這一章也花了大量的篇幅進行介紹,也許你已經讀過javascript 權威指南,或者已經是讀第N篇了,然而這篇文章的目...
摘要:講清楚之中的這一節來探討。所以當函數作為構造函數調用,則函數內部的綁定到該函數上。在通過構造函數實例化對象時,對象內部的也同樣指向該實例對象。 講清楚之 javascript中的this 這一節來探討this。 在 javascript 中 this 也是一個神的存在,相對于 java 等語言在編譯階段確定,而在 javascript 中, this 是動態綁定,也就是在運行期綁定的。...
閱讀 1523·2021-08-09 13:47
閱讀 2781·2019-08-30 15:55
閱讀 3506·2019-08-29 15:42
閱讀 1127·2019-08-29 13:45
閱讀 3019·2019-08-29 12:33
閱讀 1753·2019-08-26 11:58
閱讀 995·2019-08-26 10:19
閱讀 2423·2019-08-23 18:00