这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战**
昨天少了一点内容,关于new有个地方很有意思,在传统的是面向类的语言中,“构造函数”是类中的一些特殊方法,使用new初始化类时会调用类中的构造函数,通常是这样的:
something = new MyClass(..);
JavaScript中的new操作符,使用方法看起来和面向类的语言一样,绝大多数开发者都认为JavaScript中new的机制也和那些语言一样,然而,JavaScript中的new的机制实际上和面向类的语言完全不同。
在这里重新定义一下JavaScript中的“构造函数”,在JavaScript中,构造函数只是一些使用new操作符时被调用的函数,他们不属于某个类,也不会实例化一个类,实际上,他们甚至都不能说是一种特殊的函数类型,他们只是被new操作符调用的普通函数而已。实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”。
使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作:
- 创建(或者说构造)一个全新的对象。
- 这个新对象会被执行[[prototype]]连接。
- 这个新对象会绑定到函数调用的this。
- 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
上一篇中分析了this绑定的四条规则,那么这些规则的优先级是怎么设定的呢?
毫无疑问,默认绑定的优先级是最低的,暂时先不考虑默认绑定。
先看显式绑定和隐式绑定哪个优先级更高:
function foo() {
console.log( this.a );
}
var obj1 = {
a: 2,
foo: foo
};
var obj2 = {
a: 3,
foo: foo
};
obj1.foo(); // 2
obj2.foo(); // 3
obj1.foo.call( obj2 ); // 3
obj2.foo.call( obj1 ); // 2
可以看出,显式绑定的优先级更高。
再来看隐式绑定和new绑定的优先级谁高:
function foo() {
this.a = something;
}
var obj1 = {
foo: foo
};
var obj2 = {};
obj1.foo( 2 );
console.log( obj.a ); // 2
obj1.foo.call( obj2, 3);
console.log( obj2.a ); // 3
var bar = new obj1.foo( 4 );
console.log( obj1.a ); // 2
console.log( bar.a ); // 4
可以看到new绑定比隐式绑定优先级更高。
new和call/apply无法一起使用,因此不能通过new foo.call(obj1)来直接进行测试。 (这两个的测试比较复杂,暂时不讲解。)
直接说绑定规则:
- 函数是否在
new中调用(new绑定)?是的话this绑定的是新创建的对象。var bar = new foo(); - 函数是否通过
call、apply(显式绑定)调用?是的话this绑定的是指定的对象。var bar = foo.call(obj2); - 函数是否在某个上下文对象中调用(隐式绑定)?是的话
this就绑定的是那个上下文对象。var bar = obj1.foo(); - 如果都不是的话就是默认绑定,在严格模式下就绑定到
undefined,否则绑定到全局对象。var bar = foo();
规则之外的情况:
-
如果把
null或者undefined作为this的绑定对象传入call、apply或者bind,这些值在调用时候会被忽略,实际应用的是默认绑定规则。 -
ES6中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定
this,具体来说,箭头函数会继承外层函数调用的this绑定(无论this绑定到什么)。