iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 7
0
Modern Web

從技術文章深入學習 JavaScript系列 第 7

Day 07 [原型鍊03] JavaScript中,new操作符的工作原理是什么

文章選取自

參考源:該問答串的所有人

連接:https://www.zhihu.com/question/36440948

來源:知乎

開始

由MDN的解釋會經過四步

  1. 創建一個空的簡單JavaScript對象(即 {});
  2. 鏈接該對象(即設置該對象的構造函數)到另一個對象;
  3. 將步驟1新創建的對像作為 this 的上下文;
  4. 如果該函數沒有返回對象,則返回 this。

看起來有夠文謅謅,我們來用自己的代碼描寫看看

function Foo() {}
var instance = new Foo()

恩就是創建實例,那裡面到底發生甚麼呢

到底發生了甚麼

階段一 先來模擬前面兩點

function Foo() {}

let realInstance = new Foo()
console.log(realInstance);

function newInstance(constructor) {
  let obj = {}
  obj.__proto__ = constructor.prototype
  return obj
}
let testInstance = newInstance(Foo)
console.log(testInstance);

看一下打印的樣子

https://ithelp.ithome.com.tw/upload/images/20200917/20124350s6J71fBHK8.png

感覺沒啥毛病,可是如果我們這樣

function Foo(obj) {
  this.name = obj.name
  this.age = obj.age
}

let realInstance = new Foo({
  name: 'Mike',
  age: 5
})
console.log(realInstance);

function newInstance(constructor) {
  let obj = {}
  obj.__proto__ = constructor.prototype
  return obj
}
let testInstance = newInstance(Foo)
console.log(testInstance);

https://ithelp.ithome.com.tw/upload/images/20200917/20124350HBf8ez0p4P.png

所以我們現在要解決我們創造的new沒辦法賦予實例特性

階段二: 所以才要綁定this

為啥要綁定this呢?

因為函數在定義時使用到了this,我們必須把this綁定給obj

其實要解決就是執行

function Foo(obj) {
  this.name = obj.name
  this.age = obj.age
}

let realInstance = new Foo({
  name: 'Mike',
  age: 5
})
console.log(realInstance);

function newInstance(constructor, argInFoo) {
  let obj = {}
  obj.__proto__ = constructor.prototype
  constructor.call(obj, argInFoo)
  return obj
}
let testInstance = newInstance(Foo, {
  name: 'Mike',
  age: 5
})
console.log(testInstance);

https://ithelp.ithome.com.tw/upload/images/20200917/20124350nfKc73uYG6.png

現在看起來又更完善了!!但是我們如果去檢查instanceof 的話呢?

console.log(testInstance instanceof Foo);

https://ithelp.ithome.com.tw/upload/images/20200917/20124350ZJmCCJU6xP.png

可以完全沒毛病!! 所以我們成功模擬了new!!!

優化

先來看看我們的代碼

function newInstance(constructor, argObj) {
  let obj = {}
  obj.__proto__ = constructor.prototype
  constructor.call(obj, argObj)
  return obj
}

我們可以發現我們是假設創建實例的構造函數只能傳入一個obj 這會導致我們創建實例沒那麼自由,比方說這樣

// 定義了另一個構造函數Bar
function Bar(name, age) {
  this.name = name
  this.age = age
}
let realInstance = new Bar('Mike', 5)
console.log(realInstance);

function newInstance(constructor, argObj) {
  let obj = {}
  obj.__proto__ = constructor.prototype
  constructor.call(obj, argObj)
  return obj
}
let testInstance = newInstance(Bar, 'Mike', 5)
console.log(testInstance);

https://ithelp.ithome.com.tw/upload/images/20200917/20124350bA31PY7x38.png

明明我們正確使用,但創建了卻和真的實例不一樣

所以我們的目標是甚麼?

不管Foo定義長怎樣,只要我們正確傳遞參數就可以創建實例

因此我們要這樣去定義

// 這裡用到了rest參數,讓所有剩餘參數都傳入
function newInstance(constructor, ...resArg) { 
  let obj = {}
  obj.__proto__ = constructor.prototype
  // 因為resArg會被當成數組,所以我們改用apply會方便超多
  constructor.apply(obj, argObj)
  return obj
}

https://ithelp.ithome.com.tw/upload/images/20200917/20124350SVFwQM7rPu.png

好了這樣我們真的完成了全部!!


上一篇
Day 06 [this 02] ES6箭頭函數、箭頭函數與普通函數的區別
下一篇
Day 08 [原型鍊04] JavaScript常用八種繼承方案
系列文
從技術文章深入學習 JavaScript29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言