**this 是在运行式进行绑定的,并不是在编写是绑定
this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式
this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用**
默认绑定
var a = 'aaa';
var obj = {
a: 'bbb'
}
var fun = {
a: 'ddd',
foo: function(){
(function(){
var a = 'ccc';
console.log(this.a)
})(); //=>aaa
(function(){
console.log(this.a) //=>bbb
}).call(obj);
(function(){
console.log(this.a) //=>ddd
}).call(fun);
}
}
fun.foo();
//匿名函数内默认this指向window
如果使用严格模式,则不能将全局对象用户默认绑定,因此this会绑定到undefined
function foo(){
"use strict";
console.log(this.a)
}
var a = 3;
foo() //Uncaught TypeError: Cannot read property 'a' of undefined
隐式绑定
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo: foo
}
obj.foo(); //=>2
隐式绑定的函数会丢失绑定对象
function foo(){
console.log(this.a);
}
var obj = {
a:2,
foo: foo
};
var bar = obj.foo;
var a ="oops";
bar(); //=> 'oops'
function foo(){
console.log(this.a)
}
function doFoo(fn){
//fn其实引用的是foo
fn(); //调用位置,因为doFoo的作用域中没有变量a,所以往上找到全局变量a='global'
}
var obj = {
a: 2,
foo: foo
}
var a = 'global';
doFoo(obj.foo) //=>'global'
显示绑定
function foo(){
console.log(this.a)
}
var obj = {
a: 2
}
foo.call(obj) //=>2
如果传入的是一个原始值,这个原始值会转换成它的对象形式,这个过程称为“装箱”
function foo(){
console.log(this)
}
foo.call(2) //=>Number
foo.call("str") //>String
new绑定
使用new 来调用函数,会自动执行下面的操作
1. 创建一个全新的对象
2. 这个新对象会被执行prototype连接
3. 这个新对象会绑定到函数调用的this
4. 如果函数没有返回其他对象,则new表达式中的函数调用会自动返回这个新对象
function foo(a){
this.a = a;
}
var bar = new foo(2);
console.log(bar.a) //=>2
箭头函数中的this
箭头函数中的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,箭头函数的绑定无法被修改
如果要判断一个运行中的函数的this绑定,就需要找到这个函数的直接调用位置,找到之后就可以顺序应用以下四条规则来判断this的指向:
- 由new调用?绑定到新创建的对象
- 由call、apply或bind调用?绑定到制定对象
- 由上下文对象调用?绑定到那个上下文对象
- 默认,在严格模式下绑定到undefined,否则绑定到全局对象