new 原理及模擬實(shí)現(xiàn)

定義

new 運(yùn)算符創(chuàng)建一個(gè)用戶(hù)定義的對(duì)象類(lèi)型的實(shí)例或具有構(gòu)造函數(shù)的內(nèi)置對(duì)象的實(shí)例。 ——(來(lái)自于MDN)

舉個(gè)栗子

function Car(color) {
    this.color = color;
}
Car.prototype.start = function() {
    console.log(this.color + " car start");
}
var car = new Car("black");
car.color; // 訪(fǎng)問(wèn)構(gòu)造函數(shù)里的屬性
// black
car.start(); // 訪(fǎng)問(wèn)原型里的屬性
// black car start

可以看出 new 創(chuàng)建的實(shí)例有以下 2 個(gè)特性

  • 訪(fǎng)問(wèn)到構(gòu)造函數(shù)里的屬性
  • 訪(fǎng)問(wèn)到原型里的屬性

注意點(diǎn)
ES6新增 symbol 類(lèi)型有勾,不可以使用 new Symbol(),因?yàn)?symbol 是基本數(shù)據(jù)類(lèi)型蛙吏,每個(gè)從Symbol()返回的 symbol 值都是唯一的涉瘾。

Number("123"); // 123
String(123); // "123"
Boolean(123); // true
Symbol(123); // Symbol(123)

new Number("123"); // Number {123}
new String(123); // String {"123"}
new Boolean(true); // Boolean {true}
new Symbol(123); // Symbol is not a constructor

模擬實(shí)現(xiàn)

當(dāng)代碼 new Foo(...) 執(zhí)行時(shí)要出,會(huì)發(fā)生以下事情:

  • 一個(gè)繼承自 Foo.prototype 的新對(duì)象被創(chuàng)建捉偏。
  • 使用指定的參數(shù)調(diào)用構(gòu)造函數(shù) Foo 倒得,并將 this 綁定到新創(chuàng)建的對(duì)象。new Foo 等同于 new Foo()夭禽,也就是沒(méi)有指定參數(shù)列表霞掺,F(xiàn)oo 不帶任何參數(shù)調(diào)用的情況。
  • 由構(gòu)造函數(shù)返回的對(duì)象就是 new 表達(dá)式的結(jié)果讹躯。如果構(gòu)造函數(shù)沒(méi)有顯式返回一個(gè)對(duì)象菩彬,則使用步驟1創(chuàng)建的對(duì)象。

模擬實(shí)現(xiàn)第一步

new 是關(guān)鍵詞潮梯,不可以直接覆蓋挤巡。這里使用 create 來(lái)模擬實(shí)現(xiàn) new 的效果。

new 返回一個(gè)新對(duì)象酷麦,通過(guò) obj.proto = Con.prototype 繼承構(gòu)造函數(shù)的原型,同時(shí)通過(guò) Con.apply(obj, arguments)調(diào)用父構(gòu)造函數(shù)實(shí)現(xiàn)繼承喉恋,獲取構(gòu)造函數(shù)上的屬性(【進(jìn)階3-3期】)沃饶。實(shí)現(xiàn)代碼如下

// 第一版
function create() {
    // 創(chuàng)建一個(gè)空的對(duì)象
    var obj = new Object(),
    // 獲得構(gòu)造函數(shù),arguments中去除第一個(gè)參數(shù)
    Con = [].shift.call(arguments);
    // 鏈接到原型轻黑,obj 可以訪(fǎng)問(wèn)到構(gòu)造函數(shù)原型中的屬性
    obj.__proto__ = Con.prototype;
    // 綁定 this 實(shí)現(xiàn)繼承糊肤,obj 可以訪(fǎng)問(wèn)到構(gòu)造函數(shù)中的屬性
    Con.apply(obj, arguments);
    // 返回對(duì)象
    return obj;
};

測(cè)試一下

