js new的原理


学习本文内容内容前,你需要了解js中对象,原型链call(),bind(),apply(),arguments的用法。

new操作符的原理

在调用 new 的过程中做了四件事:

  1. 新生成对象:new关键字会首先创建一个空对象
  2. 链接到原型:将这个空对象的原型对象指向构造函数的原型属性,从而继承原型上的方法
  3. 绑定this:将this指向这个空对象,执行构造函数中的代码,以获取私有属性
  4. 返回新对象:如果构造函数返回了一个对象res,就将该返回值res返回,如果返回值不是对象,就将创建的对象返回

理解了new的原理,我们就可以手动实现一个new的创建了。

//创建一个 _new
function _new() {
    //定义空对象
    /*
        * arguments获取调用_new时所传入的实参
        * 它并不是一个真正的数组
        * 我们约定,调用_new时,所传入的第一个元素是构造器
        */
    const obj = {};

    // 取出参数列表的第一个参数(构造函数)
    /*
        * shift作用:
        * 1.取出数组的第一个元素(构造器)
        * 2.同时它会修改数组
        */
    const fn = [].shift.call(arguments);

    //手动指正obj的构造函数为 fn(链接原型)
    obj.__proto__ = fn.prototype;

    //调用fn,改变this为obj,传入剩余参数arguments(此时arguments是取出第一个元素后的部分)
    const res = fn.apply(obj, arguments);

    //考虑到fn函数中有return的原因,需要对res进行判断
    // return typeof res === 'object' || typeof res === 'function' ? res : obj;
    return res instanceof Object ? res : obj;
}

案例

//构造函数
function Person(name, sex) {
    this.name = name;
    this.sex = sex
}
 Person.prototype.getName = function() {
    return this.name
}

const person1 = new Person('hugh', '男');
const person2 = _new(Person, 'hugh', '男');
console.log(person1) //Person {name: 'hugh', sex: '男'}
console.log(person2)//Person {name: 'hugh', sex: '男'}

//共享原型上的方法
console.log(person1.getName === person2.getName); //true

注解:

1.关于[].shift.call(arguments)

因为 arguments 是类数组对象,虽然有length属性,但是没有 shift 方法,故通过 call 方法改变执行上下文调用 shift 方法

2.关于obj.proto = fn.prototype

原型链使用, __proto__ 是每个对象拥有的隐藏属性,指向它构造函数的原型对象(prototypeprototype 是每个构造函数(Fuction.prototype除外)拥有的属性,它是函数所独有的,它是从一个函数指向一个对象(构造函数的原型对象)

若是对上面的shiftcall和原型链不是特别熟悉,我们再看看下面一个。

//不携带参数
function new1() {
    // 获取构造函数和传递的参数
    const [fn, ...args] = [...arguments];

    //1.通过Object.create()方法创建一个构造函数实例原型引用的一个对象
    const obj = Object.create(fn.prototype);

    //2.将构造函数中的this指向obj,执行构造函数代码,获取返回值 
    const res = fn.apply(obj, args);
    //3.判断返回值类型 
    return res instanceof Object ? res : obj;
}

//携带参数
function new2(fn, ...args) {
    //1.创建一个空对象,并将对象的__proto__指向构造函数的prototype 这里我两步一起做了
    const obj = Object.create(fn.prototype);
    //2.将构造函数中的this指向obj,执行构造函数代码,获取返回值 
    const res = fn.apply(obj, args);
    //3.判断返回值类型 
    return res instanceof Object ? res : obj;
}

文章作者: 弈心
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 弈心 !
评论
  目录