Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Nutzungseinführung des neuen Betreibers

Detaillierte Nutzungseinführung des neuen Betreibers

不言
不言nach vorne
2019-04-13 10:51:492545Durchsuche

Dieser Artikel bietet Ihnen eine detaillierte Einführung in die Verwendung des neuen Operators. Er hat einen gewissen Referenzwert. Ich hoffe, er wird Ihnen hilfreich sein.

Ich glaube, dass viele Front-End-Partner, die gerade erst mit dem Front-End in Kontakt gekommen sind oder sogar mehrere Jahre gearbeitet haben, immer noch ein vages Verständnis für den neuen Betreiber haben.

Zum Beispiel bin ich kürzlich mit einem Front-End-Partner in Kontakt gekommen, der mir gesagt hat, dass daran nichts auszusetzen ist Das!

Ist diese Antwort also falsch oder richtig?

Lassen Sie uns dieses Thema ausführlich besprechen:

Es gibt viele Möglichkeiten, ein Objekt zu erhalten, die häufigste davon sind Objektliterale:

var obj = {}

Aber aus grammatikalischer Sicht Ansicht, dies ist eine Zuweisungsanweisung, die der Variablen obj den entgegengesetzten Literalwert zuweist (das ist vielleicht nicht sehr genau, aber tatsächlich wird hier eine Instanz eines Objekts erhalten!!)

Oftmals Wenn wir sagen, dass wir ein Objekt erstellen möchten, berühren viele Freunde die Tastatur mit beiden Händen und geben diesen Code mit wenigen Klicks ein.

Wie oben erwähnt, erhält dieser Satz tatsächlich nur eine Instanz eines Objekts. Kann dieser Code also mit der Erstellung eines Objekts gleichgesetzt werden? Schauen wir weiter nach unten.

Um eine Instanz eines Objekts zu erhalten, ist eine andere Methode, die einem Objektliteral entspricht, der Konstruktor:

var obj = new Object()

Sobald dieser Code eingegeben ist, werden meiner Meinung nach alle beeindruckt sein Was ich gerade gesagt habe: Es gibt nichts dagegen einzuwenden, dass obj nur ein Instanzobjekt ist! Dann werden sich viele Freunde wieder fragen: Ist das nicht nur ein neuer Partner? new

Ja, das ist tatsächlich ein neues Objekt, denn in JavaScript ist obj ein Objekt und wird über den neuen Operator abgerufen, sodass viele Freunde sich dessen sicher sind. Sagen Sie: new wird zum Erstellen von Objekten verwendet!

Es ist nicht schwer zu erklären, dass viele Leute das Erstellen von Objekten und das Instanziieren von Objekten verwechseln!!

Betrachten wir es anders: Da alles in js ein Objekt ist, warum müssen wir dann Objekte erstellen? ? Es ist ein Objekt für sich. Wie können wir eines erstellen? Können wir uns das also als

vorstellen? 继承

Nachdem ich so viel gesagt habe, glaube ich, dass viele Partner davon verblüfft waren, aber unser Ziel ist eines: klarzustellen, dass Neues zur Vererbung und nicht zur sogenannten Erstellung von Objekten verwendet wird! !

Was sind die Merkmale des geerbten Instanzobjekts?

    Zugriff auf Eigenschaften im Konstruktor
  1. Zugriff auf Eigenschaften in der Prototypkette
Das Folgende ist eine klassische Vererbung, wärmen Sie sich mit diesem Code auf, der Spaß macht fängt gleich an:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = '男'
}

Person.prototype.nation = '汉'

Person.prototype.say = function() {
  console.log(`My name is ${this.age}`)
}

var person = new Person('小明', 25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
console.log(person.nation)

person.say()
Jetzt lösen wir das erste Problem: Wie können wir auf die Eigenschaften im Konstruktor zugreifen? Die Antwort lautet oder call
function Parent() {
  this.name = ['A', 'B']
}

function Child() {
  Parent.call(this)
}

var child = new Child()
console.log(child.name) // ['A', 'B']

child.name.push('C')
console.log(child.name) // ['A', 'B', 'C']
. Nachdem das erste Problem gelöst ist, lösen wir das zweite: Wie greife ich auf die Eigenschaften in der Prototypenkette zu? Die Antwort ist apply__proto__Jetzt modifizieren wir den Aufwärmcode oben leicht und erstellen eine Instanz ohne die Verwendung von „new“:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = '男'
}

Person.prototype.nation = '汉'

Person.prototype.say = function() {
  console.log(`My name is ${this.age}`)
}

// var person = new Person('小明', 25)
var person = New(Person, '小明', 25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
console.log(person.nation)

person.say()

function New() {
  var obj = {}
  Constructor = [].shift.call(arguments) // 获取arguments第一个参数:构造函数
  // 注意:此时的arguments参数在shift()方法的截取后只剩下两个元素
  obj.__proto__ = Constructor.prototype // 把构造函数的原型赋值给obj对象
  Constructor.apply(obj, arguments) // 改变够着函数指针,指向obj,这是刚才上面说到的访问构造函数里面的属性和方法的方式
  return obj
}
Die New-Funktion im obigen Code ist die Implementierung des neuen Operators

Hauptschritte:

    Ein leeres Objekt erstellen
  1. Ersten Parameter der Argumente abrufen
  2. Die Prototypenkette des Konstruktors obj zuweisen
  3. Verwenden Sie apply, um den this-Punkt des Konstruktors so zu ändern, dass er auf das obj-Objekt zeigt. Anschließend kann obj auf die Eigenschaften im Konstruktor und auf die Eigenschaften und Methoden des Prototyps zugreifen
  4. Das obj-Objekt zurückgeben
Vielleicht sehen das viele Freunde und denken, dass new diese Dinge tut.

Eines haben wir jedoch übersehen. Funktionen in js haben keine Ausnahmen.

Was passiert mit der New-Funktion oben, wenn wir im Konstruktor ein Objekt oder einen Basiswert zurückgeben?

Schauen wir uns noch einmal einen Code an:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = '男'
  
  return {
    name: name,
    gender: '男'
  }
}

Person.prototype.nation = '汉'

Person.prototype.say = function() {
  console.log(`My name is ${this.age}`)
}

var person = new Person('小明', 25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
console.log(person.nation)

person.say()
führte den Code aus und stellte fest, dass nur die beiden Felder

und name wie erwartet ausgegeben wurden, gender und age waren undefiniert und nation Einen Fehler melden. say()

Ändern Sie den Code des Codekonstruktors:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = '男'
  
  // return {
  //   name: name,
  //   gender: '男'
  // }
  return 1
}

// ...
Führen Sie den Code aus und stellen Sie fest, dass alle Felder schließlich wie erwartet ausgegeben werden.

Hier ist eine Zusammenfassung:

    Wenn der Konstruktor einen Referenztyp zurückgibt, können die Eigenschaften im Konstruktor nicht verwendet werden, sondern nur das zurückgegebene Objekt
  1. Wenn der Konstruktor Wenn eine Funktion einen Basistyp zurückgibt, ist dies dasselbe wie wenn kein Rückgabewert vorhanden ist und der Konstruktor nicht betroffen ist.
Überlegen wir nun, wie wir die Funktion „Neu“ ändern können, um die beiden oben zusammengefassten Funktionen zu erreichen? Lesen Sie weiter:

function Person(name, age) {
  // ...
}

function New() {
  var obj = {}
  Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  
  // return obj
  return typeof result === 'object' ? result : obj
}

var person = New(Person, '小明', 25)

console.log(person.name)
// ...
Führen Sie diesen Code aus und stellen Sie fest, dass die beiden oben zusammengefassten Punkte erreicht wurden.

Lösung: Verwenden Sie eine Variable, um den Rückgabewert des Konstruktors zu empfangen, bestimmen Sie dann den Rückgabewerttyp in der neuen Funktion und geben Sie je nach Typ unterschiedliche Werte zurück.

Siehe hier. Ein anderer Freund sagte: „Neu ist jetzt vollständig implementiert, oder?“ ! ! Die Antwort ist definitiv nein. Schauen wir uns weiter einen Codeabschnitt an:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = '男'
  
  // 返回引用类型
  // return {
  //   name: name,
  //   gender: '男'
  // }
  
  // 返回基本类型
  // return 1
  
  // 例外
  return null
}
Führen Sie den Code erneut aus und stellen Sie fest, dass erneut ein Fehler aufgetreten ist! ! !

Warum tritt dann dieses Problem auf?

Habe ich nicht gerade zusammengefasst, dass der Konstruktor bei der Rückgabe des Basistyps nicht betroffen ist und null der Basistyp ist?

In diesem Moment galoppieren zehntausend Gras- und Schlammpferde in meinem Herzen! ! !

解惑:null是基本类型没错,但是使用操作符typeof后我们不难发现:

typeof null === 'object' // true

特例:typeof null返回为'object',因为特殊值null被认为是一个空的对象引用

明白了这一点,那问题就好解决了:

function Person(name, age) {
  // ...
}

function New() {
  var obj = {}
  Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  // return obj
  // return typeof result === 'object' ? result : obj
  return typeof result === 'object' ? result || obj : obj
}

var person = New(Person, '小明', 25)

console.log(person.name)
// ...

解决方案:判断一下构造函数返回值result,如果result是一个引用(引用类型和null),就返回result,但如果此时result为false(null),就使用操作符||之后的obj

好了,到现在应该又有小伙伴发问了,这下New函数是彻彻底底实现了吧!!!

答案是,离完成不远了!!

别急,在功能上,New函数基本完成了,但是在代码严谨度上,我们还需要做一点工作,继续往下看:

这里,我们在文章开篇做的铺垫要派上用场了:

var obj = {}

实际上等价于

var obj = new Object()

前面说了,以上两段代码其实只是获取了object对象的一个实例。再者,我们本来就是要实现new,但是我们在实现new的过程中却使用了new

这个问题把我们引入到了到底是先有鸡还是先有蛋的问题上!

这里,我们就要考虑到ECMAScript底层的API了————Object.create(null)

这句代码的意思才是真真切切地创建了一个对象!!

function Person(name, age) {
  // ...
}

function New() {
  // var obj = {}
  // var obj = new Object()
  var obj = Object.create(null)
  Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  // return obj
  // return typeof result === 'object' ? result : obj
  return typeof result === 'object' ? result || obj : obj
}

var person = New(Person, '小明', 25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
// 这样改了之后,以下两句先注释掉,原因后面再讨论
// console.log(person.nation)
// person.say()

好了好了,小伙伴常常舒了一口气,这样总算完成了!!

但是,这样写,新的问题又来了。

小伙伴:啥?还有完没完?

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = '男'
}

Person.prototype.nation = '汉'

Person.prototype.say = function() {
  console.log(`My name is ${this.age}`)
}

function New() {
  // var obj = {}
  // var obj = new Object()
  var obj = Object.create(null)
  Constructor = [].shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  // return obj
  // return typeof result === 'object' ? result : obj
  return typeof result === 'object' ? result || obj : obj
}

var person = New(Person, '小明', 25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
// 这里解开刚才的注释
console.log(person.nation)
person.say()

别急,我们执行一下修改后的代码,发现原型链上的属性nation和方法say()报错,这又是为什么呢?

Detaillierte Nutzungseinführung des neuen Betreibers

从上图我们可以清除地看到,Object.create(null)创建的对象是没有原型链的,而后两个对象则是拥有__proto__属性,拥有原型链,这也证明了后两个对象是通过继承得来的。

那既然通过Object.create(null)创建的对象没有原型链(原型链断了),那我们在创建对象的时候把原型链加上不就行了,那怎么加呢?

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = '男'
}

Person.prototype.nation = '汉'

Person.prototype.say = function() {
  console.log(`My name is ${this.age}`)
}

function New() {
  Constructor = [].shift.call(arguments)
  
  // var obj = {}
  // var obj = new Object()
  // var obj = Object.create(null)
  var obj = Object.create(Constructor.prototype)
  
  // obj.__proto__ = Constructor.prototype
  // Constructor.apply(obj, arguments)
  var result = Constructor.apply(obj, arguments)
  // return obj
  // return typeof result === 'object' ? result : obj
  return typeof result === 'object' ? result || obj : obj
}

var person = New(Person, '小明', 25)

console.log(person.name)
console.log(person.age)
console.log(person.gender)
console.log(person.nation)

person.say()

这样创建的对象就拥有了它初始的原型链了,这个原型链是我们传进来的构造函数赋予它的。

也就是说,我们在创建新对象的时候,就为它指定了原型链了,新创建的对象继承自传进来的构造函数!

现在,我们来梳理下最终的New函数做了什么事,也就是本文讨论的结果————new操作符到底做了什么?

  1. 获取实参中的第一个参数(构造函数),就是调用New函数传进来的第一个参数,暂时记为Constructor
  2. 使用Constructor的原型链结合Object.create创建一个对象,此时新对象的原型链为Constructor函数的原型对象;(结合我们上面讨论的,要访问原型链上面的属性和方法,要使用实例对象的__proto__属性)
  3. 改变Constructor函数的this指向,指向新创建的实例对象,然后call方法再调用Constructor函数,为新对象赋予属性和方法;(结合我们上面讨论的,要访问构造函数的属性和方法,要使用call或apply)
  4. 返回新创建的对象,为Constructor函数的一个实例对象。

现在我,我们来回答文章开始时提出的问题,new是用来创建对象的吗?

现在我们可以勇敢的回答,new是用来做继承的,而创建对象的其实是Object.create(null)。
在new操作符的作用下,我们使用新创建的对象去继承了他的构造函数上的属性和方法、以及他的原型链上的属性和方法!

写在最后:

补充一点关于原型链的知识:

  1. JavaScript中的函数也是对象,而且对象除了使用字面量定义外,都需要通过函数来创建对象
  2. prototype属性可以给函数和对象添加可共享(继承)的方法、属性,而__proto__是查找某函数或对象的原型链方式
  3. prototype和__proto__都指向原型对象
  4. 任意一个函数(包括构造函数)都有一个prototype属性,指向该函数的原型对象
  5. 任意一个实例化的对象,都有一个__proto__属性,指向构造函数的原型对象。

Das obige ist der detaillierte Inhalt vonDetaillierte Nutzungseinführung des neuen Betreibers. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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