深入理解new操作符


new的定義:

1、創建一個新的對象;

2、將構造函數的prototype賦值給新對象的_proto_;

3、構造函數中的this指向新對象,並且調用構造函數;

4、如果構造函數無返回值,或者不是引用類型,返回新對象;否則為構造函數的返回值。

instanceof

instanceof運算符用於判斷一個對象的原型鏈是否存在一個構造函數的prototype屬性。

語法:object instanceof constructor 

參數:object(要檢測的對象)  constructor(某個構造函數)

描述:instanceof運算符用來檢測constructor。prototype是否存在於參數object的原型鏈上

下面通過代碼闡述instanceof的內部機制,假設有x instanceof y一條語句,則其內部實際做了如下判斷:

while(x.__proto__!==null) {
    if(x.__proto__===y.prototype) {
        return true;
        break;
    }
    x.__proto__ = x.__proto__.proto__;
}
if(x.__proto__==null) {return false;}

 

x會一直沿着隱式原型鏈__proto__向上查找直到x.__proto__.__proto__...===y.prototype為止,如果找到則返回true,即xy的實例,否則返回falsex不是y的實例。

相關面試題

function F() {}
function O() {}

O.prototype = new F();
var obj = new O();

console.log(obj instanceof O); // true
console.log(obj instanceof F); // true
console.log(obj.__proto__ === O.prototype); // true
console.log(obj.__proto__.__proto__ === F.prototype); // true

 

根據new的內部機制改寫代碼

function F() {}
function O() {}

var obj = (function () {
    var obj1 = {};
    obj1.__proto__ = F.prototype; // new F();
    O.prototype = obj1; // O.prototype = new F();
    obj.__proto__ = O.prototype; // new O();
    obj.__proto__ = obj1;
    return obj;
})()

 

如果改一下代碼順序,結果將不同

function F() {}
function O() {}

var obj = new O();
O.prototype = new F();

console.log(obj instanceof O); // false
console.log(obj instanceof F); // false
console.log(obj.__proto__ === O.prototype); // false
console.log(obj.__proto__.__proto__ === F.prototype); // false

 

和其他高級語言一樣javascript也有new操作符,我們知道new可以用來實例化一個類,從而在內存中分配一個實例對象。但在JavaScript中,萬物皆對象,那么為什么還要通過new來產生對象?接下來就來了解一下new的特性:

function Animal(name) {
  this.name = name;
}
Animal.color = "black";
Animal.prototype.say = function() {
  console.log("I'm" + this.name);
};
var cat = new Animal("cat");
console.log(
  cat.name,  //cat
  cat.height  //undefined
);
cat.say();//I'm cat
console.log(
  Animal.name,//Animal
  Animal.color//back
);
Animal.say();//Animal.say is a function

代碼解讀:

1-3:創建了一個叫Animal的函數,並在其this上定義了屬性:name,name的值是函數被執行時的形參。

4:在Animal對象(Animal本身是一個函數對象)上定義了一個靜態屬性:color,並復制“black”

5-7:在Animal函數的原型對象prototype上定義了一個say()方法,say方法輸出了this的name值。

8:通過new關鍵字創建了一個新對象cat

9-1:3:cat對象嘗試訪問name和color屬性,並調用say方法。

14-18:Animal對象嘗試訪問name和color屬性,並調用say方法

剖析new的內部原理:

var cat = new Animal("cat");

Animal本身是一個普通函數,但當通過new來創建對象時,Animal就是構造函數。

JS引擎執行這句代碼時,在內部做了很多工作,用偽代碼模擬其內部流程如下:

new Animal('cat') = {
  var obj = {};
  obj._proto_ = Animal.prototype;
  var result  = Animal.call(obj,"cat");
  return typyof result === 'object' ? result : obj;
}

將上述流程分為4個步驟來理解:

1、創建一個空對象obj;

2、把obj_prototype_指向構造函數Animal的原型對象prototype,此時便建立了obj對象的原型鏈:obj->Animal.prototype->Object.prototype->null

3、在obj對象的執行環境調用Animal函數並傳遞參數“cat”.相當於var result = obj.Animal("cat").當這句執行完之后,obj便產生了屬性name並賦值“cat”。關於 call 的用法請參考:深入理解 call、apply 和 bind

4、考察第3步的返回值,如果無返回值或者返回一個非對象值,則將obj作為新對象返回:否則會將result作為新對象返回。根據以上過程,我們發現cat其實就是第四步的返回值,因此我們對cat對象的認知就多一點:

  • cat的原型鏈是:cat->Animal.prototype->Object.prototype->null
  • cat上新增了一個屬性:name

分析完了cat的生產過程,在分析一下輸出結果:

  • cat.name - 在第三步中,obj對象就產生了name屬性。因此cat.name就是這里的obj.name
  • cat.color - cat 對象先查找自身的color,沒有找到便會沿着原型鏈查找,在上述例子中,我們僅對Animal對象定義了color,並沒有在其原型鏈上定義,因此找不到。
  • cat.say - cat會先查找自身的say方法,沒有找到便會沿着原型鏈找,在上述例子中,我們在Animal的prototype上定義了say,因此在原型鏈找到了say方法。

另外,在say方法中還訪問this.name,這里的this指的是其調用者obj,因此輸出的是obj.name的值。

對於Animal來說,它本身也是一個對象,因此它在訪問屬性和方法時也遵守上述查找規則,所以:

  • Animal.color >  " black "
  • Animal.name >  " Animal " 
  • Animal.say() >   Animal.say is not a function

需要注意的是:Animal先查找自身的name,找到了name,但這個name並不是我們定義的name,而是函數對象的屬性,一般情況下,函數對象在生產時會內置name屬性並將函數名作為賦值(僅函數對象)。

另外,Animal 在自身沒有找到 say() 方法,也會沿着其原型鏈查找,Animal 的原型鏈如下所示:

 

 Animal 的原型鏈: Animal->Function.prototype->Object.prototype->null

 由於 Animal 的原型鏈上也沒有定義 say 方法,因此返回異常提示。

探索new意義:

1、cat繼承了Animal對象

2、cat是Animal的實例

cat 是通過 new 產生的對象,那么 cat 到底是不是 Animal 的實例對象? 我們先來了解一下JS是如何來定義實例對象

1
A instanceof B

如果上述表達式為 true,JavaScript 認為 A 是 B 的實例對象,我們用這個方法來判斷一下 cat 和 Animal 。

1
cat instanceof Animal; //true

從結果看,cat 確實是 Animal 實例,要想更加證實這個結果,我們再來了解一下 instanceof 的內部原理:

1
2
3
4
var  L = A.__proto__;
var  R = B.prototype;
if (L === R)
     return  true ;

如果 A 的__proto__ 等價於 B 的 prototype,就返回 true 。

在 new 的執行過程【2】中,cat 的 __proto__ 指向了Animal 的 prototype,所以 cat 和 Animal 符合 instanceof 的判斷結果。

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

下面是一個經典例子,涉及 newthis、以及原型鏈相關問題,請看代碼:
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();

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM