1、MDN 对 new 运算符的描述:
new
运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。new
关键字会进行如下操作:
- 创建一个空的简单的
JavaScript
对象(即{}
); - 链接该对象(即设置对象构造函数)到另一个对象;
- 将步骤1新创建的对象作为
this
的上下文; - 如果该函数没有返回对象,则返回
this
;
看个例子:
function Person(name,age) {
this.name = name;
this.age = age;
this.habit = 'Games';
}
Person.prototype.strength = 60;
Person.prototype.sayName = function () {
console.log('my name is '+this.name);
}
var person = new Person('Decun',28);
console.log(person.name);//Decun
console.log(person.habit);//Games
console.log(person.strength);//60
person.sayName();//my name is Decun
从这个例子可以看到,实例person
可以:
- 访问到
Person
构造函数里的属性; - 访问到
Person.prototype
中的属性;
2、接下来我们一步步模拟一下new 运算符
我们写一个函数,命名为objFactory
,用来模拟new
的效果。因为new
是关键字,不能像bind
函数一样直接覆盖。用的时候是这样的:
function Person(){
......
}
//使用 new
var person = new Person(...);
//使用 objFactory
var person = objFactory(Person,...);
1、初步实现
因为new
的结果是一个新对象,所以模拟实现的时候,首先要创建一个新对象obj
。
因为obj
能访问Person.prototype
里的属性,也就是说obj的原型属性__proto__
指向了Person构造函数的prototype
。
同时obj
具有构造函数Person
里的属性,说明obj
继承了Person
,使用借用构造函数继承实现,我们可以使用apply
来给obj
添加新的属性。
现在尝试写第一版模拟实现代码:
function objFactory() {
//创建一个新对象
var obj = new Object();
//调用数组的shift():从数组中删除第一个元素,并返回该元素的值
//获取自定义对象类型
Constructor = Array.prototype.shift.call(arguments);
//obj的原型指向自定义对象类型的prototype
//这样obj就可以访问Constructor.prototype中的属性了
obj.__proto__ = Constructor.prototype;
//经典继承方式
//这样obj就能继承Constructor里面的属性了
Constructor.apply(obj, arguments);
//返回obj
return obj;
};
function Person(name,age) {
this.name = name;
this.age = age;
this.habit = 'Games';
}
Person.prototype.strength = 60;
Person.prototype.sayName = function () {
console.log('my name is '+this.name);
}
var person = objFactory(Person,'Decun', 28);
console.log(person.name);//Decun
console.log(person.habit);//Games
console.log(person.strength);//60
person.sayName();//my name is Decun
我们再总结一下,这一版中,我们:
- 用new Object()方式创建了一个新对象obj
- 取出第一个参数,就是我们要传入的构造函数。因为shift会修改原数组,所以arguments会被去除第一个参数。
- 将obj的原型指向构造函数的prototype,这样obj就可以访问到构造函数原型中的属性
- .使用apply,改变构造函数的this指向到obj对象,obj就可以访问到构造函数中的属性
- 返回obj
2、初步效果实现了,接下来看看当构造函数内部有返回对象的情况
function Person(name, age) {
this.habit = 'Games';
this.name = name;
return{
age: age,
strength:60
}
}
Person.prototype.sayName = function () {
console.log('my name is ' + this.name);
}
var person = new Person('Decun', 28);
console.log(person.name);//undefined
console.log(person.habit);//undefined
console.log(person.strength);//60
person.sayName();//person.sayName is not a function
在上面的例子中,构造函数Person
返回了一个对象,在实例 person
中只能访问返回的对象中的属性。
而且还要注意一点,在这里我们是返回了一个对象,假如我们只是返回一个基本类型的值呢?
再看一个例子:
function Person(name, age) {
this.habit = 'Games';
this.name = name;
return 1;
}
Person.prototype.sayName = function () {
console.log('my name is ' + this.name);
}
var person = new Person('Decun', 28);
console.log(person.name);//Decun
console.log(person.habit);//Games
console.log(person.strength);//undefined
person.sayName();//my name is Decun
结果完全颠倒过来了,这次虽然有返回值,但是却相当于没有返回值进行处理了。
注意:return null
的时候也会相当于没有返回值。
所以我们还需要判断返回的值是不是一个对象,如果是一个对象,我们就返回这个对象,如果不是,我们该返回什么就返回什么。
接下来就是完结版代码:
function objFactory() {
//创建一个新对象
var obj = new Object();
//调用数组的shift():从数组中删除第一个元素,并返回该元素的值
//获取自定义对象类型
Constructor = Array.prototype.shift.call(arguments);
//obj的原型指向自定义对象类型的prototype
//这样obj就可以访问Constructor.prototype中的属性了
obj.__proto__ = Constructor.prototype;
//经典继承方式
//这样obj就能继承Constructor里面的属性了
var result = Constructor.apply(obj, arguments);
//判断构造函数是否返回对象
return typeof result === 'object' ? result || obj : obj;
};
function Person(name, age) {
this.habit = 'Games';
this.name = name;
return null;
}
Person.prototype.sayName = function () {
console.log('my name is ' + this.name);
}
var person = objFactory(Person,'Decun', 28);
console.log(person.name);//Decun
console.log(person.habit);//Games
console.log(person.strength);//undefined
person.sayName();//my name is Decun