JS 的 new 做了那些事情

JS中的new運(yùn)算符,從一個(gè)自定義對(duì)象類型或者包含constructor構(gòu)建函數(shù)的內(nèi)建對(duì)象類型中實(shí)例化一個(gè)對(duì)象虑瀑。JS中已經(jīng)“萬物皆對(duì)象”情屹。為什么還要存在實(shí)例化的操作呢?

首先嚷兔,先看一段代碼:
function Animal(name) {
  this.name = name
}
Animal.color = 'black'
Animal.say = function () {
  console.log('it is an ' + this.name)
}
Animal.prototype.say = function () {
  console.log('i am a ' + this.name)
}

var cat = new Animal('cat')
console.log(
  cat.name,  // cat
  cat.height  // undefined
)
cat.say()  // i am a cat
console.log(
  Animal.name, //Animal
  Animal.color //black
);
Animal.say(); //it is an Animal
  1. 首先定義了一個(gè)函數(shù)對(duì)象Animal。作為函數(shù)對(duì)象榨崩,Animal擁有原型對(duì)象prototype谴垫,prototype中擁有一個(gè)constructor函數(shù),指向Animal函數(shù)自身母蛛。
  2. 給Animal函數(shù)對(duì)象添加了一個(gè)color屬性翩剪,賦值為black
  3. 給Animal函數(shù)對(duì)象添加了一個(gè)say方法,該方法彩郊,讀取當(dāng)前調(diào)用的this對(duì)象中的name屬性前弯,打印字符串。
  4. 給Ainimal的原型對(duì)象添加一個(gè)say的方法秫逝,該方法恕出,讀取當(dāng)前調(diào)用的this對(duì)象中的name屬性,打印字符串违帆。
  5. 通過new浙巫,從Animal函數(shù)對(duì)象中創(chuàng)建一個(gè)實(shí)例,將其賦值為變量cat
  6. 打印新建cat對(duì)象中的兩個(gè)屬性刷后,name和height的畴,分別打印為catundefined
  7. 調(diào)用cat對(duì)象的say方法,將cat對(duì)象作為this傳遞到Animal.prototype.say函數(shù)中尝胆,并執(zhí)行打印輸出
  8. 打印Animal函數(shù)對(duì)象中的兩個(gè)屬性丧裁,name和color,分別打印為Animalblack含衔。其中的name屬性是從Function.prototype上繼承而來煎娇,color是Animal函數(shù)對(duì)象的自有屬性。
  9. 調(diào)用Animal函數(shù)對(duì)象的say方法贪染,將Animal函數(shù)對(duì)象作為this傳遞到Animal.say函數(shù)中缓呛,并執(zhí)行打印輸出

關(guān)于Animal,以及Animal的say杭隙,name屬性的調(diào)用哟绊,都可以從函數(shù)對(duì)象的原型鏈的繼承上得到解釋。它的原型鏈?zhǔn)?/p>

Animal->Function.prototype->Object.prototype->null
我們重點(diǎn)關(guān)注下
var cat = new Animal('cat')

當(dāng)JS中使用new操作符 添加到一個(gè)函數(shù)對(duì)象的前面并執(zhí)行調(diào)用的時(shí)候寺渗。函數(shù)對(duì)象起到了一個(gè)自定義對(duì)象的constructor匿情,也既構(gòu)建函數(shù)的作用。
JS的new本身是一個(gè)“語(yǔ)法糖”信殊,當(dāng)JS解釋器碰到new的時(shí)候炬称,它會(huì)按照下面的偽代碼執(zhí)行:

// var cat = new Animal('cat')
var cat = (function () {
  let obj = {}
  obj.__proto__ = Animal.prototype
  let result = Animal.call(obj, 'cat')
  return (typeof result == 'object') ? result : obj
})()

將其轉(zhuǎn)成規(guī)則,則new所起到的作用流程如下:

  1. 首先憑空創(chuàng)建一個(gè)空對(duì)象obj
  2. 把 obj 的proto 指向構(gòu)造函數(shù) Animal 的原型對(duì)象 prototype涡拘,此時(shí)便建立了 obj 對(duì)象的原型鏈:obj->Animal.prototype->Object.prototype->null
  3. 在 obj 對(duì)象的執(zhí)行環(huán)境調(diào)用 Animal 函數(shù)并傳遞參數(shù) “ cat ” 玲躯。 相當(dāng)于 var result = obj.Animal("cat")。這句話鳄乏,將this指向新創(chuàng)建的obj對(duì)象跷车。并執(zhí)行構(gòu)建函數(shù)Animal。當(dāng)這句執(zhí)行完之后橱野,obj 便產(chǎn)生了屬性 name 并賦值為 "cat"朽缴。關(guān)于 call 的用法請(qǐng)參考:深入理解 call、apply 和 bind
  4. 考察第 3 步的返回值水援,如果無返回值 或者 返回一個(gè)非對(duì)象值密强,則將 obj 作為新對(duì)象返回;否則會(huì)將 result 作為新對(duì)象返回蜗元。

此時(shí)cat的原型鏈?zhǔn)?/p>

cat -> Animal.prototype -> Object.prototype -> null
為什么要使用new來創(chuàng)建對(duì)象呢或渤?

new的出現(xiàn),讓JS擁有了對(duì)象的繼承能力奕扣,從例子中看到薪鹦,通過new,成功在cat和Animal之間建立了繼承的關(guān)系惯豆。cat可以調(diào)用Animal的原型對(duì)象上的方法池磁。

