js this
在函数中 this 到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了
this 就是指针,指向我们调用函数的对象
普通函数:谁调用(this)就指向谁。
# 全局上下文
在全局执行上下文中(在任何函数体外部)this 都指代全局对象,浏览器的全局对象是 window。
// 在浏览器中, window 对象同时也是全局对象:
console.log(this === window); // true
this.name = "yz";
console.log(window.name); // "yz"
console.log(name); // "yz"
2
3
4
5
6
# 函数上下文
在函数内部,this 的值取决于函数被调用的方式。
this 绑定的优先级:
new 绑定(构造函数调用) > 显示绑定(call 和 apply 调用) > 隐式绑定(对象方法调用) > 默认绑定(普通函数调用)
# this 的 4 种指向
作为函数调用,非严格模式下,this 指向 window,严格模式下,this 指向 undefined;
作为某对象的方法调用,this 通常指向调用的对象。
在构造函数中,this 指向新创建的对象
箭头函数没有单独的 this 值,this 在箭头函数创建时确定,它与声明所在的上下文相同。
使用 apply、call、bind 可以绑定 this 的指向。
# this 的四种绑定规则
默认绑定、隐式绑定、显示绑定、new 绑定。优先级从低到高
# 默认绑定(作为普通函数被调用)
没有其他绑定规则存在时的默认规则
当函数不作为对象的属性被调用时候,也就是我们所说的普通函数方式,此时的 this 总是指向全局的对象。在浏览器的 js 里面,这个全局对象是 window 对象。
function foo() {
console.log( this.a );
}
var a = 2;
foo() ==== foo.call(window) // 2
2
3
4
5
因为 foo()是直接调用的(独立函数调用),没有应用其他的绑定规则,这里进行了默认绑定,将全局对象绑定 this 上,所以 this.a 就解析成了全局变量中的 a,即 2。
function foo() {
"use strict";
console.log(this.a);
}
var a = 2;
foo(); // Uncaught TypeError: Cannot read property 'a' of undefined
2
3
4
5
6
7
注意:在严格模式下(strict mode),全局对象将无法使用默认绑定,即执行会报 undefined 的错误
# 隐式绑定(作为对象的方法调用)
除了直接对函数进行调用外,有些情况是,函数的调用是在某个对象上触发的,即调用位置上存在上下文对象。
当函数作为对象的方法被调用的时候,this 指向该对象
function foo() {
console.log(this.a);
}
var a = 2;
var obj = {
a: 3,
foo: foo,
};
obj.foo(); // 3
2
3
4
5
6
7
8
9
10
11
12
对 foo 的调用存在上下文对象 obj,this 进行了隐式绑定,即 this 绑定到了 obj 上,所以 this.a 被解析成了 obj.a,即 3。
多层调用链
function foo() {
console.log(this.a);
}
var obj2 = {
a: 2,
fn: foo,
};
var obj1 = {
a: 1,
o1: obj2,
};
obj1.o1.fn(); // 2
2
3
4
5
6
7
8
9
10
11
12
如果是链性的关系,比如 xx.yy.obj.foo();, 上下文取函数的直接上级,即紧挨着的那个,或者说对象链的最后一个。
obj1 对象的 o1 属性值是 obj2 对象的地址,而 obj2 对象的 fn 属性的值是函数 foo 的地址; 函数 foo 的调用环境是在 obj2 中的,因此 this 指向对象 obj2;
# 显示绑定
通过这两个方法 call(…)或 apply(…)来实现
function foo() {
console.log(this.a);
}
var a = 2;
var obj1 = {
a: 3,
};
var obj2 = {
a: 4,
};
foo.call(obj1); // 3
foo.call(obj2); // 4
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# new 绑定(构造函数调用)
通常情况下,构造器里面的 this 就是指向返回的这个对象
- 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
function foo(a) {
this.a = a;
}
var a = 2;
var bar1 = new foo(3);
console.log(bar1.a); // 3
var bar2 = new foo(4);
console.log(bar2.a); // 4
2
3
4
5
6
7
8
9
10
11
每次调用生成的是全新的对象,该对象又会自动绑定到 this 上
- 如果原函数返回一个对象类型,那么将无法返回新对象,将丢失绑定 this 的新对象。
function foo() {
this.a = 10;
return new String("捣蛋鬼");
}
var obj = new foo();
console.log(obj.a); // undefined
console.log(obj); // "捣蛋鬼"
2
3
4
5
6
7
# 普通函数改变 this 指向
call、apply 和 bind 都是用于改变函数运行时内部 this 的指向的
# apply
应用某一对象的一个方法,用另一个对象替换当前对象。
语法:targetFunction.apply(thisArg,[arg1,arg2])
参数:
第一个参数 thisArg 会作为目标函数 targetFunction 运行时的 this 值传递给目标函数
第二个参数是传递给目标函数的参数数组
返回值:apply 方法的返回值和 bind 方法就完全不同了,它会直接调用并执行目标函数。
# call
调用一个对象的一个方法,以另一个对象替换当前对象。
语法:targetFunction.call(thisArg,arg1,arg2,...)
参数:
第一个参数 thisArg 会作为目标函数 targetFunction 运行时的 this 值传递给目标函数
后面的参数列表 arg1,arg2,... 是传递给目标函数的参数
返回值:apply 方法的返回值和 bind 方法就完全不同了,它会直接调用并执行目标函数。
# bind
应用某一对象的一个方法,用另一个对象替换当前对象
语法:targetFunction.bind(thisArg,arg1,arg2,...)
参数:
第一个参数 thisArg 会作为目标函数 targetFunction 运行时的 this 值传递给目标函数
后面的参数列表 arg1,arg2,... 是传递给目标函数的参数
返回值:bind 方法的返回值是一个目标函数的一个拷贝,
this 指向:这个拷贝出来的函数运行时 this 指向的就是调用 bind 传递的 thisArg 参数
注意:连续多个 bind 之后 this 指向始终指向第一个
# call、apply 和 bind 的区别
call 的 arg 传参需一个一个传,apply 则直接传一个数组。
call 和 apply 直接执行函数,而 bind 需要再一次调用
# 箭头函数 this 指向
箭头函数:调用者指向谁,(this)则指向谁
箭头函数的 this 指向,是父级程序的 this 指向:
如果父级程序有 this 指向,指向向的就是父级程序的 this 指向
如果父级程序没有 this 指向(对象,数组是没有 this),指向的是 window