Heim  >  Artikel  >  Web-Frontend  >  JavaScript: Diesmal verstehe ich den neuen Operator vollständig!

JavaScript: Diesmal verstehe ich den neuen Operator vollständig!

coldplay.xixi
coldplay.xixinach vorne
2020-09-27 17:30:371676Durchsuche

JavaScript: Diesmal verstehe ich den neuen Operator vollständig!

Vorwort

Beim Erlernen von JavaScript werden Sie unweigerlich auf den Operator new stoßen. Schauen wir uns dieses Mal genauer an und vertiefen Ihr Verständnis und Ihr Gedächtnis. new操作符,这次就来好好刨根问底一下,也算是加深理解和记忆了。

什么是new操作符?

mdn中是这么定义new操作符的:

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

在这句话里我们来看一个关键词:具有构造函数。这是个什么意思呢?我们先通过几个例子来看一下:

//例1let Animal1=function(){this.name=1};let animal=new Animal1; //这里不带()相当于不传参数//=>Animal1 {name: 1}//例2let TestObj={}let t1=new TestObj;//=>Uncaught TypeError: TestObj is not a constructor复制代码

我们可以看到,例1成功的执行了new语句,创建出了实例。例2在new一个{}对象时报错TypeError: TestObj is not a constructor,指出目标不是一个constructor。为什么普通的对象就不能执行new操作符呢?在ECMA规范里有相关的介绍:

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

意思就是:

  • 构造函数首先得是一个对象,否则不满足条件
  • 其次,对象必须拥有[[Construct]]内部方法,才可以作为构造函数  

我们这里的{}就是一个对象,满足第一个条件,那么显然,肯定是因为{}没有[[Construct]]这个内部方法,所以无法使用new操作符进行构造了。

那么我们已经搞定了new操作符的可操作对象,是不是可以去看看它的作用了呢?答案是:NO!我们再来看一个例子:

//例3let testObj={
    Fn(){        console.log("构造成功!")
    }
}let t3=new testObj.Fn;//=>Uncaught TypeError: testObj.Fn is not a constructor复制代码

what?为什么刚刚还能成功构造的函数,作为方法就不行了呢?其实在MDN中也有直接介绍:

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

意思就是,方法不能是构造函数,如果尝试创建一个方法的实例,就会抛出类型错误。这样说就懂了,但是还没完,这个说法没有完全解释清楚原理,我们再看个例子:

//例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复制代码

对照这个例子,我们在ECMA规范查阅,发现所有的函数在创建时都取决于FunctionCreate函数:

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".

这个函数的定义可以看出

  • 只有当类型为Normal的函数被创建时,它才是可构造的函数,否则他就是不可构造的。

在我们这个例子中,Arrow的类型为Arrow,而ShortHand的类型是Method,因此都不属于可构造的函数,这也解释了例3所说的"方法不能作为构造函数"。 搞清楚了new操作符可以操作的目标,终于可以神清气爽的来看看它的作用了(不容易呀TAT)。

new操作符实现了什么?

我们举一个简单的例子来具体看看它的作用:

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:大黄复制代码

我们从这个例子来分析一下,首先我们看这一句:

let animal=new Animal("大黄");复制代码

可以看到,执行new操作符后,我们得到了一个animal对象,那么我们就知道,new操作符肯定要创建一个对象,并将这个对象返回。再看这段代码:

function Animal(name){    this.name=name;    console.log("create animal");
}复制代码

同时我们看到结果,确实输出了create animal,我们就知道,Animal函数体在这个过程中被执行了,同时传入了参数,所以才执行了我们的输出语句。但我们的函数体里还有一句this.name=name体现在哪里呢?就是这一句:

console.log(animal.name);       //大黄复制代码

执行完函数体后,我们发现返回对象的name值就是我们赋值给this的值,那么不难判断,在这个过程中,this的值指向了新创建的对象。最后还有一段:

Animal.prototype.say=function(){    console.log("myName is:"+this.name);
}
animal.say();                   //myName is:大黄复制代码

animal对象调用的是Animal函数原型上的方法,说明Animalanimal对象的原型链上,那么在哪一层呢?我们验证一下:

animal.__proto__===Animal.prototype; //true复制代码

那我们就知道了,animal__proto__直接指向了Animalprototype

