溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript之new操作符是什么

發(fā)布時間:2020-09-30 16:42:32 來源:億速云 閱讀:208 作者:小新 欄目:web開發(fā)

小編給大家分享一下JavaScript之new操作符是什么,希望大家閱讀完這篇文章后大所收獲,下面讓我們一起去探討吧!

前言

在學(xué)習(xí)JavaScript的過程中,不可避免的會遇到new操作符,這次就來好好刨根問底一下,也算是加深理解和記憶了。

什么是new操作符?

mdn中是這么定義new操作符的:

new 運算符創(chuàng)建一個用戶定義的對象類型的實例或具有構(gòu)造函數(shù)的內(nèi)置對象的實例。

在這句話里我們來看一個關(guān)鍵詞:具有構(gòu)造函數(shù)。這是個什么意思呢?我們先通過幾個例子來看一下:

//例1let Animal1=function(){this.name=1};let animal=new Animal1; //這里不帶()相當(dāng)于不傳參數(shù)//=>Animal1 {name: 1}//例2let TestObj={}let t1=new TestObj;//=>Uncaught TypeError: TestObj is not a constructor復(fù)制代碼

我們可以看到,例1成功的執(zhí)行了new語句,創(chuàng)建出了實例。例2在new一個{}對象時報錯TypeError: TestObj is not a constructor,指出目標(biāo)不是一個constructor。為什么普通的對象就不能執(zhí)行new操作符呢?在ECMA規(guī)范里有相關(guān)的介紹:

If Type(argument) is not Object, return false.
If argument has a [[Construct]] internal method, return true. Return false.

意思就是:

  • 構(gòu)造函數(shù)首先得是一個對象,否則不滿足條件
  • 其次,對象必須擁有[[Construct]]內(nèi)部方法,才可以作為構(gòu)造函數(shù)  

我們這里的{}就是一個對象,滿足第一個條件,那么顯然,肯定是因為{}沒有[[Construct]]這個內(nèi)部方法,所以無法使用new操作符進(jìn)行構(gòu)造了。

那么我們已經(jīng)搞定了new操作符的可操作對象,是不是可以去看看它的作用了呢?答案是:NO!我們再來看一個例子:

//例3let testObj={
    Fn(){        console.log("構(gòu)造成功!")
    }
}let t3=new testObj.Fn;//=>Uncaught TypeError: testObj.Fn is not a constructor復(fù)制代碼

what?為什么剛剛還能成功構(gòu)造的函數(shù),作為方法就不行了呢?其實在MDN中也有直接介紹:

Methods cannot be constructors! They will throw a TypeError if you try to instantiate them.

意思就是,方法不能是構(gòu)造函數(shù),如果嘗試創(chuàng)建一個方法的實例,就會拋出類型錯誤。這樣說就懂了,但是還沒完,這個說法沒有完全解釋清楚原理,我們再看個例子:

//例4const example = {  Fn: function() { console.log(this); },  Arrow: () => { console.log(this); },
  Shorthand() { console.log(this); }
};new example.Fn();        // Fn {}new example.Arrow();     // Uncaught TypeError: example.Arrow is not a constructornew example.Shorthand(); // Uncaught TypeError: example.Shorthand is not a constructor復(fù)制代碼

對照這個例子,我們在ECMA規(guī)范查閱,發(fā)現(xiàn)所有的函數(shù)在創(chuàng)建時都取決于FunctionCreate函數(shù):

FunctionCreate (kind, ParameterList, Body, Scope, Strict, prototype)

  1. If the prototype argument was not passed, then let prototype be the intrinsic object %FunctionPrototype%.
  2. If "kind" is not Normal, let allocKind be "non-constructor".

這個函數(shù)的定義可以看出

  • 只有當(dāng)類型為Normal的函數(shù)被創(chuàng)建時,它才是可構(gòu)造的函數(shù),否則他就是不可構(gòu)造的。

在我們這個例子中,Arrow的類型為Arrow,而ShortHand的類型是Method,因此都不屬于可構(gòu)造的函數(shù),這也解釋了例3所說的"方法不能作為構(gòu)造函數(shù)"。 搞清楚了new操作符可以操作的目標(biāo),終于可以神清氣爽的來看看它的作用了(不容易呀TAT)。

