[技术分享]New 原理和初步实现以及部分知识点扩展

shangjin发布于1 个月前 • 67 次阅读

        大家都清楚构造函数,也知道构造函数通过new获得实例后,this都是指向实例的,那么大家有没有疑问,new是如何做到的!this改变指向无非是这几个方法call,apply,bind。第二个问题来了,构造函数没有return,new后也会返回一个实例化的对象,这个又是如何实现的!先看下面的代码:开局一张图,全靠字解释!

image

        图中大家可以看到我们创造了一个名为A的构造函数,A的原型上有个叫做getA的方法,我们通过正常的new获得实例b,同时传入’构造函数’实参,A里面的静态变量a命名为‘构造函数’。然后我们通过b调用b.a和b.getA(),拿到实例里面的静态变量a的值。我们模拟出来的函数也必须满足以上的条件,具体大家看下面的_new方法,容我一句句解析,有些地方有扩展,大家耐心观看即可!

        首先,我们创建了一个_obj的对象,然后通过shift获取第一个参数,赋值给变量constructor,这里说几句,为啥创建_obj的时候不用new Object,而是直接用{},这个是为了节约性能,具体可以自行google。第二个要说的Array.prototype.shift.call[].shift.call的区别,前者是通过原型链调用shift方法,后者是直接调用[]上的shift方法,性能上后者更优!

        再来看下一句,_obj._proto_ = constructor.prototype,我们都知道我们要返回的对象可能是_obj,所以我们要让_obj继承A这个原型对象,如何让_obj继承A的原型呢,我们想到了正常new获得的实例对象,通过_proto_可以指向A的原型,于是就有了我们这句代码!有爱探究的童鞋,如果要问为啥实例的_proto_指向原型,大家可以去看看es系列相关规范!

        接下来一句是关键,var instance = constructor.apply(_obj, arguments),constructor就是我们传入的原型对象,这里为了大家便于理解,我们用A做例子,也就是大家把constructor当作是A,A指定了内部的this指向我们上面创建的_obj对象,同时把剩下的参数传进去了,结合具体下面这个例子,var c = _new(A, ‘模拟构造函数’),我们可以看出‘模拟构造函数’这个实参被传入了A中,同时this因为指向_obj,也就是_obj所指向的对象,注意,_obj也只是一个拥有对象引用地址的变量,真正的对象是存在内存中的,也就是说_obj这个变量即使不存在了,只要对对象引用还存在,那么对象还是存在的!上面这段话有点拗口,大家可以仔细读几遍,对我们理解下面有帮助!这个时候因为this指向了_obj,那么这个this.a就是_obj.a,那么_obj.a的值就是‘模拟构造函数’,这样我们如果返回_obj,能访问A中的所有静态变量。如果大家还不理解为啥_obj能访问A中的所有变量,大家可以去看看上面对这句代码的解析,说白了也是引用地址的功劳,A把this指向_obj的时候,_obj.a其实是扩展了其指向对象的属性,增加了其指向对象一个属性名为a,值为‘模拟构造函数’的键值对!

        最后一句,还是结合A这个方法,因为A没有返回值,那么instance为空,所以最后一句返回了_obj本身。

        如果构造函数有返回值,那么就返回返回值,至于能不能继承原型上的protoType就看构造函数返回值如何写啦,有兴趣的童鞋可以自己去试试!

共收到 0 条回复