this 的指向
this 是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用
注意: this 不指向函数本身,也不指向函数的词法作用域
绑定规则
- 默认绑定
无法应用其他规则时的默认规则
function foo(){
console.log(this.a);
}
var a = 3;
foo(); // 3
// foo() 是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定(注意是在非严格模式下,若启动严格模式,默认绑定不会绑定到全局函数)
// foo()指向全局对象,所以foo()中的this是在全局对象中调用的,this.a 被解析成了全局变量 a,
- 隐式绑定
当函数引用有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象
function foo() {
console.log(this.a);
}
var obj = {
a: 8,
foo: foo
}
var obj2 = {
a: 4,
obj: obj
}
obj.foo() ; // 8
obj2.obj.foo(); // 8
//var obj={ foo: foo } ,obj对象内部包含了一个指向函数的属性,并通过这个属性间接引用函数,即表示foo 函数被 obj 上下文引用了
// 当函数引用有上下文时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象,即 foo 中的 this 会绑定到 obj 上,此时 this.a = obj.a
// 对象属性引用链中只有上一层(最后一层)在调用位置中起作用
隐式绑定的问题是:被隐式绑定的函数会丢失绑定对象,即会应用到默认绑定
function foo() {
console.log(this.a);
}
var obj = {
a: 8,
foo: foo
}
var a = 0;
var b = obj.foo;
b(); // 0
// b 引用的是 foo 函数本身,此时丢掉了绑定的对象,即 b() 其实是一个不带任何修饰的函数调用,因此 this 指向全局对象
- 显示绑定
使用 call(obj) 或 apply (obj,argu) 把函数绑定到 obj上去
function foo() {
console.log(this.a);
}
var obj = {
a: 8,
}
var a = 9;
foo.call(obj); // 8
// 当使用foo.call(obj) 时,foo就会把它的 this 强制绑定到 参数obj上
// 若参数是一个原始值,则它会转换成它的对象形式,叫做“装箱”
为了解决丢失绑定的问题 ,我们提出了一种 “硬绑定”
function foo() {
console.log(this.a);
}
var obj = {
a: 8,
}
var a = 9;
var bar = function(){
foo.call(obj);
}
bar(); //8
// 每次调用bar(),都会在 obj 上调用 foo
setTimeout(bar,100);
bar.call(window); // 8
// 这里没有输出 9,说明硬绑定的 bar 不可以再修改它的 this
ES5 bind()
bind(obj) 会返回一个硬编码的新函数,它会把你指定的参数设置为 this 的上下文并调用原始函数
function foo() {
console.log(this.a);
}
var obj = {
a: 8,
}
var a = 9;
var bar = foo.bind(obj);
bar(); //8
- new 绑定
使用 new 来调用函数时,会自动执行下面的操作:
1.创建一个全新的对象
2.这个新的对象会被执行 prototype 操作
3.这个新的对象会绑定到函数调用的 this
4.如果这个函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象
function foo(a) {
this.a = a;
}
var a = 9;
var bar = new foo(8);
console.log(bar.a); //8
// 使用 new 来调用 foo()时,我们会构造一个全新的对象并把它绑定到 foo()调用中的this 上,即把 bar 绑定到 foo 的 this 上,所以 bar.a = foo 中的 this.a (bar.a)
四种绑定的优先级
new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定
箭头函数的 this 的规则
箭头函数不是使用 function 关键字定义的,故 this 不遵循以上的四种规则,而是根据外层作用域来决定 this
function foo(a) {
return (a)=>{
console.log(this.a);
};
}
var obj1 = {a:1};
var obj2 = {a:2};
var bar = foo.call(obj1);
bar.call(obj2); // 1
// foo() 内部创建的箭头函数会捕获调用时 foo()的 this,由于foo() 的 this 绑定到了 obj1上,bar (foo返回的参数,即箭头函数)的 this 也会绑定到 obj1上
// 箭头函数的绑定无法修改 (new 也无法修改)