同学给我来个手写的new吧

1.手写一个new

首先看看new怎么用

function Person(name){
    this.name = name
}
Person.prototype.eat = function() {
    console.log("Eating")
}
var qd = new Person('qd')
console.log(qd)
qd.eat();
  使用new创建的实例:
    能够访问构造函数里的属性(name);
    能够访问原型中的属性(eat)
在调用new时候发生了四件事:
    1新生成一个对象
    2将构造函数的作用域赋值给新对象(即绑定新对象的 this)
    3执行构造函数中的代码(即为这个新对象添加属性)
    4返回新对象

A版本

function create() {
    //1.获取构造函数,并删除arguments中的第一项
    var Con = [].shift.call(arguments);
    
    //2.创建一个空对象并连接到构造函数的原型,使它能够访问原型中的属性
    var obj = Object.create(Con.prototype);
    
    3.使用apply改变构造函数中this的指向实现继承,使obj能够访问到构造函数中的属性
    var ret = Con.apply(obj,arguments);
    
    4.优先返回构造函数返回的对象
    return ret instanceof Object ? ret : obj;
}

解释一下4中的判断:主要考虑的是没有返回值,

//1.有返回值且为对象:
function Person (name, sex) {
    this.name = name
    return {
        sex:sex
    }
}
1.构造函数中有返回值且为对象,那么创建的实例就只能访问到返回对象中的属性,
所以要判断一下ret的类型,如果是对象的话,则返回这个对象。

//2构造函数没有返回值:
function Person(name){
    this.name = name;
}
2.构造函数没有返回值,那么创建的实例就能访问这个构造函数中的所有属性了,此时ret就会为undefined,所以返回obj。

3,返回值为基本类型
function Person(name){
    this.name = name
    return 'str'
}
3.构造函数中没有返回值但是返回值是undefined以外的其他基本类型(比如字符串),这种情况当成第二种情况(没有返回值)来处理。

好了我们来验证一下吧:

function Person (name) {
    this.name = name
}
Person.prototype.eat = function() {
    console.log("Eating")
}

function create() {
    var Con = [].shift.call(arguments);
    var obj = Object.create(Con.prototype);
    var ret = Con.apply(obj, arguments);
    return ret instanceof Object ? ret : obj;
}

//调用
var qdleader = create(Person, 'qdleader');
console.log(qdleader); //Person{name:'LinDaiDai'}
qdleader.eat()  //'Eatting'





注意:object.create方法,参数为new出来的实例的原型,也就是_proto_的指向

还有其他版本的: b版本

function create() {
    // 创建一个空的对象
    let obj = {}
    // 获得构造函数
    // 因为 arguments 类数组,所以我们可以用数组的 shift 来实现 arguments 的 ‘push’ 和 ‘pop’
    // 将 arguments 进数组,并将第一个元素移除并赋值给 Con (Constructor).
    let Con = [].shift.call(arguments)
    // 链接到原型
    obj.__proto__ = Con.prototype
    // 绑定this,并执行构造函数,就相当于 obj .constructor(arguments)
    let result = Con.apply(obj, arguments)
    // 确保 new 出来的是个对象
    return typeof result === 'object' ? result : obj
}




1:arguments是一个类数组,以像是数组的样子取到传入的所有参数,有一个callee属性(值为被调用的方法)利用数组的shift方法拿到了第一项 也就是Animal函数,同时原来的arguments被更改,失去了第一个参数。再后面被apply拿来做参数

2:Constructor.apply(obj, arguments)
意思是在obj里执行构造函数,并且挨着arguments里剩下的参数

3:[ ]可以等价Array.prototype.shift ,也就是在arguments上执行数组的shift方法的意思

4:最后可以加一个判断,是否为对象再返回

比b版本更好一点的写法:

function _new() {
    let target = {}; //创建的新对象
    //第一个参数是构造函数
    let [constructor, ...args] = [...arguments];
    //执行[[原型]]连接;target 是 constructor 的实例
    target.__proto__ = constructor.prototype;
    //执行构造函数,将属性或方法添加到创建的空对象上
    let result = constructor.apply(target, args);
    if (result && (typeof (result) == "object" || typeof (result) == "function")) {
        //如果构造函数执行的结构返回的是一个对象,那么返回这个对象
        return result;
    }
    //如果构造函数返回的不是一个对象,返回创建的新对象
    return target;
}

个人感觉A版本好一些

欢迎加群!

 

posted on 2020-03-22 13:15  前端架构师  阅读(397)  评论(0编辑  收藏  举报

导航

  • !