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
操作符,这次就来好好刨根问底一下,也算是加深理解和记忆了。
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)
- If the prototype argument was not passed, then let prototype be the intrinsic object %FunctionPrototype%.
- If "kind" is not Normal, let allocKind be "non-constructor".
这个函数的定义可以看出
Normal
的函数被创建时,它才是可构造的函数,否则他就是不可构造的。在我们这个例子中,Arrow
的类型为Arrow
,而ShortHand
的类型是Method
,因此都不属于可构造的函数,这也解释了例3所说的"方法不能作为构造函数"。
搞清楚了new
操作符可以操作的目标,终于可以神清气爽的来看看它的作用了(不容易呀TAT)。
我们举一个简单的例子来具体看看它的作用:
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
函数原型上的方法,说明Animal
在animal
对象的原型链上,那么在哪一层呢?我们验证一下:
animal.__proto__===Animal.prototype; //true复制代码
那我们就知道了,animal
的__proto__
直接指向了Animal
的prototype
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"}复制代码
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</code >. Warum können gewöhnliche Objekte den Operator <code>new
nicht ausführen? Es gibt eine entsprechende Einführung in der ECMA-Spezifikation:🎜🎜Wenn Type(argument) nicht Object ist, wird false zurückgegeben.🎜bedeutet:🎜
Wenn das Argument eine interne Methode[[Construct]]
hat, Rückkehr wahr. Gibt false zurück.🎜
[[Construct]]
haben, bevor es als Konstruktor verwendet werden kann {}</code > Wir haben hier ein Objekt, das die erste Bedingung erfüllt, dann muss es offensichtlich daran liegen, dass <code>{}
nicht über die interne Methode [[Construct]]
verfügt, also new</code kann nicht verwendet werden > Operator wird erstellt. 🎜🎜Da wir nun die ausführbaren Objekte des <code>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"); //{}复制代码
🎜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: 🎜🎜Im Vergleich zu diesem Beispiel haben wir die ECMA-Spezifikation überprüft und festgestellt, dass alle Funktionen davon abhängenvar _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; }复制代码Nach dem Login kopierenNach dem Login kopierenwenn sie erstellt werden. FunctionCreate
Function:🎜🎜FunctionCreate (kind, ParameterList, Body, Scope, Strict, Prototyp)🎜🎜Die Definition von Diese Funktion ist sichtbar🎜
- Wenn das Prototyp-Argument nicht übergeben wurde, dann Prototyp sei das intrinsische Objekt %FunctionPrototype %.
- Wenn „kind“ nicht „Normal“ ist, sei allocKind „Nicht-Konstruktor“.
🎜In unserem Beispiel ist der Typ von
- Nur wenn eine Funktion vom Typ
Normal
erstellt wird, handelt es sich um eine konstruierbare Funktion, andernfalls ist sie nicht konstruierbar.Arrow
Arrow
und der Typ vonShortHand
Es handelt sich um eineMethode
, 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 dernew
-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:🎜
🎜Lass es uns anhand dieses Beispiels analysieren. Schauen wir uns zunächst diesen Satz an:🎜rrreee🎜Wie Sie sehen können, führen Sie denfunction _new(fn, ...arg) { const obj = Object.create(fn.prototype); const res = fn.apply(obj, arg); return res instanceof Object ? res : obj;复制代码Nach dem Login kopierenNach dem Login kopierennew
aus operation Nach dem Operator erhalten wir einanimal
-Objekt, dann wissen wir, dass dernew
-Operator ein Objekt erstellen und dieses Objekt zurückgeben muss. Schauen Sie sich diesen Code noch einmal an: 🎜rrreee🎜Gleichzeitig sehen wir, dass das Ergebniscreate animal
tatsächlich ausgegeben wird Gleichzeitig werden Parameter übergeben, sodass unsere Ausgabeanweisung ausgeführt wird. Aber wo spiegelt sich der Satzthis.name=name
in unserem Funktionskörper wider? Dies ist der Satz:🎜rrreee🎜Nachdem wir den Funktionskörper ausgeführt haben, haben wir festgestellt, dass dername
-Wert des zurückgegebenen Objekts der Wert ist, den wirthis
zugewiesen haben, also ist dies nicht der Fall In diesem Prozess zeigt der Wert vonthis
auf das neu erstellte Objekt. Am Ende gibt es einen weiteren Absatz: 🎜rrreee🎜Das Objektanimal
ruft die Methode für den FunktionsprototypAnimal
auf und gibt an, dass sichAnimal
in < befindet code>animal code> Die Prototypenkette des Objekts. Auf welcher Ebene befindet es sich also? Überprüfen wir: 🎜rrreee🎜Dann wissen wir, dass der__proto__
vonanimal
direkt auf denprototype
vonAnimal
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"}复制代码Nach dem Login kopierenNach dem Login kopieren可以看到,直接无视了返回值,那我们返回一个对象试试:
function Animal(name){ this.name=name; return {}; }new Animal("test"); //{}复制代码Nach dem Login kopierenNach dem Login kopieren我们发现返回的实例对象被我们的返回值覆盖了,到这里大致了解了
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; }复制代码Nach dem Login kopierenNach dem Login kopieren上面是比较好理解的版本,我们可以简化一下得到下面这个版本:
function _new(fn, ...arg) { const obj = Object.create(fn.prototype); const res = fn.apply(obj, arg); return res instanceof Object ? res : obj;复制代码Nach dem Login kopierenNach dem Login kopieren大功告成!
总结
本文从定义出发,探索了
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!