首頁  >  文章  >  web前端  >  關於JS中new調用函數的原理介紹

關於JS中new調用函數的原理介紹

不言
不言原創
2018-07-10 17:38:102346瀏覽

這篇文章主要介紹了關於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

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

以上是關於JS中new調用函數的原理介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn