首先, 要明白 this 既不指向函数自身, 也不指函数的词法作用域. this 一般存在于函数中, 表示当前函数的执行上下文, 如果函数没有执行, 那么 this 没有内容, 只有函数在执行后 this 才有绑定.
然后, 我们来看看 this 四种绑定规则, 也可以说在四种不同函数执行方式时 this 的指向.
1. 默认绑定(执行)
默认执行: 即没有其他绑定规则存在时的默认规则. 这也是函数调用中最常用的规则. this 指向 Windows, 严格模式下指向 undefined
- function fn(){
- console.log(this)
- }
- fn()
fn()打印到控制台结果为 Windows
开启严格模式后
- function fn(){
- "use strict"
- console.log(this)
- }
- fn()
fn()打印到控制台结果为 undefined
2. 隐式绑定(执行)
通过对象执行(通过上下文对象执行) obj.fn(): 当前的执行对象
- function fn(){
- console.log(this.a)
- }
- var a = 10;
- var obj = {
- a:20,
- b:fn
- }
- var obj2 = {
- a:30,
- b:obj.b
- }
- obj2.b(); //??
obj2.b(); 打印到控制台结果为 30
因为 obj2.b(); 在上述代码中等价于 obj2.fn(), 所以此时 this 指向 obj2, 所以打印结果为 30;
多层调用
- function fn(){
- console.log(this.a)
- }
- var a = 10;
- var obj = {
- a:20,
- b:fn
- }
- var obj2 = {
- a:30,
- b:obj.b
- }
- var obj3 = {
- a:40,
- b:obj2
- }
- obj3.b.b() //?
obj3.b.b() 打印结果为 30
上述代码调用结果为 obj3.b->obj2,obj2.b->obj.b->fn, 因为 this 只获取最近一层调用的上下文对象, 即 obj2,
所以结果为 30
隐式丢失(函数别名)
注意: 这里存在一个陷阱, 大家在分析调用过程时, 要特别小心
- function fn() {
- console.log( this.a );
- }
- var a = 2;
- var obj = {
- a: 3,
- b: fn
- };
- var bar = obj.b;
- bar(); //?
bar() 打印结果为 2
为什么会这样, obj.b 赋值给 bar, 那调用 bar()为什么没有触发隐式绑定, 使用的是默认绑定呢.
这里有个概念要理解清楚, obj.b 是引用属性, 赋值给 bar 的实际上就是 fn 函数(即: bar 指向 fn 本身).
那么, 实际的调用关系是: 通过 bar 找到 fn 函数, 进行调用. 整个调用过程并没有 obj 的参数, 所以是默认绑定, 全局属性 a.
隐士丢失(回调函数)
- function fn(){
- console.log(this.a)
- }
- var a=20;
- var obj = {
- a:20,
- b:fn
- }
- setTimeout(obj.b, 2000);
- function setTimeout(cb,t){
- // t
- cb() //?
- }
cb()打印结果为 20
同样的道理, 虽然参传是 obj.fn, 因为是引用关系, 所以传参实际上传的就是 fn 对象本身的引用. 对于 setTimeout 的调用, 还是 setTimeout -> 获取参数中 fn 的引用参数 -> 执行 fn 函数, 中间没有 obj 的参与. 这里依旧进行的是默认绑定.
3. 显示绑定(执行)
- function fn(){
- console.log(this.a)
- }
- var obj1= {
- a:10
- }
- var obj2 ={
- a:20
- }
- var obj3 ={
- a:30
- }
- fn.bind(obj1)();
- fn.call(obj2);
- fn.apply(obj3);
fn.bind(obj1)(); 打印结果为 10
fn.call(obj2); 打印结果为 20
fn.apply(obj3); 打印结果为 30
这里要注意 fn.bind(obj1)后面还要加一个括号才执行!
4.new 绑定(构造函数执行(通过 new 执行))
JS 中的 new 操作符, 和其他语言中 (如 JAVA) 的 new 机制是不一样的. JS 中, 它就是一个普通函数调用, 只是被 new 修饰了而已.
使用 new 来调用函数, 会自动执行如下操作:
如果函数没有返回其他对象, 那么 new 表达式中的函数调用会自动返回这个新对象.
从这点可以看出, this 指向的就是对象本身.
- function foo(a) {
- this.a = a;
- }
- var a = 2;
- var bar1 = new foo(3);
- console.log(bar1.a); // ?
- var bar2 = new foo(4);
- console.log(bar2.a); // ?
因为每次调用生成的是全新的对象, 该对象又会自动绑定到 this 上, 所以打印结果分别为 3,4
绑定规则优先级
优先级是这样的, 以按照下面的顺序来进行判断:
数是否在 new 中调用(new 绑定)? 如果是的话 this 绑定的是新创建的对象.
数是否通过 call,apply(显式绑定)或者硬绑定调用? 如果是的话, this 绑定的是 指定的对象.
数是否在某个上下文对象中调用(隐式绑定)? 如果是的话, this 绑定的是那个上下文对象.
果都不是的话, 使用默认绑定. 如果在严格模式下, 就绑定到 undefined, 否则绑定到 全局对象.
来源: https://www.cnblogs.com/mhcll/p/11439900.html