js的new操作符

js的new操作符

我们先来了解一下new运算符的基本概念:

new运算符创建一个用户自定义的对象类型的实例或具有构造函数的内置对象的实例。new关键字会进行如下操作:

  1. 先创建一个新的空的简单JavaScript对象(即{}
  2. 然后让这个空对象的_proto_指向函数的原型prototype
  3. 将步骤1创建的对象作为this的上下文(将对象作为函数的this传进去)
  4. 如果该函数没有返回对象,则返回this

再来看一段代码:

1
2
3
4
5
6
7
8
9
function Person(name){
this.name = name;
return name;
}
var person = new Person("Tom");
var person1 = Person("Tom");

console.log(person); //Person { name: 'Tom' }(返回的是构造函数实例化后的对象)
console.log(person1);//Tom

可以看到,有new和没new的区别所在,现在分析一下new做了什么

当代码new Person(...)执行时,会发生以下事情:

  1. 一个继承自Person.prototype的新对象被创建
  2. 使用参数Tom调用构造函数Person,并将this绑定到新创建的对象。
  3. 由构造函数Person返回的对象就是 new 表达式的结果(即构造函数实例化后的对象)。

那要是构造函数返回的是一个对象呢?

1
2
3
4
5
6
function Person(name){
this.name = name;
return {name : "Gates"};
}
var person = new Person("Tom"); //Person{ name: 'Gates'}
var person1 = Person("Tom");//Person{ name: 'Gates'}

可以看到,当函数返回的是对象时,则返回的即为该对象。

可是,当构造函数返回null,undefined等非对象时,情况又是什么样的?

1
2
3
4
5
6
function Person(name){
this.name = name;
return null;
}
var person = new Person("Tom"); //Person{ name: 'Tom'}
var person1 = Person("Tom");//null

可以看到,return null时(当使用new操作符)返回的也是构造函数实例后的对象。这是因为null、undefined为非对象,所以return的其实为this

那,要是没有return语句呢?

1
2
3
4
5
function Person(name) {
this.name = name;
}
var person = new Person("Tom"); //Person{ name: 'Tom'}
var person1 = Person("Tom"); //Undefined

其实,如果在JavaScript没有return语句,那么函数就会默认return this

所以,我们可以得出结论:当我们使用了new操作符时,①构造函数如果没有返回对象,那么这个返回值毫无意义;②构造函数如果返回值为对象,那么这个返回值会被正常使用

接下来,我们再看一个例子:

1
2
3
4
5
6
7
8
9
function Person(name){
this.name = name;
}
Person.prototype.sayName = function(){
console.log(this.name);
}
var person = new Person("Tom");
console.log(person.name);//Tom
person.sayName(); //Tom

从这里,我们可以看出new操作符的一些作用:

  • new 通过构造函数Person 创建出来的实例可以访问到构造函数中的属性
  • new 通过构造函数Person 创建出来的实例可以访问到构造函数原型链中的属性,也就是说通过 new 操作符,实例与构造函数通过原型链连接了起来

最后,我们可以自己尝试模拟实现new运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
function create() { //(Con, ...args)函数接收不定量的参数,第一个参数为构造函数,接下来的参数被构造函数使用
var obj = {}; //内部创建一个空对象obj
Con = [].shift.call(arguments);
obj._proto_ = Con.prototype;
//将新对象的内部属性__proto__指向构造函数的原型,这样新对象就可以访问原型中的属性和方法。等同于Object.setPrototypeOf(obj, Con.prototype);
var result = Con.apply(obj, arguments);//使用apply,将构造函数中的this指向新对象,这样新对象就可以访问构造函数中的属性和方法;将obj对象绑定到构造函数上,并且传入剩余参数
return result instanceof Object ? result : obj;//判断构造函数返回值是否为对象,如果为对象就使用构造函数返回的值,否则使用 obj,这样就实现了忽略构造函数返回的原始值
}

function Person(name) {
this.name = name;
}
var person = objectFactory(Person, 'Tom');

参考资料: