手写new操作符,看完这篇就会了

440 阅读1分钟

定义

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

new constructor(arg1, arg2, /* …, */ argN)

普通函数与构造函数的区别

image.png

关于构造函数中返回值的表现,我们来验证一下:

  • 构造函数中返回非对象,则忽略该返回值
function A(name) {
  this.name = name;
  return '测试文本';
}
const a = new A('june');
console.log(a); // { name: 'june' }
console.log(a.name); // june
  • 构造函数中返回对象,该对象会成为整个表达式的结果
function A(name) {
  this.name = name;
  return { str: '这是一个测试文本' };
}
const a = new A('june');
console.log(a); // { str: '这是一个测试文本' }
console.log(a.name); // undefined

注意:箭头函数没有this,不可用于创建构造函数。

类构造函数

普通构造函数是可以直接作为函数直接调用的,那么这个构造函数内部的this会指向什么呢?window

function A(name) {
  console.log(this); // window
  this.name = name;
}
A('june');
console.log(window.name); // june

class 是 ES6 引入的一个语法糖,提供了一种更清晰、更面向对象的语法来创建对象和实现继承

class Person {
  constructor(name, age) {
      console.log(this);
    this.name = name;
    this.age = age;
  }
  greet() {
    console.log(`Hello, my name is ${this.name}`);
  }
}

Person('june', 18); 
// Uncaught TypeError: Class constructor Person cannot be invoked without 'new'

所以,调用类构造函数必须使用 new 操作符调用

const per = new Person('june', 18);
console.log(per); // { name: 'june', age: 18 }

new的作用

  • 创建一个空的简单 JavaScript 对象。为方便起见,我们称之为 newInstance

  • 如果构造函数的 prototype 属性是一个对象,则将 newInstance 的 [[Prototype]] 指向构造函数的 prototype 属性,否则 newInstance 将保持为一个普通对象,其 [[Prototype]] 为 Object.prototype

  • 使用给定参数执行构造函数,并将 newInstance 绑定为 this 的上下文(换句话说,在构造函数中的所有 this 引用都指向 newInstance,给这个新对象添加属性)。

  • 如果构造函数返回非原始值,则该返回值成为整个 new 表达式的结果。否则,如果构造函数未返回任何值或返回了一个原始值,则返回 newInstance。(通常构造函数不返回值,但可以选择返回值,以覆盖正常的对象创建过程)

手动实现 new

结合前面对new操作符的解析,我们来模拟一下

function myNew(constructor, ...args) { 
  // 创建空对象
  let obj = new Object();
    
  // 将构造函数的原型绑定到新创建的对象[[Prototype]]上
  obj.__proto__ = Object.create(constructor.prototype);
  // 或 Object.setPrototypeOf(obj, Constructor.prototype);
    
  // 调用构造函数
  let res = constructor.apply(obj,  args);
    
  // 判断返回值类型,若返回值为对象,它将作为返回值,否则返回新建的对象
  return res instanceof Object ? res : obj;
};

测试一下:

function Person (name, age){
  this.name = name;
  this.age = age;
}
Person.prototype.greet = function(){
  console.log(`Hello, my name is ${this.name}`); 
}

const per = myNew(Person, 'june', 18);

console.log(per.name); // june
console.log(per.age); // 18
console.log(per.greet); // Hello, my name is june

如果文章对你有帮助,请先点赞再收藏!

参考文章

mdn-new