詳解 JS 中 new 挪用函數道理

JavaScript 中常常運用組織函數建立對象(經由歷程 new 操作符挪用一個函數),那在運用 new 挪用一個函數的時刻究竟發生了什麼?先看幾個例子,再詮釋背地發生了什麼。

1)看三個例子

1.1 無 return 語句

組織函數末了沒有 return 語句,這也是運用組織函數時默許狀況,末了會返回一個新對象,以下:

function Foo(age) {
  this.age = age;
}

var o = new Foo(111);
console.log(o);

這是罕見的運用組織函數建立對象的歷程,打印出來的是 {age: 111}

1.2 return 對象範例數據

組織函數末了 return 對象範例數據:

function Foo(age) {
  this.age = age;

  return { type: "我是顯式返回的" };
}

var o = new Foo(222);
console.log(o);

打印出來的是 {type: '我是顯式返回的'},也就是說,return 之前的事情都白做了,末了返回 return 背面的對象。

1.3 return 基礎範例數據

那是不是只需組織函數體內末了有 return,返回都是 return 背面的數據呢?

我們看下返回基礎範例數據的狀況:

function Foo(age) {
  this.age = age;

  return 1;
}

var o = new Foo(333);
console.log(o);

打印出來的是 {age: 333},和沒有 return 時結果一樣。跟預期不一樣,背地你道理看下面剖析。

2)背地道理

2.1 非箭頭函數的狀況

當運用 new 操作符建立對象是,ES5 官方文檔在 函數定義 一節中做了以下定義 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:

  1. Let obj be a newly created native ECMAScript object.
  2. Set all the internal methods of obj as specified in 8.12.
  3. Set the [[Class]] internal property of obj to Object.
  4. Set the [[Extensible]] internal property of obj to true.
  5. Let proto be the value of calling the [[Get]] internal property of F with argument “prototype”.
  6. If Type(proto) is Object, set the [[Prototype]] internal property of obj to proto.
  7. 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.
  8. 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.
  9. If Type(result) is Object then return result.
  10. Return obj.

看第 8、9 步:

8)挪用函數
F,將其返回值賦給
result;个中,
F 實行時的實參為傳遞給
[[Construct]](即
F 自身) 的參數,
F 內部
this 指向
obj

9)假如
result
Object 範例,返回
result

這也就詮釋了假如組織函數顯式返回對象範例,則直接返回這個對象,而不是返回最最先建立的對象。

末了在看第 10 步:

10)假如
F 返回的不是對象範例(第 9 步不成立),則返回建立的對象
obj

假如組織函數沒有顯式返回對象範例(顯式返回基礎數據範例或許直接不返回),則返回最最先建立的對象。

2.2 箭頭函數的狀況

那假如組織函數是箭頭函數怎麼辦?

箭頭函數中沒有 [[Construct]] 要領,不能運用 new 挪用,會報錯。

NOTICE:个中 [[Construct]] 就是指組織函數自身。

相干範例在
ES6 的官方文檔 中有提,但自從 ES6 以來的官方文檔巨難明,在此不做表述。

3)new 挪用函數完整歷程

3.1 中文形貌及相干代碼剖析

除了箭頭函數以外的任何函數,都能夠運用 new 舉行挪用,背地發生了什麼,上節英文報告的很清晰了,再用中文形貌以下:

1)建立 ECMAScript 原生對象 obj
2)給 obj 設置原生對象的內部屬性;(和原型屬性差別,內部屬性示意為 [[PropertyName]],兩個方括號包裹屬性名,而且屬性名大寫,比方罕見 [[Prototype]][[Constructor]]
3)設置 obj 的內部屬性 [[Class]]Object
4)設置 obj 的內部屬性 [[Extensible]]true
5)將 proto 的值設置為 Fprototype 屬性值;
6)假如 proto 是對象範例,則設置 obj 的內部屬性 [[Prototype]] 值為 proto;(舉行原型鏈關聯,完成繼續的癥結
7)假如 proto 是不對象範例,則設置 obj 的內部屬性 [[Prototype]] 值為內建組織函數 Objectprototype 值;(函數 prototype 屬性能夠被改寫,假如改成非對象範例,obj[[Prototype]] 就指向 Object 的原型對象)
8)9)10)見上節剖析。(決議返回什麼)

關於第 7 步的狀況,見下面代碼:

function Foo(name) {
  this.name = name;
}

var o1 = new Foo("xiaoming");
console.log(o1.__proto__ === Foo.prototype); // true

// 重寫組織函數原型屬性為非對象範例,實例內部 [[Prototype]] 屬性指向 Object 原型對象
// 由於實例是一個對象範例的數據,默許會繼續內建對象的原型,
// 假如組織函數的原型不滿足構成原型鏈的請求,那就跳過直接和內建對象原型關聯
Foo.prototype = 1;
var o2 = new Foo("xiaohong");
console.log(o2.__proto__ === Foo.prototype); // false
console.log(o2.__proto__ === Object.prototype); // true

3.2 更簡約的言語形貌

若實行 new Foo(),歷程以下:

1)建立新對象 o
2)給新對象的內部屬性賦值,癥結是給[[Prototype]]屬性賦值,組織原型鏈(假如組織函數的原型是 Object 範例,則指向組織函數的原型;不然指向 Object 對象的原型);
3)實行函數 Foo,實行歷程當中內部 this 指向新建立的對象 o
4)假如 Foo 內部顯式返回對象範例數據,則,返回該數據,實行完畢;不然返回新建立的對象 o

4)幾點申明

4.1 推斷是不是是 Object 範例

關於一個數據是不是是 Object 範例,能夠經由歷程 instanceof 操作符舉行推斷:假如 x instanceof Object 返回 true,則 xObject 範例。

由上可知,null instanceof Object 返回 false,所以 null 不是 Object 範例,只管typeof null 返回 “Object”。

4.2 instanceof 道理

instanceof 的事情道理是:在表達式 x instanceof Foo 中,假如 Foo 的原型(即 Foo.prototype)出現在 x 的原型鏈中,則返回 true,不然,返回 false

由於函數的原型能夠被改寫,所以會出現在 x 經由歷程 Foo new 出來以後完整改寫 Foo 的原型 x instanceof Foo 返回 false 的狀況。由於實例建立以後重寫組織函數原型,實例指向的原型已不是組織函數的新的原型了,見下面代碼:

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

    原文作者:L小庸
    原文地址: https://segmentfault.com/a/1190000015424508
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