this指向问题
this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式
全局函数(默认绑定)
全局函数的this,在严格模式下指向undefined,非严格模式下指向window
function foo() { console.log(this) } foo() // window
'use strict'
function foo() { console.log(this) } foo() // undefined
对象方法(隐式绑定)
作为对象的方法存在的函数,谁调用它,this就指向谁。
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
obj2.foo() // 42
对象属性引用链中只有最顶层或者说最后一层会影响调用位置,举例来说:
function foo() {
console.log( this.a );
}
var obj2 = {
a: 42,
foo: foo
};
var obj1 = {
a: 2,
obj2: obj2
};
obj1.obj2.foo(); // 42
对象方法重新赋值为变量,要时刻注意是谁调用了函数:
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 函数别名!
var a = "global"; // a 是全局对象的属性
bar(); // "global"
显式绑定
call
function maile() {
console.log(this.age)
}
var obj = {
age: 18
}
maile.call(obj) // 18
apply
function maile(a) {
console.log(this.age, a)
}
var obj = {
age: 18
}
maile.apply(obj, [1]) // 18 1
bind
bind函数调用的参数就是this,返回的还是一个函数。
function maile() {
console.log(this.age)
}
var obj = {
age: 18
}
maile.bind(obj)() // 18
如果你把 null 或者 undefined 作为 this 的绑定对象传入 call、apply 或者 bind,这些值在调用时会被忽略,实际应用的是默认绑定规则。
function foo() {
console.log( this.a );
}
var a = 2;
foo.call( null ); // 2
然而,总是使用 null 来忽略 this 绑定可能产生一些副作用。如果某个函数确实使用了this(比如第三方库中的一个函数),那默认绑定规则会把 this 绑定到全局对象(在浏览器中这个对象是window),这将导致不可预计的后果(比如修改全局对象)。既然我们希望this是空,使用下面的方式会比较安全些:
function foo(a,b) {
console.log( "a:" + a + ", b:" + b );
}
// 我们的 DMZ 空对象
var arr = Object.create( null );
// 把数组展开成参数
foo.apply( arr, [2, 3] ); // a:2, b:3
构造函数(new 绑定)
使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。创建(或者说构造)一个全新的对象。这个新对象会被执行 [[ 原型 ]] 连接。这个新对象会绑定到函数调用的 this。如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
function Foo(a) {
this.a = a
}
var foo = new Foo(2)
console.log(foo.a) // 2
箭头函数
箭头函数并不是使用 function 关键字定义的,而是使用被称为“胖箭头”的操作符 => 定义的。箭头函数不使用 this 的四种标准规则,而是根据外层(函数或者全局)作用域来决定 this。
var a = 2
var obj = {
a: 1,
foo: () => {
console.log(this.a)
}
}
obj.foo() // 2
间接引用
function foo() {
console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2
p.foo = o.foo 的返回值是目标函数的引用,因此调用的是foo函数,不是o或者p对象中的函数。
优先级
默认绑定是最低的,显式绑定优先级更高,new 绑定比隐式绑定优先级高