this是什么
当一个函数被调用,会创建一个执行上下文,它包含了函数在哪里被调用,调用方式,传入的参数信息等,this就是这个记录属性,this是在函数被调用(运行)时绑定的,它指向什么完全取决于函数在哪里被什么方式调用
绑定规则
默认绑定
这是最常用的函数调用类型:独立函数调用,举例!
function foo() {
console.log(this.a);
}
var a = 2;
foo() // 结果为2
在本例中,函数调用时应用了this的默认绑定,因此this指向全局对象
在全局作用域下直接调用函数时,this指向window对象
隐式绑定
调用位置是否有上下文对象,也会影响到this的指向,举例!
function foo() {
console.log(this.a);
}
var obj={
a:42,
foo:foo
};
obj.foo() // 结果为42
当函数引用有上下文对象时,隐式绑定规则会把函数中调用的this绑定到这个上下文对象,因此此时this指向对象obj1
隐式丢失现象
被隐式绑定的函数会丢失绑定对象,也就是说这个时候它会应用默认绑定,举例!
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // 注意,这里相当于是赋值操作了
var a = 3;
bar() // 结果为3
虽然bar是obj.foo的一个引用,但实际上,它引用的是全局下foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数调用,相当于全局下直接调用foo函数了,所以此时this指向window对象
除此以外,回调函数丢失this绑定也是非常常见的,举例!
function foo() {
console.log(this.a);
}
function doFoo(fn){
fn()
}
var obj = {
a: 2,
foo: foo
};
var a = 3;
doFoo(obj.foo) // 结果为3
参数传递其实就是一种隐式赋值(LHS),所以传入参数时也会被隐式赋值,所以结果和上面的一样
当函数被当做对象的方法调用时,this指向这个对象
显示绑定
使用 call(),apply(),bind()方法可以在调用函数时强制把它的this绑定到obj上(但bind 方法会返回一个新函数),举例!
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
};
foo.call(obj) // 结果为2
从this绑定的角度来说,call()和apply()方法是一样的,区别只在于传参不一样,但这两种方法仍然无法直接解决丢失绑定的问题,不过好在它们的变种可以解决,举例!
function foo() {
console.log(this.a);
}
var obj = {
a: 2,
};
var bar = function () {
foo.call(obj);
}
var a = 3;
bar(); // 结果为2
setTimeout(bar, 100); // 结果为2
bar.call(window) // 结果为2
我们先创建了函数bar,并在它内部手动调用foo.call(obj),强制把foo的this绑定给了obj,之后无论怎么调用函数bar,它总会手动在obj上调用foo,这种是显式的强制绑定,叫“硬绑定”,硬绑定的典型应用场景就是创建一个包裹函数,负责接受参数并返回值,举例!
function foo(something) {
console.log(this.a, something);
return this.a + something
}
var obj = {
a: 2,
};
var bar = function () {
return foo.apply(obj, arguments)
}
var b = bar(3); //结果为2 3
console.log(b); //结果为5
由于硬绑定是很常用的模式,所以ES5提供了内置方法,也就是bind();举例!
function foo(something) {
console.log(this.a, something);
return this.a + something
}
var obj = {
a: 2,
};
var bar = foo.bind(obj);
var b = bar(3); //结果为2 3
console.log(b); //结果为5
结果和上例一样,bind()会返回一个硬编码的新函数,会把指定的参数设置为this的上下文并调用原始函数
使用call(),apply(),bind()方法时,this指向传入的对象
new绑定
最后一条this绑定规则,在此之前先澄清一个关于javascript中函数和对象的误解:javascript中的构造函数,只是一些在使用new操作符时被调用的函数,只是被new操作符调用的普通函数而已!实际上并不存在所谓的“构造函数”,只有对于函数的“构造调用”。
使用new来调用函数,会自动执行以下操作:
- 创建一个全选的对象
- 这个新对象的原型prototype指向构造函数的原型
- 这个新对象会绑定到函数调用的this
- 如果函数没有返回其它对象,那么new表达式中的函数调用会自动返回这个新对象
举例!
function foo(a) {
this.a = a;
}
var bar = new foo(2);
console.log(bar.a); // 结果为2
使用new来调用foo时,会构造一个新的对象并把它绑定到foo()调用中的this上
使用构造函数时,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(something) {
this.a = something;
};
var obj1 = {
foo: foo
};
var obj2 = {};
obj1.foo(2);
console.log(obj1.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绑定比隐式绑定优先级更高
被忽略的this
如果把null或者undefined作为this的绑定对象传入call,apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则,举例!
function foo() {
console.log(this.a);
};
var a = 2;
foo.call(null) // 结果为2
箭头函数的this
箭头函数并不是用function关键字定义的,不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this,举例!
function foo() {
return (a) => {
console.log(this.a);
}
};
var obj1 = { a:2 };
var obj2 = { a:3 };
var bar = foo.call(obj1);
bar.call(obj2) // 结果为2
foo()内部创建的箭头函数会捕获调用时foo()的this,由于foo的this绑定待obj1,bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改(new也不行)