Heim >Web-Frontend >js-Tutorial >Detaillierte Nutzungseinführung des neuen Betreibers
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
vorstellen? 继承
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:
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()
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:
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()
报错,这又是为什么呢?
从上图我们可以清除地看到,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操作符到底做了什么?
Constructor
;Constructor
的原型链结合Object.create
来创建
一个对象,此时新对象的原型链为Constructor
函数的原型对象;(结合我们上面讨论的,要访问原型链上面的属性和方法,要使用实例对象的__proto__属性)Constructor
函数的this指向,指向新创建的实例对象,然后call
方法再调用Constructor
函数,为新对象赋予属性和方法;(结合我们上面讨论的,要访问构造函数的属性和方法,要使用call或apply)Constructor
函数的一个实例对象。现在我,我们来回答文章开始时提出的问题,new是用来创建对象的吗?
现在我们可以勇敢的回答,new是用来做继承的,而创建对象的其实是Object.create(null)。
在new操作符的作用下,我们使用新创建的对象去继承了他的构造函数上的属性和方法、以及他的原型链上的属性和方法!
写在最后:
补充一点关于原型链的知识:
- JavaScript中的函数也是对象,而且对象除了使用字面量定义外,都需要通过函数来创建对象
- prototype属性可以给函数和对象添加可共享(继承)的方法、属性,而__proto__是查找某函数或对象的原型链方式
- prototype和__proto__都指向原型对象
- 任意一个函数(包括构造函数)都有一个prototype属性,指向该函数的原型对象
- 任意一个实例化的对象,都有一个__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!