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

資訊專(zhuān)欄INFORMATION COLUMN

講清楚之 javascript 對(duì)象(一)

boredream / 591人閱讀

摘要:工廠函數(shù)創(chuàng)建實(shí)例同時(shí)也面臨實(shí)例類(lèi)型的問(wèn)題返回的對(duì)象是構(gòu)造函數(shù)的實(shí)例為什么實(shí)例函數(shù)不相等呢在中是一種引用類(lèi)型。使用指定的參數(shù)調(diào)用構(gòu)造函數(shù),并將綁定到新創(chuàng)建的對(duì)象。構(gòu)造函數(shù)返回的對(duì)象就是表達(dá)式的結(jié)果。

有了前面幾節(jié)的知識(shí),這一節(jié)我們理解起來(lái)就要輕松很多。在 javascript 里函數(shù)也是對(duì)象,瀏覽器的全局上下文也是對(duì)象, key - value 的身影在代碼里比較常見(jiàn),合理的使用對(duì)象多維度、可擴(kuò)展的特性可以為開(kāi)發(fā)中帶來(lái)很多樂(lè)趣。

如果知識(shí)存在盲區(qū),則實(shí)際開(kāi)發(fā)中就會(huì)就會(huì)應(yīng)為評(píng)估不足,模型設(shè)計(jì)不合理出現(xiàn)各種問(wèn)題, 小則打打補(bǔ)丁、模塊API重新設(shè)計(jì),做兼容處理。 大則是關(guān)鍵數(shù)據(jù)維度無(wú)法滿(mǎn)足應(yīng)用場(chǎng)景, 就需要費(fèi)事費(fèi)力的進(jìn)行架構(gòu)調(diào)整或者重構(gòu)了。

下面我們來(lái)梳理一下 javascript 對(duì)象的表現(xiàn)方式和特點(diǎn),過(guò)于細(xì)節(jié)的知識(shí)就不梳理了。

JavaScript 的設(shè)計(jì)是一個(gè)簡(jiǎn)單的基于對(duì)象的范式。一個(gè)對(duì)象就是一系列屬性的集合,一個(gè)屬性包含一個(gè)屬性名和一個(gè)屬性值。一個(gè)屬性的值可以是函數(shù),這種情況下屬性也被稱(chēng)為方法。除了瀏覽器里面預(yù)定義的那些對(duì)象之外,我們也可以定義自己的對(duì)象。熟悉 javascript 的語(yǔ)法特性,合理的設(shè)計(jì)數(shù)據(jù)模型,創(chuàng)建靈活、不含糊的自定義對(duì)象能夠提高 javascript 的運(yùn)行效率。
字面量對(duì)象

使用字面量方式創(chuàng)建對(duì)象占據(jù)了大多數(shù)開(kāi)發(fā)場(chǎng)景,字面量對(duì)象示例:

let foo = {
    a: 1,
    b: "1234",
    c: function () {
        console.log(this.a + this.b)
    }
}
let foo1 = {
    a: 666,
    b: "hi",
    c: function () {
        console.log(`${this.b}, ${this.a}`)
    }
}
foo.c() // "11234"
foo1.c() // "hi, 666"

對(duì)象字面量的特點(diǎn)主要是直觀、簡(jiǎn)單靈活,每一個(gè)key、value在編碼階段就是確定的。

使用對(duì)象字面量的方式來(lái)創(chuàng)建對(duì)象的缺點(diǎn)是,當(dāng)我們需要?jiǎng)?chuàng)建多個(gè)相同對(duì)象時(shí)必須為每個(gè)對(duì)象在源代碼中編寫(xiě)變量和方法。當(dāng)這樣的相同內(nèi)容的對(duì)象很多時(shí)就是一場(chǎng)災(zāi)難。于是我們發(fā)明了很多其他創(chuàng)建對(duì)象的方式,下面進(jìn)一步探討。

工廠模式

工廠模式創(chuàng)建對(duì)象示例:

let createFoo = function (a, b, c) {
    let o = new Object()
    o.a = a
    o.b = b
    o.c = c
    return o
}
let foo = createFoo(1, "1234", function(){
    console.log(this.a + this.b)
})
let foo1 = createFoo(666, "hi", function(){
    console.log(`${this.b}, ${this.a}`)
})

foo.c() // "11234"
foo1.c() // "hi, 666"

所謂工廠模式就是對(duì)象的創(chuàng)建就像"商品"通過(guò)工廠按照標(biāo)準(zhǔn)化的流程被加工出來(lái)。

上面就是一個(gè)工廠函數(shù)的栗子,執(zhí)行 createFoo 函數(shù)時(shí)先創(chuàng)建一個(gè)對(duì)象 o,然后把傳遞進(jìn)來(lái)的實(shí)參添加到 o 上面,最后返回對(duì)象 o。這樣每次執(zhí)行 createFoo 函數(shù)都會(huì)返回一個(gè)新的對(duì)象,當(dāng)我們需要1000個(gè)相似對(duì)象時(shí) createFoo 就為我們?cè)趦?nèi)部生成了1000個(gè)獨(dú)立的對(duì)象 o。通過(guò)對(duì)這個(gè)栗子的分析會(huì)發(fā)現(xiàn): 工廠函數(shù)在進(jìn)行大批量對(duì)象創(chuàng)建時(shí)對(duì)資源的消耗比較大,同時(shí)由于每次都返回的是一個(gè)新對(duì)象,我們就沒(méi)辦法判斷對(duì)象的類(lèi)型。

工廠函數(shù)與字面量方式創(chuàng)建對(duì)象相比,優(yōu)勢(shì)就是不用在編碼階段創(chuàng)建大批量相似結(jié)構(gòu)的對(duì)象,而這一系列的創(chuàng)建工作都是在運(yùn)行階段創(chuàng)建的。每次創(chuàng)建實(shí)例時(shí)都要?jiǎng)?chuàng)建實(shí)例對(duì)應(yīng)的所有屬性和方法,所以工廠函數(shù)同樣存在創(chuàng)建N個(gè)實(shí)例需要?jiǎng)?chuàng)建N個(gè)屬性、方法的問(wèn)題。

工廠函數(shù)創(chuàng)建實(shí)例同時(shí)也面臨實(shí)例類(lèi)型的問(wèn)題:

foo instanceof createFoo // false
foo1 instanceof createFoo // false

// 返回的對(duì)象是構(gòu)造函數(shù) Object 的實(shí)例
foo instanceof Object // true
foo1 instanceof Object // true
為什么實(shí)例函數(shù)不相等呢?
在 JavaScript 中 objects 是一種引用類(lèi)型。兩個(gè)獨(dú)立聲明的對(duì)象永遠(yuǎn)也不會(huì)相等(因?yàn)樽兞?foo 和 foo1 指向的堆地址不同),即使他們有相同的屬性,只有在比較一個(gè)對(duì)象和這個(gè)對(duì)象的引用時(shí),才會(huì)返回true.
let too = {
    a: 1
}
let too1 = {
    a: 1
}
let too2 = too1

too == too1 // false
too === too1 // false

too1 == too2 // true
too1 ===too2 // true
構(gòu)造函數(shù)

構(gòu)造函數(shù)方式創(chuàng)建自定義對(duì)象,就是利用函數(shù)中構(gòu)造函數(shù)、原形、實(shí)例對(duì)象之間的關(guān)系來(lái)封裝私有屬性、公有屬性:

function Foo (a, b, c) {
    this.a = a
    this.b = b
    this.c = c
}
let foo1 = new Foo(1, "1234", function(){
    console.log(this.a + this.b)
})
let foo2 = new Foo(666, "hi", function(){
    console.log(`${this.b}, ${this.a}`)
})

// foo1、foo2 是 Foo 的實(shí)例
foo1 instanceof Foo // true
foo2 instanceof Foo // true

構(gòu)造函數(shù)的實(shí)現(xiàn)看著要簡(jiǎn)單很多,也能通過(guò)實(shí)例判斷出類(lèi)型。

構(gòu)造函數(shù)的執(zhí)行邏輯:

構(gòu)造函數(shù)初始化階段首先會(huì)向上下文棧中壓入一個(gè)上下文,接著在變量對(duì)象創(chuàng)建的時(shí)候會(huì)收集實(shí)參,初始化函數(shù)內(nèi)部的變量申明、確定 this 的指向、確定作用鏈。將實(shí)參的值分別拷貝給變量a、b、c。然后像普通函數(shù)一樣進(jìn)入執(zhí)行階段,執(zhí)行函數(shù)內(nèi)部語(yǔ)句.

構(gòu)造函數(shù)就是函數(shù) 既然構(gòu)造函數(shù)就是普通函數(shù), 那么為什在函數(shù)前面加一個(gè) new 就能實(shí)例化并返回一個(gè)對(duì)象呢?

我們來(lái)創(chuàng)建一個(gè)模擬構(gòu)造函數(shù)加深理解,沒(méi)錯(cuò)是創(chuàng)建一個(gè)構(gòu)造函數(shù)(思路來(lái)源于網(wǎng)絡(luò), 無(wú)恥的偷過(guò)來(lái)了?????)。

// 假設(shè)我們創(chuàng)建一個(gè)汽車(chē)對(duì)象類(lèi)型, car函數(shù)
function Car(make, model, year) {
    this.make = make
    this.model = model
    this.year = year
    this.drive = function (name) {
        console.log(`${name} drives the ${this.model} ${this.make}`)
    }  
}

// 將函數(shù)以參數(shù)形式傳入
function New(func) {
    // 聲明一個(gè)中間對(duì)象,該對(duì)象為最終返回的實(shí)例
    let res = {}
    if (func.prototype !== null) {
        // 將實(shí)例的原型指向構(gòu)造函數(shù)的原型
        res.__proto__ = func.prototype
    }
    // ret為構(gòu)造函數(shù)執(zhí)行的結(jié)果,這里通過(guò)apply,將構(gòu)造函數(shù)內(nèi)部的this指向修改為指向res,即為實(shí)例對(duì)象
    var ret = func.apply(res, Array.prototype.slice.call(arguments, 1))
    // 當(dāng)我們?cè)跇?gòu)造函數(shù)中明確指定了返回對(duì)象時(shí),那么new的執(zhí)行結(jié)果就是該返回對(duì)象
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret
    }
    // 如果沒(méi)有明確指定返回對(duì)象,則默認(rèn)返回res,這個(gè)res就是實(shí)例對(duì)象
    return res
}
// 通過(guò)new聲明創(chuàng)建實(shí)例,這里的p1,實(shí)際接收的正是new中返回的res
let mycar  = New(Car, "Tesla", "Model X", 2018)
mycar.drive("小丸子")
console.log(mycar.make);

// mycar 是 Car 的實(shí)例
mycar instanceof Car // true

let mycar = new Car(...) 實(shí)例化對(duì)象的方式看作是let mycar = New(Car, "Tesla", "Model X", 2018) 的一種簡(jiǎn)單的語(yǔ)法糖寫(xiě)法。

代碼 new Car(...) 執(zhí)行時(shí),會(huì)發(fā)生以下事情:

一個(gè)繼承自 Car.prototype 的新對(duì)象被創(chuàng)建。

使用指定的參數(shù)調(diào)用構(gòu)造函數(shù) Car ,并將 this 綁定到新創(chuàng)建的對(duì)象。new Car 等同于 new Car(),也就是沒(méi)有指定參數(shù)列表,Car 不帶任何參數(shù)調(diào)用的情況。

構(gòu)造函數(shù)返回的對(duì)象就是 new 表達(dá)式的結(jié)果。如果構(gòu)造函數(shù)沒(méi)有顯式返回一個(gè)對(duì)象,則使用步驟1創(chuàng)建的對(duì)象。(一般情況下,構(gòu)造函數(shù)不返回值,但是用戶(hù)可以選擇主動(dòng)返回對(duì)象,來(lái)覆蓋正常的對(duì)象創(chuàng)建步驟)

實(shí)例類(lèi)型無(wú)法判斷的問(wèn)題, 通過(guò)構(gòu)造函數(shù)的方式來(lái)創(chuàng)建對(duì)象完美的解決了。但是構(gòu)造器函數(shù)存在和工廠函數(shù)一樣的問(wèn)題:每次創(chuàng)建一個(gè)實(shí)例對(duì)象時(shí)都會(huì)在內(nèi)部新建一個(gè)中間對(duì)象,實(shí)例方法也會(huì)創(chuàng)建N次,這樣就存在不必要的內(nèi)層消耗。

原型與構(gòu)造函數(shù)組合

在上面Car構(gòu)造函數(shù)的栗子中,當(dāng)創(chuàng)建100個(gè) Car 的實(shí)例時(shí)內(nèi)部復(fù)制了100次 drive 函數(shù)。 雖然每個(gè) drive 函數(shù)的功能一樣,但是由于分別屬于不同的實(shí)例就每次都分配獨(dú)立的內(nèi)存空間。

