JS 的 new 做了那些事情

JS中的new運算符,從一個自定義對象類型或者包含constructor構建函數的內建對象類型中實例化一個對象。JS中已經“萬物皆對象”。為什么還要存在實例化的操作呢?

首先,先看一段代碼:
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. 首先定義了一個函數對象Animal。作為函數對象,Animal擁有原型對象prototype,prototype中擁有一個constructor函數,指向Animal函數自身。
  2. 給Animal函數對象添加了一個color屬性,賦值為black
  3. 給Animal函數對象添加了一個say方法,該方法,讀取當前調用的this對象中的name屬性,打印字符串。
  4. 給Ainimal的原型對象添加一個say的方法,該方法,讀取當前調用的this對象中的name屬性,打印字符串。
  5. 通過new,從Animal函數對象中創建一個實例,將其賦值為變量cat
  6. 打印新建cat對象中的兩個屬性,name和height,分別打印為catundefined
  7. 調用cat對象的say方法,將cat對象作為this傳遞到Animal.prototype.say函數中,并執行打印輸出
  8. 打印Animal函數對象中的兩個屬性,name和color,分別打印為Animalblack。其中的name屬性是從Function.prototype上繼承而來,color是Animal函數對象的自有屬性。
  9. 調用Animal函數對象的say方法,將Animal函數對象作為this傳遞到Animal.say函數中,并執行打印輸出

關于Animal,以及Animal的say,name屬性的調用,都可以從函數對象的原型鏈的繼承上得到解釋。它的原型鏈是

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

當JS中使用new操作符 添加到一個函數對象的前面并執行調用的時候。函數對象起到了一個自定義對象的constructor,也既構建函數的作用。
JS的new本身是一個“語法糖”,當JS解釋器碰到new的時候,它會按照下面的偽代碼執行:

// 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
})()

將其轉成規則,則new所起到的作用流程如下:

  1. 首先憑空創建一個空對象obj
  2. 把 obj 的proto 指向構造函數 Animal 的原型對象 prototype,此時便建立了 obj 對象的原型鏈:obj->Animal.prototype->Object.prototype->null
  3. 在 obj 對象的執行環境調用 Animal 函數并傳遞參數 “ cat ” 。 相當于 var result = obj.Animal("cat")。這句話,將this指向新創建的obj對象。并執行構建函數Animal。當這句執行完之后,obj 便產生了屬性 name 并賦值為 "cat"。關于 call 的用法請參考:深入理解 call、apply 和 bind
  4. 考察第 3 步的返回值,如果無返回值 或者 返回一個非對象值,則將 obj 作為新對象返回;否則會將 result 作為新對象返回。

此時cat的原型鏈是

cat -> Animal.prototype -> Object.prototype -> null
為什么要使用new來創建對象呢?

new的出現,讓JS擁有了對象的繼承能力,從例子中看到,通過new,成功在cat和Animal之間建立了繼承的關系。cat可以調用Animal的原型對象上的方法。

通過 new 創建的 對象 和 構造函數 之間建立了一條原型鏈,原型鏈的建立,讓原本孤立的對象有了依賴關系和繼承能力,讓JavaScript 對象能以更合適的方式來映射真實世界里的對象,這是面向對象的本質

測試下
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(): 調用Foo函數對象的getName方法,此時打印 2
  2. getName(); 調用全局的getName方法。JS代碼執行分為兩個階段,具體參考執行上下文的文章,首先在代碼為執行前,getName指的是打印 5 的函數聲明,但在執行到此時的時候,上面 getName 全局變量的執行,將getName的賦值指向了 打印4 的函數。因此此時打印 4
  3. Foo().getName(); 將Foo函數執行后返回的對象作為this,并調用該this中包含的getName方法。首先,Foo()的調用是在全局作用域,因此return this 等價于 return window。Foo() == window。此時Foo().getName()變成了this.getName()。但此時并不等價于第二條,因為在Foo()執行的過程中,在Foo函數內部,對全局變量getName進行了重新賦值,此時全局函數getName打印輸出 1
  4. getName(); 此時的結果和上一條打印結果輸出相同,打印輸出 1
  5. new Foo.getName(); 此時出現了new操作符,它將后面的函數Foo.getName作為了構建函數,創建了一個新的實例,在創建的過程中,會執行Foo.getName函數,因此此時輸出 2
  6. new Foo().getName(); 此時出現了new操作符,根據就近原則,等價于(new Foo()).getName()。即先創建了Foo()對象的一個實例 obj = new Foo()。此時return的this等同于新建實例對象obj。接下來的調用變成了obj.getName,也既Foo.prototype.getName。打印輸出 3
  7. new new Foo().getName(); 首先出現了兩次new操作符,而每次new操作符,都需要跟隨一個構建函數的調用。在表達式中一共有兩次調用(函數的調用通過()實現)。因此按照就近原則,表達式等價于 new (new Foo()).getName()。new Foo(),根據new的執行原則,返回新建實例obj。此時等價于new obj.getName()。和第6條一樣。因此此時,打印輸出 3
最后打印順序為 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

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容