Home > Web Front-end > JS Tutorial > Detailed usage introduction of new operator

Detailed usage introduction of new operator

不言
Release: 2019-04-13 10:51:49
forward
2613 people have browsed it

This article brings you a detailed introduction to the usage of the new operator. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

I believe that many front-end partners who have just been exposed to the front-end or even those who have worked for several years still have a vague understanding of the new operator.

For example, I recently came into contact with a front-end partner who has been working for two years. He told me that new is used to create objects. It is understandable. Many people may answer this!

So is this answer wrong or right?

Let’s discuss this issue comprehensively:

There are many ways to get an object, the most common of which is object literals:

var obj = {}
Copy after login
Copy after login

But from a grammatical point of view, this is an assignment statement, which assigns the opposite literal value to the variable obj (this may not be very accurate, but in fact, an instance of an object is obtained here!!)

Many times, when we say we want to create an object, many friends touch the keyboard with both hands and type out this code in a few clicks.

As mentioned above, this sentence actually only obtains an instance of an object. So, can this code be equated with creating an object? Let's continue reading.

To get an instance of an object, another method that is equivalent to the object literal is the constructor:

var obj = new Object()
Copy after login
Copy after login

As soon as this code is typed out, I believe everyone will be impressed by what I just said There is no objection to the fact that obj is just an instance object! Then many friends will ask again: Isn’t this just a new object coming out! Yes, this is indeed the creation of a new object, because in JavaScript, everything is explained as an object, obj is an object, and it is obtained through the new operator, so many friends are sure of it Said: new is used to create objects!

It is not difficult to explain that many people confuse creating objects and instantiating objects!!

Let’s look at it differently: Since everything in js is an object, why do we need to create objects? ? It is an object in itself, how can we create one? So can we think of this as a kind of

inheritance

? Having said so much, I believe many partners have been stunned, but our purpose is one: to clarify that new is used for inheritance rather than the so-called creation of objects! !

What are the characteristics of the inherited instance object?

Access properties in the constructor
  1. Access properties on the prototype chain
  2. The following is a classic inheritance, use this code to warm up , the fun starts right away:
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()
Copy after login
Now let’s solve the first problem: How can we access the properties in the constructor? The answer is

call

or apply
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']
Copy after login
. Now that the first problem is solved, let’s solve the second one: How to access the properties on the prototype chain? The answer is __proto__Now let’s slightly transform the above warm-up code and create an instance without using 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
}
Copy after login

The New function in the above code is the new operator. Implementation

Main steps:

Create an empty object
  1. Get the first parameter of arguments
  2. Assign the prototype chain of the constructor to obj
  3. Use apply to change the this point of the constructor to point to the obj object. Afterwards, obj can access the properties in the constructor and the properties and methods on the prototype
  4. Return the obj object
  5. Maybe many friends see this and think that new does these things, but~~

However, we have overlooked one thing, the functions in js have return values. Even constructors are no exception.

If we return an object or a basic value in the constructor, what will happen to the New function above?

Let’s look at a piece of code again:

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()
Copy after login

executes the code and finds that only the two fields

name

and gender are output as expected, age , nation are undefined, say() reports an error. Change the code of the code constructor:

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

// ...
Copy after login

Execute the code and find that all fields are finally output as expected.

Here is a summary:

When the constructor returns a reference type, the properties in the constructor cannot be used, only the returned object can be used;
  1. When the constructor When a function returns a basic type, it is the same as when there is no return value, and the constructor is not affected.
  2. Now let’s consider how to change the New function to achieve the two functions summarized above? Continue reading:
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)
// ...
Copy after login

Execute this code and find that the two points summarized above have been achieved.

Solution: Use a variable to receive the return value of the constructor, then determine the return value type in the New function, and return different values ​​according to different types.

See here. Another friend said, New has been fully implemented now, right? ! ! The answer is definitely no. Let’s continue to look at a piece of code:

function Person(name, age) {
  this.name = name
  this.age = age
  this.gender = '男'
  
  // 返回引用类型
  // return {
  //   name: name,
  //   gender: '男'
  // }
  
  // 返回基本类型
  // return 1
  
  // 例外
  return null
}
Copy after login

Execute the code again and find that something went wrong again! ! !

So why does this problem occur?

Didn’t I just summarize that the constructor is not affected when returning the basic type, and null is the basic type?

At this moment, there are ten thousand grass and mud horses galloping in my heart! ! !

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

typeof null === 'object' // true
Copy after login

特例: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)
// ...
Copy after login

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

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

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

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

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

var obj = {}
Copy after login
Copy after login

实际上等价于

var obj = new Object()
Copy after login
Copy after login

前面说了,以上两段代码其实只是获取了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()
Copy after login

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

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

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

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()
Copy after login

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

Detailed usage introduction of new operator

从上图我们可以清除地看到,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()
Copy after login

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

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

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

The above is the detailed content of Detailed usage introduction of new operator. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:segmentfault.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template