// 測(cè)試用例
function Car(color) {
    this.color = color;
}
Car.prototype.start = function() {
    console.log(this.color + " car start");
}
var car = create(Car, "black");
car.color;
// black
car.start();
// black car start

完美!不熟悉 apply / call 的搜索查看:深度解析 call 和 apply 原理氓鄙、使用場(chǎng)景及實(shí)現(xiàn)馆揉。不熟悉繼承的查看:JavaScript常用八種繼承方案

模擬實(shí)現(xiàn)第二步

上面的代碼已經(jīng)實(shí)現(xiàn)了 80%,現(xiàn)在繼續(xù)優(yōu)化抖拦。構(gòu)造函數(shù)返回值有如下三種情況:

  • 返回一個(gè)對(duì)象
  • 沒(méi)有 return升酣,即返回 undefined
  • 返回undefined 以外的基本類(lèi)型
情況1:返回一個(gè)對(duì)象
function Car(color, name) {
    this.color = color;
    return {
        name: name
    }
}
var car = new Car("black", "BMW");
car.color;
// undefined
car.name;
// "BMW"

實(shí)例 car 中只能訪(fǎng)問(wèn)到返回對(duì)象中的屬性。

情況2:沒(méi)有 return态罪,即返回 undefined
function Car(color, name) {
    this.color = color;
}

var car = new Car("black", "BMW");
car.color;
// black

car.name;
// undefined

實(shí)例 car 中只能訪(fǎng)問(wèn)到構(gòu)造函數(shù)中的屬性噩茄,和情況1完全相反。

情況3:返回undefined 以外的基本類(lèi)型
function Car(color, name) {
    this.color = color;
    return "new car";
}

var car = new Car("black", "BMW");
car.color;
// black

car.name;
// undefined

實(shí)例 car 中只能訪(fǎng)問(wèn)到構(gòu)造函數(shù)中的屬性复颈,和情況1完全相反绩聘,結(jié)果相當(dāng)于沒(méi)有返回值。

所以需要判斷下返回的值是不是一個(gè)對(duì)象,如果是對(duì)象則返回這個(gè)對(duì)象凿菩,不然返回新創(chuàng)建的 obj對(duì)象机杜。所以實(shí)現(xiàn)代碼如下:

// 第二版
function create() {
    // 創(chuàng)建一個(gè)空的對(duì)象
    var obj = new Object(),
    // 獲得構(gòu)造函數(shù),arguments中去除第一個(gè)參數(shù)
    Con = [].shift.call(arguments);
    // 鏈接到原型衅谷,obj 可以訪(fǎng)問(wèn)到構(gòu)造函數(shù)原型中的屬性
    obj.__proto__ = Con.prototype;
    // 綁定 this 實(shí)現(xiàn)繼承椒拗,obj 可以訪(fǎng)問(wèn)到構(gòu)造函數(shù)中的屬性
    var ret = Con.apply(obj, arguments);
    // 優(yōu)先返回構(gòu)造函數(shù)返回的對(duì)象
    return typeof ret === 'object' ? ret : obj;
};

思考題

問(wèn)題:用 JS 實(shí)現(xiàn)一個(gè)無(wú)限累加的函數(shù) add,示例如下:

add(1); // 1
add(1)(2);  // 3
add(1)(2)(3)会喝; // 6
add(1)(2)(3)(4)陡叠; // 10 
// 以此類(lèi)推

實(shí)現(xiàn):

function add(a) {
    function sum(b) { // 使用閉包
        a = a + b; // 累加
        return sum;
    }
    sum.toString = function() { // 重寫(xiě)toString()方法
        return a;
    }
    return sum; // 返回一個(gè)函數(shù)
}
add(1); // 1
add(1)(2);  // 3
add(1)(2)(3); // 6
add(1)(2)(3)(4)肢执; // 10 

我們知道打印函數(shù)時(shí)會(huì)自動(dòng)調(diào)用 toString()方法枉阵,函數(shù) add(a) 返回一個(gè)閉包 sum(b),函數(shù) sum() 中累加計(jì)算 a = a + b预茄,只需要重寫(xiě)sum.toString()方法返回變量 a 就OK了兴溜。

參考

前端進(jìn)階系列——木易楊

看完文章,還有福利拿耻陕,往下看??????
感興趣的小伙伴可以在公號(hào)【grain先森】后臺(tái)回復(fù)【190313】獲取HTML5詳解拙徽、CSS3詳解和Vue詳解及實(shí)戰(zhàn)項(xiàng)目,可以轉(zhuǎn)發(fā)朋友圈和你的朋友分享哦诗宣。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末膘怕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子召庞,更是在濱河造成了極大的恐慌岛心,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件篮灼,死亡現(xiàn)場(chǎng)離奇詭異忘古,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)诅诱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)髓堪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人娘荡,你說(shuō)我怎么就攤上這事干旁。” “怎么了炮沐?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵疤孕,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我央拖,道長(zhǎng)祭阀,這世上最難降的妖魔是什么鹉戚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮专控,結(jié)果婚禮上抹凳,老公的妹妹穿的比我還像新娘。我一直安慰自己伦腐,他們只是感情好赢底,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著柏蘑,像睡著了一般幸冻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咳焚,一...
    開(kāi)封第一講書(shū)人閱讀 51,598評(píng)論 1 305
  • 那天洽损,我揣著相機(jī)與錄音,去河邊找鬼革半。 笑死碑定,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的又官。 我是一名探鬼主播延刘,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼六敬!你這毒婦竟也來(lái)了碘赖?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤外构,失蹤者是張志新(化名)和其女友劉穎普泡,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體典勇,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年叮趴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了割笙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡眯亦,死狀恐怖伤溉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情妻率,我是刑警寧澤乱顾,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站宫静,受9級(jí)特大地震影響走净,放射性物質(zhì)發(fā)生泄漏券时。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一伏伯、第九天 我趴在偏房一處隱蔽的房頂上張望橘洞。 院中可真熱鬧,春花似錦说搅、人聲如沸炸枣。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)适肠。三九已至,卻和暖如春候引,著一層夾襖步出監(jiān)牢的瞬間侯养,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工背伴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沸毁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓傻寂,卻偏偏與公主長(zhǎng)得像息尺,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子疾掰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 定義 new 運(yùn)算符創(chuàng)建一個(gè)用戶(hù)定義的對(duì)象類(lèi)型的實(shí)例或具有構(gòu)造函數(shù)的內(nèi)置對(duì)象的實(shí)例搂誉。 ——(來(lái)自于MDN) 舉個(gè)栗...
    程序員依揚(yáng)閱讀 907評(píng)論 0 2
  • 第3章 基本概念 3.1 語(yǔ)法 3.2 關(guān)鍵字和保留字 3.3 變量 3.4 數(shù)據(jù)類(lèi)型 5種簡(jiǎn)單數(shù)據(jù)類(lèi)型:Unde...
    RickCole閱讀 5,128評(píng)論 0 21
  • 本文為阮一峰大神的《ECMAScript 6 入門(mén)》的個(gè)人版提純! babel babel負(fù)責(zé)將JS高級(jí)語(yǔ)法轉(zhuǎn)義静檬,...
    Devildi已被占用閱讀 1,985評(píng)論 0 4
  • [TOC] 參考阮一峰的ECMAScript 6 入門(mén)參考深入淺出ES6 let和const let和const都...
    郭子web閱讀 1,781評(píng)論 0 1
  • 風(fēng) 有些涼炭懊,浸骨的冰。往日的溫柔己停止撫摸拂檩。小鳥(niǎo)嘰嘰侮腹,訴說(shuō)著往日的傳說(shuō),并協(xié)商著南迀的旅行稻励。云兒也有幾分成熟后的冷...
    惜王福利閱讀 610評(píng)論 6 18