实现一个new函数
在日常javaScript开发中,我们多多少少都会用到new
,最常见的比如new Date
等等,在一些js
面向对象中,用到new
的地方更多了,比如我们通过function
来模拟声明一个类,需要实例化的时候就需要用new xxx()
:
function Class() {
// ...
}
var inst = new Class();
在MDN上对在执行了new
之后的介绍如下:
- 一个新对象被创建。它继承自
*foo*.prototype
- 构造函数
*foo*
被执行。执行的时候,相应的传参会被传入,同时上下文会被指定为这个新实例。new *foo*
等同于new *foo*()
, 只能用在不传递任何参数的情况。 - 如果构造函数返回了一个“对象”,那么这个对象会取代整个
new
出来的结果。如果构造函数没有返回对象,那么new
出来的结果为步骤1创建的对象。(一般情况下构造函数不返回任何值,不过用户如果想覆盖这个返回值,可以自己选择返回一个普通对象来覆盖。当然,返回数组也会覆盖,因为数组也是对象。)
在知道了new
之后发生的事情,我们的_new
函数就可以按照上面的几个步骤来:
function _new() {
// 获取到所有参数
var args = [].slice.call(arguments),
// 创建一个空对象
obj = Object.create({}),
// 把第一个参数作为构造器
Constructor = args[0],
res;
// 继承构造器下的原型
obj.__proto__ = args[0].prototype;
// 执行构造器,并传入相关参数
res = Constructor.apply(obj, args.slice(1));
return obj;
}
上面是第一版,我们把构造器的执行结果作为返回值返回出去,下面我们一起写个模拟类测试下:
function Class(name, age) {
this.name = name;
this.age = age;
}
Class.prototype = {
constructor: Class,
method: function () {
console.log("I'm method under Class.prototype");
}
};
var inst = _new(Class, "rwson", 24);
inst.method();
console.log(inst);
可以看到在控制台打出的类似下图的东西
我们现在实现了一个版本,只是把在_new
方法中的obj
对象返回出来了,刚才上面第三步骤说到,如果构造函数返回了一个“对象”,那么这个对象会取代整个
new出来的结果。如果构造函数没有返回对象,那么
new出来的结果为步骤1创建的对象。(一般情况下构造函数不返回任何值,不过用户如果想覆盖这个返回值,可以自己选择返回一个普通对象来覆盖。当然,返回数组也会覆盖,因为数组也是对象。)
,针对构造函数有返回值的情况,我们需要把该返回值返回,下面我们把之前的代码改改,以适配有返回值的情况:
function _new() {
var args = [].slice.call(arguments),
obj = Object.create({}),
Constructor = args[0],
res;
obj.__proto__ = args[0].prototype;
res = Constructor.apply(obj, args.slice(1));
// 判断Constructor的返回值类型
return typeof res === "object" ? res : obj;
}
同样我们写个类做测试
function Class(name, age) {
this.name = name;
this.age = age;
return {
name: name,
age: age
};
}
var inst = _new(Class, "rwson", 24);
console.log(inst);
观察在控制台的输出应该和下面的类似:
到这里我们的_new
方法就实现了,但是由于Object.create
的原因,在IE9-
的版本中可能不太支持,所以我们需要把Object.create
改成new Object
或者对象字面量的形式
function _new() {
var args = [].slice.call(arguments),
obj = {},
Constructor = args[0],
res;
obj.__proto__ = args[0].prototype;
res = Constructor.apply(obj, args.slice(1));
// 判断Constructor的返回值类型
return typeof res === "object" ? res : obj;
}