相同的功能函數(shù)怎么忍受得了重復(fù)創(chuàng)建。回憶之前我們?cè)?b>原型一節(jié)講到的,每個(gè)函數(shù)存在prototype 屬性,通過(guò)該屬性指向自己的原型對(duì)象。那我們可以在函數(shù)的原型上做文章,將實(shí)例公共的屬性和方法掛載在原型上。實(shí)例通過(guò)__ptoto__屬性指向了構(gòu)造函數(shù)的原型,從而讓構(gòu)造函數(shù)的原型對(duì)象在各個(gè)實(shí)例的原型鏈上,于是我們通過(guò)構(gòu)造函數(shù)的原型來(lái)實(shí)現(xiàn)公有屬性和方法的封裝,且只會(huì)創(chuàng)建一次。

還是上面 Car的栗子:

function Car(make, model, year) {
    this.make = make
    this.model = model
    this.year = year
}
Car.prototype.drive = function (name) {
    console.log(`${name} drives the ${this.model} ${this.make}`)
}

let mycar  = new Car( "Tesla", "Model X", 2018)
mycar.drive("小丸子")

上面的栗子也還可以寫(xiě)成這樣子:

function Car(make, model, year) {
    this.make = make
    this.model = model
    this.year = year
}

Car.prototype = {
    constructor: Car,
    drive: function () {
        console.log(`${name} drives the ${this.model} ${this.make}`)
    }
}

let mycar  = new Car( "Tesla", "Model X", 2018)
mycar.drive("小丸子")

兩種寫(xiě)法是等價(jià)的,需要注意的是后一種相當(dāng)于創(chuàng)建一個(gè)新對(duì)象并賦值給了構(gòu)造函數(shù)Car的原型,如果不將新原型的constructor重現(xiàn)指向構(gòu)造函數(shù),則會(huì)導(dǎo)致構(gòu)造函數(shù)Car的實(shí)例類(lèi)型判斷出錯(cuò)(instanceof Car 為 false).

不同的實(shí)現(xiàn)方法都有各自的使用場(chǎng)景。同時(shí)對(duì)象的實(shí)現(xiàn)方式又與數(shù)據(jù)維度以及另外一個(gè)話題 設(shè)計(jì)模式有關(guān)。我們使用原型與構(gòu)造函數(shù)組合模式就能夠解決很多問(wèn)題。

關(guān)于 javascript 的各種模式可以參考:

Javascript設(shè)計(jì)模式

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

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

相關(guān)文章

  • 講清楚之 javascript 參數(shù)傳值

    摘要:講清楚之參數(shù)傳值參數(shù)傳值是指函數(shù)調(diào)用時(shí),給函數(shù)傳遞配置或運(yùn)行參數(shù)的行為,包括通過(guò)進(jìn)行傳值。所以對(duì)的賦值會(huì)改變上下文棧中標(biāo)識(shí)符保存的具體值此時(shí)如果使用的是按引用傳遞,則變量所指向的對(duì)象因該也被賦值為。 講清楚之 javascript 參數(shù)傳值 參數(shù)傳值是指函數(shù)調(diào)用時(shí),給函數(shù)傳遞配置或運(yùn)行參數(shù)的行為,包括通過(guò)call、apply 進(jìn)行傳值。 在實(shí)際開(kāi)發(fā)中,我們總結(jié)javascript參數(shù)傳...

    itvincent 評(píng)論0 收藏0
  • 講清楚之執(zhí)行上下文

    摘要:棧底為全局上下文,棧頂為當(dāng)前正在執(zhí)行的上下文。位于棧頂?shù)纳舷挛膱?zhí)行完畢后會(huì)自動(dòng)出棧,依次向下直至所有上下文運(yùn)行完畢,最后瀏覽器關(guān)閉時(shí)全局上下文被銷(xiāo)毀。 講清楚之執(zhí)行上下文 標(biāo)簽 : javascript 什么是執(zhí)行上下文? 當(dāng) JavaScript 代碼執(zhí)行一段可執(zhí)行代碼時(shí),會(huì)創(chuàng)建對(duì)應(yīng)的上下文(execution context)并將該上下文壓入上下文棧(context stack...

    3fuyu 評(píng)論0 收藏0
  • 講清楚之 javascript原形

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

    高勝山 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

boredream

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<