1. 首页

【进阶13期】深度解析 new 原理及模拟实现

定义

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

举个栗子

function Car(color) {
    this.color = color;
}
Car.prototype.start = function() {
    console.log(this.color + " car start");
}

var car = new Car("black");
car.color; // 访问构造函数里的属性
// black,JS中文网 – 前端进阶资源分享 www.javascriptc.com

car.start(); // 访问原型里的属性
// black car start

可以看出 new 创建的实例有以下 2 个特性

  • 1、访问到构造函数里的属性
  • 2、访问到原型里的属性

注意点

ES6新增 symbol 类型,不可以使用 new Symbol(),因为 symbol 是基本数据类型,每个从Symbol()返回的 symbol 值都是唯一的。

Number("123"); // 123
String(123); // "123"
Boolean(123); // true
Symbol(123); // Symbol(123)

new Number("123"); // Number {123}
new String(123); // String {"123"}
new Boolean(true); // Boolean {true}
new Symbol(123); // Symbol is not a constructor
//JS中文网 – 前端进阶资源分享 www.javascriptc.com

模拟实现

当代码 new Foo(...) 执行时,会发生以下事情:

  1. 一个继承自 Foo.prototype 的新对象被创建。
  2. 使用指定的参数调用构造函数 Foo ,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
  3. 由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。

模拟实现第一步

new 是关键词,不可以直接覆盖。这里使用 create 来模拟实现 new 的效果。

new 返回一个新对象,通过 obj.proto = Con.prototype 继承构造函数的原型,同时通过 Con.apply(obj, arguments)调用父构造函数实现继承,获取构造函数上的属性(【进阶3-3期】)。

实现代码如下

// 第一版
function create() {
  // 创建一个空的对象
    var obj = new Object(),
  // 获得构造函数,arguments中去除第一个参数
    Con = [].shift.call(arguments);
  // 链接到原型,obj 可以访问到构造函数原型中的属性
    obj.__proto__ = Con.prototype;
  // 绑定 this 实现继承,obj 可以访问到构造函数中的属性
    Con.apply(obj, arguments);
  // 返回对象,JS中文网 – 前端进阶资源分享 www.javascriptc.com
    return obj;
};

测试一下

// 测试用例
function Car(color) {
    this.color = color;
}
Car.prototype.start = function() {
    console.log(this.color + " car start");
}

var car = create(Car, "black");
car.color;
// black

car.start();
// black car start

完美!

不熟悉 apply / call 的点击查看:【进阶3-3期】深度解析 call 和 apply 原理、使用场景及实现

不熟悉继承的点击查看:JavaScript常用八种继承方案

JS中文网 – 前端进阶资源分享 www.javascriptc.com

模拟实现第二步

上面的代码已经实现了 80%,现在继续优化。

构造函数返回值有如下三种情况:

  • 1、返回一个对象
  • 2、没有 return,即返回 undefined
  • 3、返回undefined 以外的基本类型

情况1:返回一个对象

function Car(color, name) {
    this.color = color;
    return {
        name: name
    }
}

var car = new Car("black", "BMW");
car.color;
// undefined

car.name;
// "BMW"

实例 car 中只能访问到返回对象中的属性

情况2:没有 return,即返回 undefined

function Car(color, name) {
    this.color = color;
}

var car = new Car("black", "BMW");
car.color;
// black,JS中文网 – 前端进阶资源分享 www.javascriptc.com

car.name;
// undefined

实例 car 中只能访问到构造函数中的属性,和情况1完全相反。

情况3:返回undefined 以外的基本类型

function Car(color, name) {
    this.color = color;
    return "new car";
}

var car = new Car("black", "BMW");
car.color;
// black

car.name;
// undefined

实例 car 中只能访问到构造函数中的属性,和情况1完全相反,结果相当于没有返回值。

所以需要判断下返回的值是不是一个对象,如果是对象则返回这个对象,不然返回新创建的 obj对象。

所以实现代码如下:

// 第二版
function create() {
  // 创建一个空的对象
    var obj = new Object(),
  // 获得构造函数,arguments中去除第一个参数
    Con = [].shift.call(arguments);
  // 链接到原型,obj 可以访问到构造函数原型中的属性
    obj.__proto__ = Con.prototype;
  // 绑定 this 实现继承,obj 可以访问到构造函数中的属性
    var ret = Con.apply(obj, arguments);
  // 优先返回构造函数返回的对象
  //JS中文网 – 前端进阶资源分享 www.javascriptc.com
  return ret instanceof Object ? ret : obj;
};

【进阶3-4期】思考题解

问题:用 JS 实现一个无限累加的函数 add,示例如下:

add(1); // 1
add(1)(2);  // 3
add(1)(2)(3); // 6
add(1)(2)(3)(4); // 10 

// 以此类推

实现:

function add(a) {
  function sum(b) { // 使用闭包
      a = a + b; // 累加
      return sum;
  }
  sum.toString = function() { // 重写toString()方法
        return a;
    }
  return sum; // 返回一个函数
}

add(1); // 1
add(1)(2);  // 3
add(1)(2)(3); // 6
add(1)(2)(3)(4); // 10 

我们知道打印函数时会自动调用 toString()方法,函数 add(a) 返回一个闭包 sum(b),函数 sum() 中累加计算 a = a + b,只需要重写sum.toString()方法返回变量 a 就OK了。

参考

JavaScript 深入之 new 的模拟实现
MDN 之 new 运算符
MDN 之 Symbol
javascript 函数 add(1)(2)(3)(4) 实现无限极累加

往期目录:

  1. JS深入之理解JavaScript 中的执行上下文和执行栈
  2. JS深入之执行上下文栈和变量对象
  3. JS深入之执行上下文栈和变量对象
  4. JS深入之带你走进内存机制
  5. JS深入之4类常见内存泄漏及如何避免
  6. JS深入浅出图解作用域链和闭包
  7. JS深入之从作用域链理解闭包
  8. JS深入之闭包面试题解
  9. JS深入之5种this绑定全面解析
  10. JS深入之重新认识箭头函数的this
  11. JS深入之深度解析 call 和 apply 原理
  12. JS深入之深度解析bind原理、使用场景及模拟实现

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文来源于网络,其版权属原作者所有,如有侵权,请与小编联系,谢谢!

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:【进阶13期】深度解析 new 原理及模拟实现

链接:https://www.javascriptc.com/453.html

原文链接:https://github.com/yygmind/blog/issues/24

« 【进阶14期】详细解析赋值、浅拷贝和深拷贝的区别
【进阶12期】深度解析bind原理、使用场景及模拟实现»
Flutter 中文教程资源

相关推荐

QR code