JS的new做了什么?

146 阅读3分钟

在日常的开发中经常会用到new操作符,那么它到底做了哪些事情呢?让我们一起来学习一下new具体干了什么。

new做了什么?

假设我们开发一款打怪兽游戏,玩家可以打一堆怪兽,那么我们应该制造一堆怪兽。 一只怪兽有以下属性:

攻击力

生命值

行走

奔跑

死亡

攻击

防御

那么,我们可以这样制造一只怪兽

var 怪兽 = {
  ID: 1, // 用于区分每只怪兽
  攻击力: 10,
  生命值: 99,
  行走: function () {},
  奔跑: function () {},
  死亡: function () {},
  攻击: function () {},
  防御: function () {}
}

怪兽制造局.制造(怪兽)

那么,如何制造100只怪兽呢?可以这样子批量制造:

var 怪兽们 = []
for (let i = 0; i < 100; i++) {
  var 怪兽 = {
    ID: i + 1, // 用于区分每只怪兽
    攻击力: 10,
    生命值: 99,
    行走: function () {},
    奔跑: function () {},
    死亡: function () {},
    攻击: function () {},
    防御: function () {}
  }
  怪兽们.push(怪兽)
}
怪兽制造局.批量制造(怪兽们)

但是这样子批量制造存在一个缺点:浪费内存。攻击力、行走、奔跑等每个怪兽都是一样的,没必要创建100次。只有ID和生命值每只怪兽是不一样的,才需要创造100次。 有人想到了用原型去解决这个问题:

怪兽.原型 = {
  攻击力: 10,
  行走: function () {},
  奔跑: function () {},
  死亡: function () {},
  攻击: function () {},
  防御: function () {}
}

function 怪兽(ID) {
  var 临时对象 = {} // JS帮我们创建临时对象
  临时对象.__proto__ = 怪兽.原型 // JS帮我们绑定原型
  临时对象.ID = ID
  临时对象.生命值 = 42
  return 临时对象 // JS帮我们返回临时对象
}

// 然后就可以愉快地制造怪兽了
var 怪兽们 = []
for(var i = 0; i<100; i++) {
    怪兽们.push(怪兽(i+1))
}

怪兽制造局.批量制造(怪兽们)

看到这里有人会质疑了,讲了这么一大堆,这跟new有啥关系? 其实new关键字就是替我们实现了这个(怪兽函数)过程

  1. 创建一个临时对象
  2. 绑定原型:将临时对象的__proto__(实际名字不一定叫这个,不同浏览器不一样)指向构造函数的prototype属性,这一步其实是为了绑定共有属性,比如:行走
  3. 改变this指向,将this指向临时对象
  4. 执行构造函数,这一步其实是为了绑定私有属性,比如:怪兽的ID
  5. 返回临时对象
    • 一般是返回临时对象;
    • 但是当 构造函数有返回值时 则需要做判断再返回对应的值,是 对象类型则返回该对象,是 原始类型则返回第一步创建的空对象

用new重写:

怪兽.原型 = {
  攻击力: 10,
  行走: function () {},
  奔跑: function () {},
  死亡: function () {},
  攻击: function () {},
  防御: function () {}
}

function 怪兽(ID) {
  this.ID = ID
  this.生命值 = 42
}

// 然后就可以愉快地制造怪兽了
var 怪兽们 = []
for(var i = 0; i<100; i++) {
    怪兽们.push(new 怪兽(i+1))
}

怪兽制造局.批量制造(怪兽们)

new其实是帮我们省了代码,也就是所谓的语法糖。

实现一个new

掌握了new的原理,那么实现起来也就很简单了:

function myNew(Con, ...args) {
  // 创建一个临时对象
  let obj = {};
  // 将这个空对象的__proto__指向构造函数的原型
  // obj.__proto__ = Con.prototype;
  Object.setPrototypeOf(obj, Con.prototype);
  // 将this指向空对象,并且执行了构造函数
  let res = Con.apply(obj, args);
  // 对构造函数返回值做判断,然后返回对应的值
  return res instanceof Object ? res : obj;
}

完。