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

資訊專欄INFORMATION COLUMN

講清楚之 javascript 對象繼承

Jonathan Shieber / 1772人閱讀

摘要:中的繼承并不是明確規(guī)定的,而是通過模仿實(shí)現(xiàn)的。繼承中的繼承又稱模擬類繼承。將函數(shù)抽離到全局對象中,函數(shù)內(nèi)部直接通過作用域鏈查找函數(shù)。這種范式編程是基于作用域鏈,與前面講的繼承是基于原型鏈的本質(zhì)區(qū)別是屬性查找方式的不同。

這一節(jié)梳理對象的繼承。

我們主要使用繼承來實(shí)現(xiàn)代碼的抽象和代碼的復(fù)用,在應(yīng)用層實(shí)現(xiàn)功能的封裝。

javascript 的對象繼承方式真的是百花齊放,屬性繼承、原型繼承、call/aplly繼承、原型鏈繼承、對象繼承、構(gòu)造函數(shù)繼承、組合繼承、類繼承... 十幾種,每一種都細(xì)講需要花很多時間,這里大致梳理常用的幾種。 javascript 中的繼承并不是明確規(guī)定的,而是通過模仿實(shí)現(xiàn)的。下面我們簡單梳理幾種有代表性的繼承。

原型繼承

ECMAScript 5 中引入了一個新方法: Object.create??梢哉{(diào)用這個方法來創(chuàng)建一個新對象。新對象的原型就是調(diào)用 create 方法時傳入的參數(shù):

let too = {
    a: 1
}
let foo = Object.create(too)
console.log(foo.a) // 1

通過使用Object.create方法, 對象 too 會被自動加入到 foo 的原型上。我們可以手動模擬實(shí)現(xiàn)一個Object.create相同功能的函數(shù):

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

原型繼承是基于函數(shù)的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

上面構(gòu)造函數(shù)TOO 通過重新指定prototype屬性,指向了構(gòu)造函數(shù)Foo的一個實(shí)例,然后在Too構(gòu)造函數(shù)中調(diào)用Foo的構(gòu)造函數(shù),從而完成對構(gòu)造函數(shù)Foo功能的繼承。實(shí)例bar 通過屬性__proto__來訪問原型鏈上的共享屬性和方法。

class繼承

javascript 中的 class繼承又稱模擬類繼承。ES6中正式引入了 class 關(guān)鍵字來實(shí)現(xiàn)類語言方式創(chuàng)建對象。從此我們也可以使用抽象類的方式來實(shí)現(xiàn)繼承。

// 父類
class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}
// 子類
class Square extends Polygon {
  constructor(sideLength) {
    super(sideLength, sideLength); // 調(diào)用父對象的搞糟函數(shù)
  }
  get area() {
    return this.height * this.width;
  }
  set sideLength(newLength) {
    this.height = newLength;
    this.width = newLength;
  }
}

var square = new Square(2);

在JavaScript中沒有類的概念,只有對象。雖然我們使用class關(guān)鍵字,這讓 JavaScript 看起來似乎是擁有了”類”,可表面看到的不一定是本質(zhì),class只是語法糖,實(shí)質(zhì)還是原型鏈那一套。因此,JavaScript中的繼承只是對象與對象之間的繼承。反觀繼承的本質(zhì),繼承便是讓子類擁有父類的一些屬性和方法,在JavaScript中便是讓一個對象擁有另一個對象的屬性和方法。

繼承的實(shí)現(xiàn)是有很多種,這里不一一列舉。需要注意的是 javascript 引擎在原型鏈上查找屬性是比較耗時的,對性能有副作用。與此同時我們遍歷對象時,原型上的屬性也會被枚舉出來。要識別屬性是在對象上還是從原型上繼承的,我們可以使用對象上的hasOwnProperty方法:

let foo = {
    a: 1
}
foo.hasOwnProperty("a") // true
foo.hasOwnProperty("toString") // false

使用hasOwnProperty方法檢測屬性是否直接存在于該對象上并不會遍歷原型鏈。

javascript 支持的是實(shí)現(xiàn)繼承,不支持接口繼承,實(shí)現(xiàn)繼承主要依賴的是原型鏈。

思考

前面我們講到的基本是 javascript 怎么實(shí)現(xiàn)面向?qū)ο缶幊痰囊恍┲R點(diǎn)。

不從概念來講,簡單來說當(dāng)我們有屬性和方法需要被重復(fù)使用,或者屬性需要被多個對象共享時就需要去考慮繼承的問題。在函數(shù)層面,大家通常的做法是使用作用域鏈來實(shí)現(xiàn)內(nèi)層作用域?qū)ν鈱幼饔糜驅(qū)傩曰蚝瘮?shù)的共享訪問。舉個栗子吧~~

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個輪子滾不動了,嘎。。。

這。。。什么鬼。是不是有種似曾相識的感覺,這其實(shí)就是經(jīng)典的閉包 ,jquery 年代很多插件 js 庫都采用這種方式去封裝獨(dú)立的功能。說閉包也是繼承是不是有點(diǎn)勉強(qiáng),但是 javascript 里函數(shù)也是對象,閉包利用函數(shù)的作用域鏈來訪問上層作用域的屬性和函數(shù)。當(dāng)然像閉包這樣不使用this去實(shí)現(xiàn)私有屬性比較麻煩, 閉包只適合單實(shí)例的場景。再舉一個栗子:

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}, 做完作業(yè)`)
        GoToBed(name)
    }
    homework()
}

maruko()
nobita()

// 小丸子, 吃完晚餐
// 小丸子, 睡覺了...
// 大雄, 做完作業(yè)
// 大雄, 睡覺了...

像上面栗子中這樣,以面向過程的方式將公共方法抽離到上層作用域的用法比較常見, 至少我很長時間都是這么干的。將GoToBed函數(shù)抽離到全局對象中,函數(shù)marukonobita 內(nèi)部直接通過作用域鏈查找GoToBed函數(shù)。這種松散結(jié)構(gòu)的代碼塊組織其實(shí)跟上面閉包含義是差不多的。

所以依據(jù)作用域鏈來進(jìn)行公共屬性、方法的管理嚴(yán)格意義上不能算是繼承, 只能算是 javascript 面向過程的一種代碼抽象分解的方式,一種編程范式。這種范式編程是基于作用域鏈,與前面講的繼承是基于原型鏈的本質(zhì)區(qū)別是屬性查找方式的不同。

全局對象 window 形成一個閉合上下文,如果我們將整個 window 對象假設(shè)為一個全局函數(shù),所有創(chuàng)建的局部函數(shù)都是該函數(shù)的內(nèi)部函數(shù)。當(dāng)我們使用這個假設(shè)時很多問題就要清晰多了,全局函數(shù)在頁面被關(guān)閉前是一直存在的,且在存活期間為內(nèi)嵌函數(shù)提供執(zhí)行環(huán)境,所有內(nèi)嵌函數(shù)都共享對全局環(huán)境的讀寫權(quán)限。

這種函數(shù)調(diào)用時命令式的,函數(shù)組織是嵌套的,使用閉包(函數(shù)嵌套)的方式來組織代碼流是無模式的一種常態(tài)。

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

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

相關(guān)文章

  • 講清楚之 javascript原形

    摘要:構(gòu)造函數(shù)和實(shí)例都通過屬性指向了原形。代碼示例是構(gòu)造函數(shù)的實(shí)例的屬性與的屬性保存的值相等,即他們指向同一個對象原形。 講清楚之javascript原型 標(biāo)簽: javascript javascript 中原形是一個比較難于理解的概念。javascript 權(quán)威指南在原形這一章也花了大量的篇幅進(jìn)行介紹,也許你已經(jīng)讀過javascript 權(quán)威指南,或者已經(jīng)是讀第N篇了,然而這篇文章的目...

    高勝山 評論0 收藏0
  • 講清楚之 javascript中的this

    摘要:講清楚之中的這一節(jié)來探討。所以當(dāng)函數(shù)作為構(gòu)造函數(shù)調(diào)用,則函數(shù)內(nèi)部的綁定到該函數(shù)上。在通過構(gòu)造函數(shù)實(shí)例化對象時,對象內(nèi)部的也同樣指向該實(shí)例對象。 講清楚之 javascript中的this 這一節(jié)來探討this。 在 javascript 中 this 也是一個神的存在,相對于 java 等語言在編譯階段確定,而在 javascript 中, this 是動態(tài)綁定,也就是在運(yùn)行期綁定的。...

    selfimpr 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<