摘要:中經(jīng)常使用構(gòu)造函數(shù)創(chuàng)建對(duì)象通過操作符調(diào)用一個(gè)函數(shù),那在使用調(diào)用一個(gè)函數(shù)的時(shí)候到底發(fā)生了什么先看幾個(gè)例子,再解釋背后發(fā)生了什么。其中就是指構(gòu)造函數(shù)本身。
JavaScript 中經(jīng)常使用構(gòu)造函數(shù)創(chuàng)建對(duì)象(通過 new 操作符調(diào)用一個(gè)函數(shù)),那在使用 new 調(diào)用一個(gè)函數(shù)的時(shí)候到底發(fā)生了什么?先看幾個(gè)例子,再解釋背后發(fā)生了什么。
1)看三個(gè)例子 1.1 無 return 語(yǔ)句構(gòu)造函數(shù)最后沒有 return 語(yǔ)句,這也是使用構(gòu)造函數(shù)時(shí)默認(rèn)情況,最后會(huì)返回一個(gè)新對(duì)象,如下:
function Foo(age) { this.age = age; } var o = new Foo(111); console.log(o);
這是常見的使用構(gòu)造函數(shù)創(chuàng)建對(duì)象的過程,打印出來的是 {age: 111}。
1.2 return 對(duì)象類型數(shù)據(jù)構(gòu)造函數(shù)最后 return 對(duì)象類型數(shù)據(jù):
function Foo(age) { this.age = age; return { type: "我是顯式返回的" }; } var o = new Foo(222); console.log(o);
打印出來的是 {type: "我是顯式返回的"},也就是說,return 之前的工作都白做了,最后返回 return 后面的對(duì)象。
1.3 return 基本類型數(shù)據(jù)那是不是只要構(gòu)造函數(shù)體內(nèi)最后有 return,返回都是 return 后面的數(shù)據(jù)呢?
我們看下返回基本類型數(shù)據(jù)的情況:
function Foo(age) { this.age = age; return 1; } var o = new Foo(333); console.log(o);
打印出來的是 {age: 333},和沒有 return 時(shí)效果一樣。跟預(yù)期不一樣,背后你原理看下面分析。
2)背后原理 2.1 非箭頭函數(shù)的情況當(dāng)使用 new 操作符創(chuàng)建對(duì)象是,ES5 官方文檔在 函數(shù)定義 一節(jié)中做了如下定義 13.2.2 [[Construct]]:
When the [[Construct]] internal method for a Function object F is called with a possibly empty list of arguments, the following steps are taken:
Let obj be a newly created native ECMAScript object.
Set all the internal methods of obj as specified in 8.12.
Set the [[Class]] internal property of obj to Object.
Set the [[Extensible]] internal property of obj to true.
Let proto be the value of calling the [[Get]] internal property of F with argument "prototype".
If Type(proto) is Object, set the [[Prototype]] internal property of obj to proto.
If Type(proto) is not Object, set the [[Prototype]] internal property of obj to the standard built-in Object prototype object as described in 15.2.4.
Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list passed into [[Construct]] as args.
If Type(result) is Object then return result.
Return obj.
看第 8、9 步:
8)調(diào)用函數(shù) F,將其返回值賦給 result;其中,F 執(zhí)行時(shí)的實(shí)參為傳遞給 [[Construct]](即 F 本身) 的參數(shù),F 內(nèi)部 this 指向 obj;
9)如果 result 是 Object 類型,返回 result;
這也就解釋了如果構(gòu)造函數(shù)顯式返回對(duì)象類型,則直接返回這個(gè)對(duì)象,而不是返回最開始創(chuàng)建的對(duì)象。
最后在看第 10 步:
10)如果 F 返回的不是對(duì)象類型(第 9 步不成立),則返回創(chuàng)建的對(duì)象 obj。
如果構(gòu)造函數(shù)沒有顯式返回對(duì)象類型(顯式返回基本數(shù)據(jù)類型或者直接不返回),則返回最開始創(chuàng)建的對(duì)象。
2.2 箭頭函數(shù)的情況那如果構(gòu)造函數(shù)是箭頭函數(shù)怎么辦?
箭頭函數(shù)中沒有 [[Construct]] 方法,不能使用 new 調(diào)用,會(huì)報(bào)錯(cuò)。
NOTICE:其中 [[Construct]] 就是指構(gòu)造函數(shù)本身。
相關(guān)規(guī)范在 ES6 的官方文檔 中有提,但自從 ES6 以來的官方文檔巨難懂,在此不做表述。3)new 調(diào)用函數(shù)完整過程 3.1 中文描述及相關(guān)代碼分析
除了箭頭函數(shù)之外的任何函數(shù),都可以使用 new 進(jìn)行調(diào)用,背后發(fā)生了什么,上節(jié)英文講述的很清楚了,再用中文描述如下:
1)創(chuàng)建 ECMAScript 原生對(duì)象 obj;
2)給 obj 設(shè)置原生對(duì)象的內(nèi)部屬性;(和原型屬性不同,內(nèi)部屬性表示為 [[PropertyName]],兩個(gè)方括號(hào)包裹屬性名,并且屬性名大寫,比如常見 [[Prototype]]、[[Constructor]])
3)設(shè)置 obj 的內(nèi)部屬性 [[Class]] 為 Object;
4)設(shè)置 obj 的內(nèi)部屬性 [[Extensible]] 為 true;
5)將 proto 的值設(shè)置為 F 的 prototype 屬性值;
6)如果 proto 是對(duì)象類型,則設(shè)置 obj 的內(nèi)部屬性 [[Prototype]] 值為 proto;(進(jìn)行原型鏈關(guān)聯(lián),實(shí)現(xiàn)繼承的關(guān)鍵)
7)如果 proto 是不對(duì)象類型,則設(shè)置 obj 的內(nèi)部屬性 [[Prototype]] 值為內(nèi)建構(gòu)造函數(shù) Object 的 prototype 值;(函數(shù) prototype 屬性可以被改寫,如果改成非對(duì)象類型,obj 的 [[Prototype]] 就指向 Object 的原型對(duì)象)
8)9)10)見上節(jié)分析。(決定返回什么)
對(duì)于第 7 步的情況,見下面代碼:
function Foo(name) { this.name = name; } var o1 = new Foo("xiaoming"); console.log(o1.__proto__ === Foo.prototype); // true // 重寫構(gòu)造函數(shù)原型屬性為非對(duì)象類型,實(shí)例內(nèi)部 [[Prototype]] 屬性指向 Object 原型對(duì)象 // 因?yàn)閷?shí)例是一個(gè)對(duì)象類型的數(shù)據(jù),默認(rèn)會(huì)繼承內(nèi)建對(duì)象的原型, // 如果構(gòu)造函數(shù)的原型不滿足形成原型鏈的要求,那就跳過直接和內(nèi)建對(duì)象原型關(guān)聯(lián) Foo.prototype = 1; var o2 = new Foo("xiaohong"); console.log(o2.__proto__ === Foo.prototype); // false console.log(o2.__proto__ === Object.prototype); // true3.2 更簡(jiǎn)潔的語(yǔ)言描述
若執(zhí)行 new Foo(),過程如下:
1)創(chuàng)建新對(duì)象 o;
2)給新對(duì)象的內(nèi)部屬性賦值,關(guān)鍵是給[[Prototype]]屬性賦值,構(gòu)造原型鏈(如果構(gòu)造函數(shù)的原型是 Object 類型,則指向構(gòu)造函數(shù)的原型;不然指向 Object 對(duì)象的原型);
3)執(zhí)行函數(shù) Foo,執(zhí)行過程中內(nèi)部 this 指向新創(chuàng)建的對(duì)象 o;
4)如果 Foo 內(nèi)部顯式返回對(duì)象類型數(shù)據(jù),則,返回該數(shù)據(jù),執(zhí)行結(jié)束;不然返回新創(chuàng)建的對(duì)象 o。
關(guān)于一個(gè)數(shù)據(jù)是否是 Object 類型,可以通過 instanceof 操作符進(jìn)行判斷:如果 x instanceof Object 返回 true,則 x 為 Object 類型。
由上可知,null instanceof Object 返回 false,所以 null 不是 Object 類型,盡管typeof null 返回 "Object"。
4.2 instanceof 原理instanceof 的工作原理是:在表達(dá)式 x instanceof Foo 中,如果 Foo 的原型(即 Foo.prototype)出現(xiàn)在 x 的原型鏈中,則返回 true,不然,返回 false。
因?yàn)楹瘮?shù)的原型可以被改寫,所以會(huì)出現(xiàn)在 x 通過 Foo new 出來之后完全改寫 Foo 的原型 x instanceof Foo 返回 false 的情況。因?yàn)閷?shí)例創(chuàng)建之后重寫構(gòu)造函數(shù)原型,實(shí)例指向的原型已經(jīng)不是構(gòu)造函數(shù)的新的原型了,見下面代碼:
const Foo = function() {}; const o = new Foo(); o instanceof Foo; // true // 重寫 Foo 原型 Foo.prototype = {}; o instanceof Foo; // false參考資料
What values can a constructor return to avoid returning this?
[[Construct]] internal method
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/95783.html
摘要:理解的函數(shù)基礎(chǔ)要搞好深入淺出原型使用原型模型,雖然這經(jīng)常被當(dāng)作缺點(diǎn)提及,但是只要善于運(yùn)用,其實(shí)基于原型的繼承模型比傳統(tǒng)的類繼承還要強(qiáng)大。中文指南基本操作指南二繼續(xù)熟悉的幾對(duì)方法,包括,,。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。 怎樣使用 this 因?yàn)楸救藢儆趥吻岸耍虼宋闹兄豢炊?8 成左右,希望能夠給大家?guī)韼椭?...(據(jù)說是阿里的前端妹子寫的) this 的值到底...
摘要:如果一個(gè)即時(shí)定時(shí)器是被一個(gè)正在執(zhí)行的回調(diào)排入隊(duì)列的,則該定時(shí)器直到下一次事件循環(huán)迭代才會(huì)被觸發(fā)。參數(shù)描述在事件循環(huán)的當(dāng)前回合結(jié)束時(shí)要調(diào)用的函數(shù)。事件輪詢隨后的調(diào)用,會(huì)在任何事件包括定時(shí)器之前運(yùn)行。 系列文章 Nodejs高性能原理(上) --- 異步非阻塞事件驅(qū)動(dòng)模型Nodejs高性能原理(下) --- 事件循環(huán)詳解 前言 終于開始我nodejs的博客生涯了,先從基本的原理講起.以前寫...
摘要:構(gòu)造函數(shù),實(shí)例構(gòu)造函數(shù),是用來創(chuàng)建對(duì)象的函數(shù),本質(zhì)上也是函數(shù)。這里剛好解釋一下時(shí),說到的,可以通過實(shí)例的訪問構(gòu)造函數(shù),但是本質(zhì)上是原型對(duì)象的屬性。 前言 最近在學(xué)vue,到周末終于有空寫一些東西了(想想又能騙贊,就有點(diǎn)小激動(dòng)?。?。在javascript基礎(chǔ)中,除了閉包之外,繼承也是一個(gè)難點(diǎn)。因?yàn)榭紤]到篇幅較長(zhǎng),所以打算分成兩個(gè)部分來寫。同樣基于《javascript高級(jí)程序設(shè)計(jì)》,做一...
摘要:首先,下載并在的中使用然后,我們需要準(zhǔn)備一個(gè)模型,在函數(shù)中,創(chuàng)建變量,用于導(dǎo)入模型導(dǎo)入模型的時(shí)候,接受兩個(gè)參數(shù),第一個(gè)表示模型路徑,第二個(gè)表示完成導(dǎo)入后的回調(diào)函數(shù),一般我們需要在這個(gè)回調(diào)函數(shù)中將導(dǎo)入的模型添加到場(chǎng)景中。 9. 動(dòng)畫 在本章之前,所有畫面都是靜止的,本章將介紹如果使用Three.js進(jìn)行動(dòng)態(tài)畫面的渲染。此外,將會(huì)介紹一個(gè)Three.js作者寫的另外一個(gè)庫(kù),用來觀測(cè)每秒幀數(shù)...
閱讀 1638·2021-11-02 14:42
閱讀 2407·2021-10-11 10:58
閱讀 733·2021-09-26 09:46
閱讀 2968·2021-09-08 09:35
閱讀 1499·2021-08-24 10:01
閱讀 1285·2019-08-30 15:54
閱讀 3666·2019-08-30 15:44
閱讀 1852·2019-08-30 10:49