new操作符實現(xiàn)了什么?

我們舉一個簡單的例子來具體看看它的作用:

function Animal(name){    this.name=name;    console.log("create animal");
}let animal=new Animal("大黃");  //create animalconsole.log(animal.name);       //大黃Animal.prototype.say=function(){    console.log("myName is:"+this.name);
}
animal.say();                   //myName is:大黃復(fù)制代碼

我們從這個例子來分析一下,首先我們看這一句:

let animal=new Animal("大黃");復(fù)制代碼

可以看到,執(zhí)行new操作符后,我們得到了一個animal對象,那么我們就知道,new操作符肯定要創(chuàng)建一個對象,并將這個對象返回。再看這段代碼:

function Animal(name){    this.name=name;    console.log("create animal");
}復(fù)制代碼

同時我們看到結(jié)果,確實輸出了create animal,我們就知道,Animal函數(shù)體在這個過程中被執(zhí)行了,同時傳入了參數(shù),所以才執(zhí)行了我們的輸出語句。但我們的函數(shù)體里還有一句this.name=name體現(xiàn)在哪里呢?就是這一句:

console.log(animal.name);       //大黃復(fù)制代碼

執(zhí)行完函數(shù)體后,我們發(fā)現(xiàn)返回對象的name值就是我們賦值給this的值,那么不難判斷,在這個過程中,this的值指向了新創(chuàng)建的對象。最后還有一段:

Animal.prototype.say=function(){    console.log("myName is:"+this.name);
}
animal.say();                   //myName is:大黃復(fù)制代碼

animal對象調(diào)用的是Animal函數(shù)原型上的方法,說明Animalanimal對象的原型鏈上,那么在哪一層呢?我們驗證一下:

animal.__proto__===Animal.prototype; //true復(fù)制代碼

那我們就知道了,animal__proto__直接指向了Animalprototype。 除此之外,如果我們在構(gòu)造函數(shù)的函數(shù)體里返回一個值,看看會怎么樣:

function Animal(name){    this.name=name;    return 1;
}new Animal("test"); //Animal {name: "test"}復(fù)制代碼

可以看到,直接無視了返回值,那我們返回一個對象試試:

function Animal(name){    this.name=name;    return {};
}new Animal("test"); //{}復(fù)制代碼

我們發(fā)現(xiàn)返回的實例對象被我們的返回值覆蓋了,到這里大致了解了new操作符的核心功能,我們做一個小結(jié)。

小結(jié)

new操作符的作用:

  • 創(chuàng)建一個新對象,將this綁定到新創(chuàng)建的對象
  • 使用傳入的參數(shù)調(diào)用構(gòu)造函數(shù)
  • 將創(chuàng)建的對象的_proto__指向構(gòu)造函數(shù)的prototype
  • 如果構(gòu)造函數(shù)沒有顯式返回一個對象,則返回創(chuàng)建的新對象,否則返回顯式返回的對象(如上文的{}

模擬實現(xiàn)一個new操作符

說了這么多理論的,最后我們親自動手來實現(xiàn)一個new操作符吧~

var _myNew = function (constructor, ...args) {    // 1. 創(chuàng)建一個新對象obj
    const obj = {};    //2. 將this綁定到新對象上,并使用傳入的參數(shù)調(diào)用函數(shù)

    //這里是為了拿到第一個參數(shù),就是傳入的構(gòu)造函數(shù)
    // let constructor = Array.prototype.shift.call(arguments);
    //綁定this的同時調(diào)用函數(shù),...將參數(shù)展開傳入
    let res = constructor.call(obj, ...args)

    //3. 將創(chuàng)建的對象的_proto__指向構(gòu)造函數(shù)的prototype
    obj.__proto__ = constructor.prototype

    //4. 根據(jù)顯示返回的值判斷最終返回結(jié)果
    return res instanceof Object ? res : obj;
}復(fù)制代碼

上面是比較好理解的版本,我們可以簡化一下得到下面這個版本:

function _new(fn, ...arg) {    const obj = Object.create(fn.prototype);    const res = fn.apply(obj, arg);    return res instanceof Object ? res : obj;復(fù)制代碼

大功告成!

看完了這篇文章,相信你對JavaScript之new操作符是什么有了一定的了解,想了解更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI