Maison > interface Web > js tutoriel > Introduction détaillée de l'utilisation du nouvel opérateur

Introduction détaillée de l'utilisation du nouvel opérateur

不言
Libérer: 2019-04-13 10:51:49
avant
2607 Les gens l'ont consulté

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 = {}
Copier après la connexion
Copier après la connexion

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()
Copier après la connexion
Copier après la connexion

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

Oui, c'est bien un nouvel objet, car en JavaScript, obj est un objet, et il s'obtient via l'opérateur new, donc beaucoup d'amis en sont sûrs. Dit : new est utilisé pour créer des objets !

Il n'est pas difficile d'expliquer que beaucoup de gens confondent créer des objets et instancier des objets !!

Regardons les choses différemment : puisque tout en js est un objet, pourquoi avons-nous besoin de créer des objets ? C'est un objet en soi, comment peut-on en créer un ? Alors pouvons-nous considérer cela comme un

 ? 继承

Cela dit, je crois que de nombreux partenaires en ont été stupéfaits, mais notre objectif est un : clarifier que new est utilisé pour l'héritage plutôt que pour la soi-disant création d'objets ! !

Quelles sont les caractéristiques de l'objet instance hérité ?

    Accéder aux propriétés dans le constructeur
  1. Accéder aux propriétés sur la chaîne de prototypes
Ce qui suit est un héritage classique, échauffez-vous avec ce code, le plaisir commence tout de suite :

Résolvons maintenant la première question : Comment pouvons-nous accéder aux propriétés dans le constructeur ? La réponse est
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()
Copier après la connexion
ou callapply 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']
Copier après la connexion
__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
}
Copier après la connexion
La fonction New dans le code ci-dessus est l'implémentation de l'opérateur new

Étapes principales :

    Créer un objet vide
  1. Récupérer le premier paramètre des arguments
  2. Attribuer la chaîne de prototypes du constructeur à obj
  3. Utilisez apply pour changer le point this du constructeur pour qu'il pointe vers l'objet obj. Ensuite, obj peut accéder aux propriétés du constructeur et aux propriétés et méthodes du prototype
  4. Renvoyer l'objet obj.
Peut-être que beaucoup d'amis voient cela et pensent que new fait exactement ces choses. Cependant~~

Cependant, nous avons oublié une chose. Les fonctions en js ont des valeurs de retour, même si elles. sont construites. Les fonctions ne font pas exception.

Qu'arrive-t-il à la fonction New ci-dessus si nous renvoyons un objet ou une valeur de base dans le constructeur ?

Regardons à nouveau un morceau de code :

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()
Copier après la connexion
exécute le code et constate que seuls les deux champs

et name sont affichés comme prévu, et gender et age ne sont pas définis nationSignaler une erreur. say()

Modifiez le code du constructeur de code :

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

// ...
Copier après la connexion
Exécutez le code et constatez que tous les champs sont finalement affichés comme prévu.

Voici un résumé :

    Lorsque le constructeur renvoie un type référence, les propriétés du constructeur ne peuvent pas être utilisées, seul l'objet renvoyé peut être utilisé
  1. Quand le constructeur Lorsqu'une fonction renvoie un type de base, c'est la même chose que lorsqu'il n'y a pas de valeur de retour, et le constructeur n'est pas affecté.
  2. Voyons maintenant comment modifier la fonction Nouveau pour obtenir les deux fonctions résumées ci-dessus ? Continuer la lecture :

Exécutez ce code et constatez que les deux points résumés ci-dessus ont été atteints.
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)
// ...
Copier après la connexion

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
}
Copier après la connexion

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
Copier après la connexion

特例: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)
// ...
Copier après la connexion

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

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

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

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

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

var obj = {}
Copier après la connexion
Copier après la connexion

实际上等价于

var obj = new Object()
Copier après la connexion
Copier après la connexion

前面说了,以上两段代码其实只是获取了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()
Copier après la connexion

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

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

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

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()
Copier après la connexion

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

Introduction détaillée de lutilisation du nouvel opérateur

从上图我们可以清除地看到,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()
Copier après la connexion

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

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

现在,我们来梳理下最终的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__属性,指向构造函数的原型对象。

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!

Étiquettes associées:
source:segmentfault.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal