Cet article vous apporte une introduction détaillée à l'utilisation du nouvel opérateur. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il vous sera utile.
Je crois que de nombreux partenaires front-end qui viennent d'être exposés au front-end ou même ceux qui travaillent depuis plusieurs années ont encore une vague compréhension du nouvel opérateur, qui est relativement vague.
Par exemple, j'ai récemment rencontré un partenaire front-end qui travaille depuis deux ans. Il m'a dit que new était utilisé pour créer des objets. C'est compréhensible que beaucoup de gens puissent répondre à cela !
Alors, cette réponse est-elle fausse ou juste ?
Discutons de ce problème en détail :
Il existe de nombreuses façons d'obtenir un objet, la plus courante étant les littéraux d'objet :
var obj = {}
Mais d'un point de vue grammatical d'un point de vue, il s'agit d'une instruction d'affectation, qui attribue la valeur littérale opposée à la variable obj (cela n'est peut-être pas très précis, mais en fait, une instance d'un objet est obtenue ici !!)
Beaucoup Parfois, lorsque nous disons que nous voulons créer un objet, de nombreux amis touchent le clavier à deux mains et tapent ce code en quelques clics.
Comme mentionné ci-dessus, cette phrase n'obtient en fait qu'une instance d'un objet. Alors, ce code peut-il être assimilé à la création d'un objet ? Continuons à regarder vers le bas.
Pour obtenir une instance d'un objet, une autre méthode équivalente à un littéral d'objet est le constructeur :
var obj = new Object()
Dès que ce code sera tapé, je pense que tout le monde comprendra je viens de dire que obj
n'est qu'un objet instance, donc il n'y a pas d'objection ! Alors de nombreux amis se demanderont à nouveau : n’est-ce pas juste un nouveau partenaire ? 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) console.log(person.name) console.log(person.age) console.log(person.gender) console.log(person.nation) person.say()
call
apply
Le premier problème est résolu, alors résolvons le second : Comment accéder aux propriétés sur la chaîne de prototypes ? La réponse est 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']
__proto__
Maintenant, modifions légèrement le code d'échauffement ci-dessus et créons une instance sans utiliser 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 }
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()
et name
sont affichés comme prévu, et gender
et age
ne sont pas définis nation
Signaler une erreur. say()
function Person(name, age) { this.name = name this.age = age this.gender = '男' // return { // name: name, // gender: '男' // } return 1 } // ...
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) // ...
Solution : utilisez une variable pour recevoir la valeur de retour du constructeur, puis déterminez le type de valeur de retour dans la fonction New et renvoyez différentes valeurs selon différents types.
Voir ici. Un autre ami a dit : « Nouveau a été entièrement mis en œuvre maintenant, n'est-ce pas ? ! ! La réponse est définitivement non. Continuons à examiner un morceau de code :
Exécutez à nouveau le code et constatez que quelque chose s'est encore mal passé ! ! !function Person(name, age) { this.name = name this.age = age this.gender = '男' // 返回引用类型 // return { // name: name, // gender: '男' // } // 返回基本类型 // return 1 // 例外 return null }
Alors pourquoi ce problème se produit-il ?
Ne viens-je pas de résumer que le constructeur n'est pas affecté lors du retour du type de base et que null est le type de base ?
En ce moment, il y a dix mille chevaux d'herbe et de boue qui galopent dans mon cœur ! ! !
解惑: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__属性,指向构造函数的原型对象。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!