通過 new 創(chuàng)建的 對(duì)象 和 構(gòu)造函數(shù) 之間建立了一條原型鏈,原型鏈的建立循帐,讓原本孤立的對(duì)象有了依賴關(guān)系和繼承能力框仔,讓JavaScript 對(duì)象能以更合適的方式來映射真實(shí)世界里的對(duì)象,這是面向?qū)ο蟮谋举|(zhì)拄养。

測(cè)試下
function Foo(){
    getName = function(){
        console.log(1)
    }
    return this;
}
Foo.getName = function(){
    console.log(2)
}
Foo.prototype.getName = function(){
    console.log(3)
}
var getName = function(){
    console.log(4)
}
function getName(){
    console.log(5)
}
// ouput:
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
  1. Foo.getName(): 調(diào)用Foo函數(shù)對(duì)象的getName方法离斩,此時(shí)打印 2
  2. getName(); 調(diào)用全局的getName方法。JS代碼執(zhí)行分為兩個(gè)階段瘪匿,具體參考執(zhí)行上下文的文章跛梗,首先在代碼為執(zhí)行前,getName指的是打印 5 的函數(shù)聲明棋弥,但在執(zhí)行到此時(shí)的時(shí)候核偿,上面 getName 全局變量的執(zhí)行,將getName的賦值指向了 打印4 的函數(shù)顽染。因此此時(shí)打印 4
  3. Foo().getName(); 將Foo函數(shù)執(zhí)行后返回的對(duì)象作為this漾岳,并調(diào)用該this中包含的getName方法轰绵。首先,F(xiàn)oo()的調(diào)用是在全局作用域尼荆,因此return this 等價(jià)于 return window左腔。Foo() == window。此時(shí)Foo().getName()變成了this.getName()捅儒。但此時(shí)并不等價(jià)于第二條液样,因?yàn)樵贔oo()執(zhí)行的過程中,在Foo函數(shù)內(nèi)部巧还,對(duì)全局變量getName進(jìn)行了重新賦值鞭莽,此時(shí)全局函數(shù)getName打印輸出 1
  4. getName(); 此時(shí)的結(jié)果和上一條打印結(jié)果輸出相同,打印輸出 1
  5. new Foo.getName(); 此時(shí)出現(xiàn)了new操作符麸祷,它將后面的函數(shù)Foo.getName作為了構(gòu)建函數(shù)澎怒,創(chuàng)建了一個(gè)新的實(shí)例,在創(chuàng)建的過程中摇锋,會(huì)執(zhí)行Foo.getName函數(shù)丹拯,因此此時(shí)輸出 2
  6. new Foo().getName(); 此時(shí)出現(xiàn)了new操作符,根據(jù)就近原則荸恕,等價(jià)于(new Foo()).getName()乖酬。即先創(chuàng)建了Foo()對(duì)象的一個(gè)實(shí)例 obj = new Foo()。此時(shí)return的this等同于新建實(shí)例對(duì)象obj融求。接下來的調(diào)用變成了obj.getName咬像,也既Foo.prototype.getName。打印輸出 3
  7. new new Foo().getName(); 首先出現(xiàn)了兩次new操作符生宛,而每次new操作符县昂,都需要跟隨一個(gè)構(gòu)建函數(shù)的調(diào)用。在表達(dá)式中一共有兩次調(diào)用(函數(shù)的調(diào)用通過()實(shí)現(xiàn))陷舅。因此按照就近原則倒彰,表達(dá)式等價(jià)于 new (new Foo()).getName()。new Foo()莱睁,根據(jù)new的執(zhí)行原則待讳,返回新建實(shí)例obj。此時(shí)等價(jià)于new obj.getName()仰剿。和第6條一樣创淡。因此此時(shí),打印輸出 3
最后打印順序?yàn)?2,4,1,1,2,3,3

參考鏈接:
https://zhuanlan.zhihu.com/p/23987456
https://stackoverflow.com/questions/1646698/what-is-the-new-keyword-in-javascript
https://www.cnblogs.com/onepixel/p/5043523.html
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末南吮,一起剝皮案震驚了整個(gè)濱河市琳彩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖露乏,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碧浊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡瘟仿,警方通過查閱死者的電腦和手機(jī)辉词,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猾骡,“玉大人,你說我怎么就攤上這事敷搪⌒讼耄” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵赡勘,是天一觀的道長(zhǎng)嫂便。 經(jīng)常有香客問我,道長(zhǎng)闸与,這世上最難降的妖魔是什么毙替? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮践樱,結(jié)果婚禮上厂画,老公的妹妹穿的比我還像新娘。我一直安慰自己拷邢,他們只是感情好袱院,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瞭稼,像睡著了一般忽洛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上环肘,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天欲虚,我揣著相機(jī)與錄音,去河邊找鬼悔雹。 笑死复哆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的荠商。 我是一名探鬼主播寂恬,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼莱没!你這毒婦竟也來了初肉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤饰躲,失蹤者是張志新(化名)和其女友劉穎牙咏,沒想到半個(gè)月后臼隔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妄壶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年摔握,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丁寄。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡氨淌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伊磺,到底是詐尸還是另有隱情盛正,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布屑埋,位于F島的核電站豪筝,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏摘能。R本人自食惡果不足惜续崖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望团搞。 院中可真熱鬧严望,春花似錦、人聲如沸逻恐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)梢莽。三九已至萧豆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昏名,已是汗流浹背涮雷。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留轻局,地道東北人洪鸭。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像仑扑,于是被迫代替她去往敵國(guó)和親览爵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

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