Was ist der neue Operator? 🎜Der new-Operator ist in mdn wie folgt definiert: 🎜
🎜 Der new-Operator erstellt eine Instanz eines benutzerdefinierten Objekttyps oder eine Instanz eines integrierten Objekts mit einem Konstruktor. 🎜
🎜In diesem Satz schauen wir uns ein Schlüsselwort an: hat einen Konstruktor. Was bedeutet das? Schauen wir uns zunächst ein paar Beispiele an: 🎜
function Animal(name){    this.name=name;    return 1;
}new Animal("test"); //Animal {name: "test"}复制代码
🎜 Wir können sehen, dass Beispiel 1 die new-Anweisung erfolgreich ausgeführt und eine Instanz erstellt hat. Beispiel 2 Wenn new ein {}-Objekt einen Fehler TypeError: TestObj is not a constructionor meldet, was darauf hinweist, dass das Ziel kein ist Konstruktor. Warum können gewöhnliche Objekte den Operator new nicht ausführen? Es gibt eine entsprechende Einführung in der ECMA-Spezifikation:🎜
🎜Wenn Type(argument) nicht Object ist, wird false zurückgegeben.
Wenn das Argument eine interne Methode [[Construct]] hat, Rückkehr wahr. Gibt false zurück.🎜
🎜bedeutet:🎜
  • Der Konstruktor muss erstens ein Objekt sein, sonst ist die Bedingung nicht erfüllt
  • Zweitens , Ein Objekt muss eine interne Methode [[Construct]] haben, bevor es als Konstruktor verwendet werden kann
🎜Der {} Wir haben hier ein Objekt, das die erste Bedingung erfüllt, dann muss es offensichtlich daran liegen, dass {} nicht über die interne Methode [[Construct]] verfügt, also newkann nicht verwendet werden > Operator wird erstellt. 🎜🎜Da wir nun die ausführbaren Objekte des new-Operators herausgefunden haben, können wir uns seine Funktion ansehen? Die Antwort lautet: NEIN! Schauen wir uns ein anderes Beispiel an: 🎜
function Animal(name){    this.name=name;    return {};
}new Animal("test"); //{}复制代码
🎜Was? Warum kann die Funktion, die gerade erfolgreich erstellt wurde, nicht als Methode funktionieren? Tatsächlich wird es auch direkt in MDN eingeführt:🎜
🎜Methoden können keine Konstruktoren sein! /strong > wird ein Typfehler ausgegeben, wenn Sie versuchen, eine Instanz einer Methode zu erstellen. Es macht Sinn, es so zu sagen, aber es ist noch nicht das Ende dieser Aussage. Schauen wir uns ein anderes Beispiel an: 🎜
var _myNew = function (constructor, ...args) {    // 1. 创建一个新对象obj
    const obj = {};    //2. 将this绑定到新对象上,并使用传入的参数调用函数

    //这里是为了拿到第一个参数,就是传入的构造函数
    // let constructor = Array.prototype.shift.call(arguments);
    //绑定this的同时调用函数,...将参数展开传入
    let res = constructor.call(obj, ...args)

    //3. 将创建的对象的_proto__指向构造函数的prototype
    obj.__proto__ = constructor.prototype

    //4. 根据显示返回的值判断最终返回结果
    return res instanceof Object ? res : obj;
}复制代码
🎜Im Vergleich zu diesem Beispiel haben wir die ECMA-Spezifikation überprüft und festgestellt, dass alle Funktionen davon abhängen wenn sie erstellt werden. FunctionCreateFunction:🎜
🎜FunctionCreate (kind, ParameterList, Body, Scope, Strict, Prototyp)🎜
  1. Wenn das Prototyp-Argument nicht übergeben wurde, dann Prototyp sei das intrinsische Objekt %FunctionPrototype %.
  2. Wenn „kind“ nicht „Normal“ ist, sei allocKind „Nicht-Konstruktor“.
🎜Die Definition von Diese Funktion ist sichtbar🎜
  • Nur wenn eine Funktion vom Typ Normal erstellt wird, handelt es sich um eine konstruierbare Funktion, andernfalls ist sie nicht konstruierbar.
🎜In unserem Beispiel ist der Typ von Arrow Arrow und der Typ von ShortHand Es handelt sich um eine Methode, also keine konstruierbare Funktion. Dies erklärt auch, was in Beispiel 3 steht: „Methode kann nicht als Konstruktor verwendet werden“. Nachdem ich die Ziele herausgefunden habe, auf die der new-Operator zugreifen kann, kann ich endlich einen klaren Blick auf seine Funktion werfen (kein einfacher TAT). 🎜

Was implementiert der neue Operator? 🎜🎜Nehmen wir ein einfaches Beispiel, um seine Funktion im Detail zu sehen:🎜
function _new(fn, ...arg) {    const obj = Object.create(fn.prototype);    const res = fn.apply(obj, arg);    return res instanceof Object ? res : obj;复制代码
🎜Lass es uns anhand dieses Beispiels analysieren. Schauen wir uns zunächst diesen Satz an:🎜rrreee🎜Wie Sie sehen können, führen Sie den new aus operation Nach dem Operator erhalten wir ein animal-Objekt, dann wissen wir, dass der new-Operator ein Objekt erstellen und dieses Objekt zurückgeben muss. Schauen Sie sich diesen Code noch einmal an: 🎜rrreee🎜Gleichzeitig sehen wir, dass das Ergebnis create animal tatsächlich ausgegeben wird Gleichzeitig werden Parameter übergeben, sodass unsere Ausgabeanweisung ausgeführt wird. Aber wo spiegelt sich der Satz this.name=name in unserem Funktionskörper wider? Dies ist der Satz:🎜rrreee🎜Nachdem wir den Funktionskörper ausgeführt haben, haben wir festgestellt, dass der name-Wert des zurückgegebenen Objekts der Wert ist, den wir this zugewiesen haben, also ist dies nicht der Fall In diesem Prozess zeigt der Wert von this auf das neu erstellte Objekt. Am Ende gibt es einen weiteren Absatz: 🎜rrreee🎜Das Objekt animal ruft die Methode für den Funktionsprototyp Animal auf und gibt an, dass sich Animal in animal code> Die Prototypenkette des Objekts. Auf welcher Ebene befindet es sich also? Überprüfen wir: 🎜rrreee🎜Dann wissen wir, dass der __proto__ von animal direkt auf den prototype von Animal verweist. Sehen wir uns außerdem an, was passiert, wenn wir einen Wert im Hauptteil des Konstruktors zurückgeben: 🎜
function Animal(name){    this.name=name;    return 1;
}new Animal("test"); //Animal {name: "test"}复制代码

可以看到,直接无视了返回值,那我们返回一个对象试试:

function Animal(name){    this.name=name;    return {};
}new Animal("test"); //{}复制代码

我们发现返回的实例对象被我们的返回值覆盖了,到这里大致了解了new操作符的核心功能,我们做一个小结。

小结

new操作符的作用:

  • 创建一个新对象,将this绑定到新创建的对象
  • 使用传入的参数调用构造函数
  • 将创建的对象的_proto__指向构造函数的prototype
  • 如果构造函数没有显式返回一个对象,则返回创建的新对象,否则返回显式返回的对象(如上文的{}

模拟实现一个new操作符

说了这么多理论的,最后我们亲自动手来实现一个new操作符吧~

var _myNew = function (constructor, ...args) {    // 1. 创建一个新对象obj
    const obj = {};    //2. 将this绑定到新对象上,并使用传入的参数调用函数

    //这里是为了拿到第一个参数,就是传入的构造函数
    // let constructor = Array.prototype.shift.call(arguments);
    //绑定this的同时调用函数,...将参数展开传入
    let res = constructor.call(obj, ...args)

    //3. 将创建的对象的_proto__指向构造函数的prototype
    obj.__proto__ = constructor.prototype

    //4. 根据显示返回的值判断最终返回结果
    return res instanceof Object ? res : obj;
}复制代码

上面是比较好理解的版本,我们可以简化一下得到下面这个版本:

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

大功告成!

总结

本文从定义出发,探索了new操作符的作用目标和原理,并模拟实现了核心功能。其实模拟实现一个new操作符不难,更重要的还是去理解这个过程,明白其中的原理。

更多相关免费学习推荐:javascript(视频)

Das obige ist der detaillierte Inhalt vonJavaScript: Diesmal verstehe ich den neuen Operator vollständig!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.im. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen