前言
要實(shí)現(xiàn)手寫new
,關(guān)鍵在于先吃透它的本質(zhì)。new
作為 JavaScript 中創(chuàng)建對(duì)象的核心操作符,看似簡(jiǎn)單的語法背后,藏著一套嚴(yán)謹(jǐn)?shù)膱?zhí)行邏輯。只有先弄清楚它究竟是什么—— 是連接構(gòu)造函數(shù)與實(shí)例對(duì)象的橋梁?還是激活原型鏈關(guān)聯(lián)的開關(guān)?再明確它能完成哪些核心功能—— 是初始化對(duì)象屬性,還是建立繼承關(guān)系?最后拆解它的執(zhí)行過程中每一步都發(fā)生了什么—— 從創(chuàng)建空對(duì)象到綁定原型,從執(zhí)行構(gòu)造函數(shù)到返回實(shí)例…… 搞懂了這些,手寫new的思路才會(huì)清晰起來。接下來,我們一起來揭露這個(gè)東西
new 是什么?
new
是JavaScript中用于創(chuàng)建對(duì)象實(shí)例的關(guān)鍵操作符,它是面向?qū)ο缶幊讨袑?duì)象實(shí)例化的核心機(jī)制。
我們通常利用new
來實(shí)例化一個(gè)對(duì)象:
function Person(name, age) {
this.name = name;
this.age = age;
this.Talk = function () {
console.log('我是' + this.name);
}
}
const person1 = new Person('張三', 25);
console.log(person1);
person1.Talk();
我們可以看到利用new
產(chǎn)生的實(shí)例繼承了構(gòu)造函數(shù)的屬性和方法,并且成功實(shí)例化了一個(gè)對(duì)象。
new 的過程中發(fā)生了什么?
當(dāng)使用new
調(diào)用函數(shù)時(shí),JavaScript引擎會(huì)執(zhí)行以下步驟:
- 創(chuàng)建新對(duì)象:創(chuàng)建一個(gè)全新的空對(duì)象
{}
- 鏈接原型:將這個(gè)新對(duì)象的
[[Prototype]]
(即__proto__
)鏈接到構(gòu)造函數(shù)的prototype
對(duì)象 - 綁定this:將構(gòu)造函數(shù)中的
this
綁定到這個(gè)新對(duì)象 - 執(zhí)行構(gòu)造函數(shù):執(zhí)行構(gòu)造函數(shù)中的代碼(通常用于初始化對(duì)象)
- 返回對(duì)象:如果構(gòu)造函數(shù)沒有返回對(duì)象,則自動(dòng)返回這個(gè)新對(duì)象
當(dāng)然,對(duì)于這5條,1
,3
就不用說了,
1
是基礎(chǔ)步驟,我們要返回一個(gè)實(shí)例化對(duì)象,必須得初始化一個(gè),沒有米怎么蒸米飯呢?
3
的話,上一期的this
指向中有講過,傳送門??=>美麗的地方0^0~
2
,我們可以在瀏覽器中console.log(person1)
,查看它的__proto__
是否等于Person.prototype
4
,我們也可以隨便在構(gòu)造函數(shù)中加一句console.log('whatever u want')
,觀察它是否會(huì)執(zhí)行
5
,重點(diǎn)來咯!當(dāng)構(gòu)造函數(shù)顯式返回一個(gè)對(duì)象時(shí),new
操作符會(huì)忽略原本創(chuàng)建的新對(duì)象,直接返回構(gòu)造函數(shù)返回的這個(gè)對(duì)象。此時(shí)新對(duì)象的屬性初始化將被丟棄,只有返回對(duì)象的內(nèi)容會(huì)被保留。
構(gòu)造函數(shù)返回對(duì)象完整規(guī)則:
- 返回對(duì)象類型(包括數(shù)組、函數(shù)等)
→ 完全替代new
默認(rèn)創(chuàng)建的對(duì)象 - 返回原始值(數(shù)字、字符串等)
→ 被忽略,仍然返回new
創(chuàng)建的新對(duì)象 - 沒有return語句
→ 正常返回new
創(chuàng)建的新對(duì)象
示例:
function Case1() {
this.a = 1;
return { b: 2 };
}
console.log(new Case1());
function Case2() {
this.a = 1;
return 123;
}
console.log(new Case2());
function Case3() {
this.a = 1;
}
console.log(new Case3());
手寫new
okk,既然我們都知道了你new
出一個(gè)對(duì)象安慰自己沒有對(duì)象的事實(shí)時(shí)會(huì)發(fā)生的事情了,我們開始手寫一個(gè)new
吧!
記住這靈魂的舞步~ :
- 創(chuàng)建新對(duì)象:創(chuàng)建一個(gè)全新的空對(duì)象
{}
- 鏈接原型:將這個(gè)新對(duì)象的
[[Prototype]]
(即__proto__
)鏈接到構(gòu)造函數(shù)的prototype
對(duì)象 - 綁定this:將構(gòu)造函數(shù)中的
this
綁定到這個(gè)新對(duì)象 - 執(zhí)行構(gòu)造函數(shù):執(zhí)行構(gòu)造函數(shù)中的代碼(通常用于初始化對(duì)象)
- 返回對(duì)象:如果構(gòu)造函數(shù)沒有返回對(duì)象,則自動(dòng)返回這個(gè)新對(duì)象
ok,假設(shè)我們有一個(gè)構(gòu)造函數(shù)Person
,要利用我們的手寫new
創(chuàng)建一個(gè)實(shí)例,
那么首先我們要?jiǎng)?chuàng)建一個(gè)函數(shù),用來實(shí)現(xiàn)new
:
function Person(name, age) {
this.name = name;
this.age = age;
}
function myNew(){}
現(xiàn)在我們要實(shí)現(xiàn)創(chuàng)建一個(gè)新的空對(duì)象:
function myNew(){
var obj = {};
}
接下來,我們要確立原型,鏈接到構(gòu)造函數(shù),那么該怎么做呢?
既然我們要用到構(gòu)造函數(shù),那么我們應(yīng)該至少應(yīng)該拿到構(gòu)造函數(shù)吧,所以我們就要把構(gòu)造函數(shù)和所需要的形參傳遞進(jìn)來!
function myNew(constructor,...args){
var obj = new Object();
}
接下來我們開始綁定prototype:
function myNew(constructor,...args){
var obj = new Object();
obj.__proto__ = constructor.prototype;
}
Next,開始激情♂地綁定this
到新的實(shí)例對(duì)象上,那么怎么樣能夠?qū)?gòu)造函數(shù)的this
綁定到obj
到上面呢?上一期我們學(xué)習(xí)了顯式綁定,我們可以利用call
、apply
、bind
方法來將其綁定到理想目標(biāo)上:
function myNew(constructor,...args){
var obj = new Object();
obj.__proto__ = constructor.prototype;
constructor.bind(obj,...args);
}
這樣我們就成功了,下一步就是要執(zhí)行構(gòu)造函數(shù)了,我們可以用一個(gè)變量接受bind
的結(jié)果并執(zhí)行它:
function myNew(constructor,...args){
var obj = new Object();
obj.__proto__ = constructor.prototype;
var before = constructor.bind(obj,...args);
before();
}
最后利用一個(gè)變量接受結(jié)果,判斷構(gòu)造函數(shù)返回的結(jié)果是否符合要求,
如果不符合則返回新對(duì)象,
如果符合則返回構(gòu)造函數(shù)準(zhǔn)備好的結(jié)果:
function myNew(constructor,...args){
var obj = new Object();
obj.__proto__ = constructor.prototype;
var before = constructor.bind(obj,...args);
var res = before();
return typeof res === 'object' ? res || obj : obj;
}
OK,看到這里大家肯定也知道我們可以優(yōu)化一部分了。我們可以把創(chuàng)建對(duì)象到綁定prototype
以及 從綁定this
到判斷結(jié)果這一段優(yōu)化:
function myNew(constructor,...args){
const obj = Object.create(constructor.prototype);
const res = constructor.apply(obj, args);
return typeof res === 'object' ? res || obj : obj;
}
總結(jié)
想要手寫一個(gè)new
,就要深深記住它在創(chuàng)建過程中會(huì)發(fā)生的事情,所以要記住以下五點(diǎn)?。。。。?!
- 創(chuàng)建新對(duì)象:創(chuàng)建一個(gè)全新的空對(duì)象
{}
- 鏈接原型:將這個(gè)新對(duì)象的
[[Prototype]]
(即__proto__
)鏈接到構(gòu)造函數(shù)的prototype
對(duì)象 - 綁定this:將構(gòu)造函數(shù)中的
this
綁定到這個(gè)新對(duì)象 - 執(zhí)行構(gòu)造函數(shù):執(zhí)行構(gòu)造函數(shù)中的代碼(通常用于初始化對(duì)象)
- 返回對(duì)象:如果構(gòu)造函數(shù)沒有返回對(duì)象,則自動(dòng)返回這個(gè)新對(duì)象
轉(zhuǎn)自https://juejin.cn/post/7533521571568336938
該文章在 2025/8/5 9:22:58 編輯過