likes
comments
collection
share

JavaScript new 篇

作者站长头像
站长
· 阅读数 28

前言

本文将对 JavaScript 语言中的 new 操作符进行全面讲解,从其使用方法、内部原理以及运算优先级等方面展开阐述。

定义及用法

定义

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。

语法

new constructor[([arguments])]

  • constructor: 构造器函数
  • arguments:用于被 constructor 调用的参数列表

例如:

function Person(name) {
  this.name = name;
}

const person = new Person('Jerry');
person.name; // Jerry

需要注意的是:如果你没有使用 new 运算符,构造函数会像其他的普通函数一样被调用,并不会创建一个对象。在这种情况下,this 的指向也是不一样的。

另外我们需要知道在使用 new 操作符的时候如果不需要传参,是可以省略掉 () 的,但是这样有时候会出现问题,我们会在后面的操作符优先级中具体讨论。

下面我们来探究以下 new 操作符的原理,为什么可以创建一个实例对象。

内部原理

new 关键字会进行如下的操作:

  1. 创建一个空的简单的 javascript 对象,即{}
  2. 设置该对象的 __proto__ 属性为构造函数的原型对象。
  3. this 指向该空对象并且执行函数体。
  4. 若函数没有返回对象,则返回 this

对于第一步我们不需要验证,我们可以来验证剩下的几个步骤:

function Person(name) {
  this.name = name;
}

const person = new Person('Jerry');
console.log(person);
console.log(person.__proto__ === Person.prototype);

浏览器中执行结果如下:

JavaScript new 篇

这样我们就验证了第二步。其实第三步也同时验证了,正因为执行了函数体,所以新创建的实例对象身上才有 name 属性值为 Jerry

对于第四步,我们可以举个反例,我们显式的手动返回一个对象类型的数据:

function Person(name) {
  this.name = name;
  return {
    age: 18
  };
}

const person = new Person('Jerry');
console.log(person);

可以看执行结果为返回的对象,并非 this

JavaScript new 篇

其实只要返回一个应用类型的值都会返回该值而非 this ,例如我们返回一个函数或者数组

function Person(name) {
  this.name = name;
  return [1, 2, 3];
}

const person = new Person('Jerry');
console.log(person); // [1, 2, 3]

但是如果我们返回一个基本类型的值呢?

function Person(name) {
  this.name = name;
  return 123;
}

const person = new Person('Jerry');
console.log(person); // {name: 'Jerry'}

这个时候返回的就是 this ,因为函数内部没有返回一个对象。

手写 new

在了解了 new 的基本使用及其内部的原理后,我们可以手写一个类似于 new 运算符的 _new 函数:

function myNew(constructor, ...args) {
  const obj = {};
  obj.__proto__ = constructor.prototype;
  const result = constructor.apply(obj, args);
  return result instanceof Object ? result : obj;
}

解释一下上面的代码:

  1. 创建一个空对象 obj
  2. obj__proto__ 属性指向构造函数的 prototype
  3. 使用 call 或者 apply 绑定 this 并执行函数体。
  4. 若函数没有返回对象就返回 obj,否则返回函数返回的内容。

运算符优先级

通过上面的讲解,你应该对 new 运算符有了很深层面的认识。但是对于它的运算优先级,同样是我们要必须掌握的一个内容。

对于 new 运算符的优先级问题我们只需要搞清楚它和 . 运算符之间的优先级问题

我们来看一种场景:

function Foo() {
  console.log(1);
  return this;
}
Foo.getName = function() {
  console.log(2);
};
Foo.prototype.getName = function() {
  console.log(3);
};

new Foo.getName();
new Foo().getName();

上面的代码其实就涉及到了 new 运算符与 . 运算符的优先级问题。

我们需要知道:

  • new 操作符带括号时. 操作符的优先级平级
  • new 操作符无括号时优先级小于 . 操作符的优先级。

知道了这两个特性后,上面代码的执行逻辑就很清楚了:

  • 执行 new Foo.getName() 时,先访问 Foo.getName,然后 new 这个函数,即执行结果打印 2
  • 执行 new Foo().getName() 时,先执行 new Foo(),返回 Foo 的实例对象,同时打印 1。接着调用实例对象上的 getName() 方法,打印 3

所以,最终打印结果为:213

JavaScript new 篇

总结

在我们学习一个单独的知识点的时候尽量的了解其基本使用后去了解其内部实现的原理,尝试自己封装。这将有利于培养我们造轮子的能力,并且可以巩固知识点。最后也要自己主动去发现关于该知识点使用时可能会遇到的问题。

转载自:https://juejin.cn/post/6910474617391939592
评论
请登录