首頁 > 軟體

JavaScript建立物件的幾種方式及關於this指向問題

2022-07-22 10:00:44

工廠模式

工廠模式一般用於抽象建立特定物件的過程,是按照特定介面建立物件的方式。

function createPerson(name, age) {
    let o = {};
    o.name = name;
    0.age = age;
    o.study = function() {
        console.log(`${this.name} is studying`)
    }
    return o
}
const p1 = createPerson('張三', 18)
const p2 = createPerson('李四', 20)
p1.study() // 張三 is studying

值得一提的是,如果在 createPerson 函數中,o.study 改為箭頭函數,那麼列印的 this 會發生改變,這是因為箭頭函數發生了 this 指向的問題。

工廠模式的優點: 在 createPerson 中,可以用不同的引數多次呼叫工廠函數,每次都會返回包含特定引數和一個方法的物件。可以解決建立多個類似物件的問題。

工廠模式的缺點:

  • 1. 沒有解決物件標識問題。
  • 2. 每次建立物件,都會建立一個公共的方法,而方法也是物件,所以造成記憶體浪費。

建構函式模式

在 JS 中, 建構函式是用於建立特定型別物件的。像 ObjectArray 這樣的原生構造物件,執行時,可以直接在執行環境中執行。當然也可以自定義建構函式,以函數的形式為自己的物件型別定義屬性和方法。

function Student(name, age) {
    this.name = name;
    this.age = age;
    this.study = function() {
        console.log(`${this.name} is studying`)
    }
}
const p1 = new Student('張三', 18)
const p2 = new Student('李四', 20)

通過上面的程式碼,可以實現和工廠模式一樣的效果。但是值得注意的是: 如果這裡的 study 函數變為箭頭函數, this 的指向是不會發生變化的。

如同上面的程式碼,如果呼叫建構函式,就必須使用 new 關鍵字,在這個過程中,建構函式會做四件事情:

  • 在記憶體中創造一個新的物件
  • 將這個新的物件的隱式原型 __proto__ 賦值為該建構函式的顯示原型 prototype
  • 建構函式的內部的 this 被賦值為這個新的物件(給新物件新增新的屬性)
  • 執行建構函式中的程式碼,並且判斷有無物件返回值,如果存在,則返回這個返回值,否則返回這個新的物件
  • 建構函式的優缺點: 首先,建構函式解決了物件的標識問題,但是如果建構函式中存在著函數,那麼每次建立一個範例,就相當於函數也被重新創造了一遍,因為在JS中函數也是相當於物件,造成了對空間的浪費。

關於 this

在標準函數中, this 參照的是把函數當成方法呼叫的上下文物件,這個時候常稱其為 this。

我們在上文說過,工廠模式和建構函式模式,其內部如果出現箭頭函數,那麼 this 的指向會出現不同,這是因為,箭頭函數中不存在 this , 箭頭函數中的 this會保留定義該函數的上下文this 到底參照哪個物件必須到函數呼叫時,才會確定, 在工廠模式時, 雖然通過p1.study(),進行呼叫,但是由於箭頭函數內部沒有 this,所以向上查詢,找到function createPerson 函數, 繫結 window, 而在建構函式模式中this 賦值給了這個新的物件,相當於箭頭函數的this, 就是這個新的物件,也就是之後的範例。

可能,通過上面的描述,很多同學仍然對下面箭頭函數中的this,會保留定義該函數的上下文不太理解,我列舉一個例子。

// 工廠模式
function createPerson(name, age) {
    let o = {};
    o.name = name;
    o.age = age;
    o.study = () => {
        console.log(`${this.name} is studying`)
    }
    return o
}

const obj = {
    name: '張三'
}

const p1 = createPerson.call(obj, '李四', 20) // createPerson 繫結 obj, 那麼箭頭函數也會繫結 obj
p1.study() // 所以這裡的輸出是 張三 is studying,而不是 undefined

原型模式

每個函數都會建立一個 prototype 屬性,這個屬性是一個物件,包含應該由特定參照型別的範例共用的屬性和方法。實際上,這個物件就是通過呼叫建構函式建立的物件的原型。使用原型物件的好處是,在它上面定義的屬性和方法可以被物件範例共用。原來在建構函式中直接賦值給物件範例的值,可以直接賦值給他們的原型。

function Student() {}
Student.prototype.name = '張三';
Student.prototype.age = 20;
Student.prototype.friends = ['ls', 'ww'];
Student.prototype.study = function() {
    console.log(`${this.name} is studying`)
}

const p1 = new Student()
const p2 = new Student()

通過上面建立的p1, p2,共用了Student原型上的屬性,相當於創造了兩個擁有相同屬性的不同物件。同理,如果上面的 study 改為了箭頭函數,大家應該也能知道 this 繫結的是誰了吧。(Tip: new 和 顯示繫結不可以同時使用)

原型模式的優缺點 : 首先原型模式解決了上兩種模式造成的空間浪費問題,但是,其屬性都是共用的,所以一般來說,原型模式不會單獨去使用。


IT145.com E-mail:sddin#qq.com