JavaScript 中 new 操作符的原理与手写实现

JavaScript 中 new 操作符的原理与手写实现详解

在 JavaScript 的编程世界里,new操作符是构建对象实例的核心工具。当我们写下new Person()这样简洁的代码时,其背后究竟隐藏着怎样精密的运作流程?接下来,就让我们深入剖析new操作符的工作原理,并一步步实现一个具备相似功能的自定义函数。

一、new 操作符如何创建对象

创建空对象

new操作符被调用时,JavaScript 引擎会立刻生成一个空的对象,形式上表现为{} 。这个空对象就像一座刚刚奠基的建筑,后续构造函数中的属性与方法会如同砖瓦一般,逐步为其增添功能与特性,它是最终实例对象的初始形态。例如,当执行new Car()时,引擎会率先创建一个空对象,为后续定义汽车的属性(如颜色、品牌)和方法(如启动、加速)做好准备。
2.

绑定原型链

新创建的空对象会与构造函数的原型对象建立起紧密的联系。具体而言,新对象的__proto__属性会被指向构造函数的prototype原型对象,从而构建起一条至关重要的原型链 。这条原型链是 JavaScript 实现继承机制的关键,它使得实例对象能够访问原型对象上定义的属性和方法。以Person构造函数为例,若在其原型上定义sayHello方法:

javascript 复制代码
function Person() {}
Person.prototype.sayHello = function() {
 	console.log("Hello!");
};
javascript 复制代码
const p1 = new Person() // p1是Person的实例
p1.sayHello()

当使用new Person()创建实例时,该实例便能调用sayHello方法,正是原型链发挥了作用,让实例拥有了从原型继承而来的能力。
3.

绑定 this 指向

在执行构造函数之前,JavaScript 会将构造函数内部的this关键字精准地指向新创建的空对象 。这一操作意义非凡,构造函数中所有对this的操作,无论是为对象添加属性,还是定义方法,都将围绕这个新对象展开。例如在Person构造函数中使用this.name = "Bob",实际上就是在为新创建的对象赋予name属性,并赋值为"Bob",塑造对象的个性化特征。

javascript 复制代码
function Person(name) {
  // 经过new之后 此时 this 已经指向了一个新的空对象 {} 
  this.name = name; // 给这个新对象添加属性 name
}

const p1 = new Person("Bob");
console.log(p1.name); // 输出 "Bob"

执行构造函数

构造函数内部的代码执行,初始化新对象 。由于构造函数的this指向的新对象,所以可以根据传入的参数,为新对象设置不同的属性值,定义专属的方法。比如,我们可以通过参数传递来初始化Person对象的姓名和年龄:

javascript 复制代码
function Person(name, age) {
	this.name = name;
	this.age = age;
}
const bob = new Person("Bob", 30);

​ 通过这种方式,每个新创建的Person对象都能拥有独特的属性值,展现出不同的状态。

返回实例对象

构造函数执行结束后,会涉及到返回值的处理,这一步决定了最终返回的对象形态。这里存在两种情况:

  • 返回值为对象 :若构造函数显式返回一个对象(例如return { job: "Engineer" }),new操作符会尊重这个返回值,直接返回该对象,之前创建的空对象将被舍弃。这赋予了构造函数极大的灵活性,开发者可以根据需求控制最终返回的实例形态。

    javascript 复制代码
    function Person(name,age){
        this.name = name
        this.age = age
        return {
            lable:'lable'
    	}
    }
    const p1 = new Person()
    console.log(p1 instanceof Person) // false
    console.log(p1)  // { lable: 'lable' }
  • 返回值为基本数据类型 :当构造函数返回基本数据类型(如return 10return "string")时,new操作符会无视这个返回值,仍然返回最初创建的新对象。这是因为new操作符的核心目标是创建对象实例,基本数据类型的返回值与这一目标不符。

    javascript 复制代码
    function Person(name, age) {
      this.name = name;
      this.age = age;
      return 1
    }
    
    Person.prototype.say = function () {
      console.log("name", this.name, "age", this.age);
    };
    
    const p = new Person("张三", 18);
    console.log(p instanceof Person) // true
    console.log(p); //Person { name: '张三', age: 18 }

二、手写实现 new 功能

  1. 基于对new操作符原理的理解,我们可以动手编写一个函数,模拟new的功能。以下是具体的实现代码:

    javascript 复制代码
    function objectFactory() {
      //创建空对象
      let obj = {};
    
      // 获取argument中第一个参数  由于argument没有shift方法 这里将this指向argument
      let Constructor = [].shift.call(arguments);
    
      //绑定原型链
      obj.__proto__ = Constructor.prototype;
        
       // 绑定this指向  调用apply 让Constructor的this指向obj
      let result = Constructor.apply(obj, arguments); // 执行构造函数this 指向obj
    
    
      //   return typeof result === 'object'?result:obj;
      // 若构造函数返回的是一个不为null的对象则返回 result 否则返回obj
      // 若是一个null对象 result || obj 返回的是obj
      return typeof ret === "object" ? result || obj : obj;
    }

    在这个**objectFactory**函数中:

    • 首先创建一个空对象obj,对应new操作符创建空对象的步骤。
    • 接着通过**obj.__proto__ = Constructor.prototype建立原型链关系**,确保实例能够继承原型上的属性和方法。
    • 然后使用apply方法将构造函数的this指向obj ,并传入参数执行构造函数,执行结果存储在result中。
    • 最后根据result的类型进行判断,如果result是对象类型且不为null,则返回result否则返回最初创建的newObject ,实现与new操作符一致的返回逻辑。
  2. 测试

    javascript 复制代码
    function Person(name, age) {
      this.name = name;
      this.age = age;
    }
    Person.prototype.say = function () {
      console.log("name", this.name, "age", this.age);
    };
    
    //与这个相同 let p = new Person("张三",18)
    let p = objectFactory(Person, "张三", 18);
    console.log(p); // Person { name: '张三', age: 18 } (这里由Person原型链指向 代表是谁的实例化)
    console.log(p instanceof Person); // 构造时有  obj.__proto__ = Constructor.prototype; 所以为true
    p.say();
相关推荐
多啦C梦a2 分钟前
《hash+history》你点“关于”,页面却没刷新?!——揭秘前端路由的“穿墙术”
前端·javascript·面试
水纹7 分钟前
使用pdfjs_3.2.146 预览并本地存储批注demo
前端·javascript
yvvvy9 分钟前
别再懵了!从小白到前端大厂选手的 History 路由通关指南(还顺带干翻 Hash)
javascript
hxmmm11 分钟前
hash路由和history路由
前端·javascript
前端付豪15 分钟前
24、前端性能优化体系:从指标定义到监控闭环的实战指南
前端·javascript·架构
gzzeason1 小时前
React源码4 三大核心模块之一:Schedule,scheduleUpdateOnFiber函数
开发语言·javascript·ecmascript
十秒耿直拆包选手2 小时前
web:js提示框、询问框、输入框的使用
开发语言·javascript
天天向上的鹿茸2 小时前
web前端用MVP模式搭建项目
前端·javascript·设计模式
天天摸鱼的java工程师3 小时前
每天导入100万数据导致数据库死锁?
java·后端·面试
山河木马3 小时前
前端学C++可太简单了:导入标准库
前端·javascript·c++