JavaScript this指向

目录

默认绑定

let、const、var 变量的默认绑定

隐式绑定

隐式丢失

new构造函数绑定

箭头函数绑定

 call(),apply(),bind()显示绑定


默认绑定

非严格模式下,this 指向全局对象(window / self / global)

严格模式下,函数内的 this 指向 undefined,全局中的 this 指向不会改变

let、const、var 变量的默认绑定

let、const 声明的全局变量,不会被绑定到 window 上

var 声明的全局变量,会被绑定到 window 上 

let a = 10
const b = 20
 
function foo () {
  console.log(this.a) // undefined
  console.log(this.b) // undefined
}
 
foo();
 
console.log(window.a) // undefined  

隐式绑定

当函数引用有上下文对象时,如 obj.foo() 的调用方式,foo() 内的 this 指向 obj

也就是说,谁调用函数,函数内的this就指向谁(无论是普通对象、还是全局对象),this永远指向最后调用它的那个对象不考虑箭头函数)

隐式丢失

隐式丢失:被隐式绑定 的函数,在特定的情况下会丢失绑定对象

容易发生隐式丢失的两种情况:

  • 使用另一个变量来给函数取别名

    取别名:使用一个变量(var foo2 = obj.foo),或一个对象(var obj2 = { a: 3, foo2: obj.foo } )

    如果发生了 this 隐式丢失,则使用上一层 this 替代丢失的 this(可能是 window,可能是 obj)

function foo () {
  console.log(this.a)
};
 
var obj = { a: 1, foo };
 
var a = 2;
 
var foo2 = obj.foo;
 
var obj2 = { a: 3, foo2: obj.foo }
 
// -----------------------------------------------------------------
 
// 此处的 this 指向 obj,被 obj 调用进行了隐式绑定,因此打印 obj 中的 a
// this 指向调用者 obj
obj.foo(); // 1
 
// foo2 指向了 obj.foo 函数
// 由于使用 另一个变量 foo2 给函数取别名,发生了 this 丢失,导致 obj 丢了
// obj 丢了,就找上层 this;是 window 调用了 foo2,除了 obj 外,上层就是 window 了
// 综上:foo2() 发生了隐式丢失,调用者是 window,使得 foo() 中的 this 指向 window
foo2(); // 2
 
// obj2.foo2() 发生了隐式丢失,调用者是 obj2,使得 foo() 中的 this 指向 obj2
obj2.foo2(); // 3

将函数作为参数传递时,会被隐式赋值,回调函数丢失 this 绑定,如 setTimeout

将函数作为参数传递时,会发生的隐式丢失,与包裹着 函数参数 的 外层函数 的 this 指向无关(比如外层函数的 this 指向 obj2对象,但是隐式丢失后,this 和 obj2 不会有关系,只跟 window/undefined 有关系)

在非严格模式下,隐式丢失后,会把函数的 this 绑定到 window 上;

在严格模式下,会把函数的 this 绑定到 undefined 上;

发生隐式丢失后,永远不会把函数的 this 绑定在 其他对象 上;

非严格模式下:

function foo () {
  console.log(this.a)
}
 
function doFoo (fn) {
  // 因为这里是 window 调用的它,所以 doFoo() 函数内的 this 本来就指向 window
  console.log(this) // Window{...}
 
  // 函数当参数传入,发生了隐式丢失(也就是 obj 没了),因此 foo 指向了 window
  fn() // 2
}
 
var obj = { a: 1, foo }
 
var a = 2
 
var obj2 = { a: 3, doFoo }
 
// 将 obj.foo 当成参数传递到 doFoo 函数中,发生了隐式丢失,因此 foo 指向了 window
doFoo(obj.foo)
 
// doFoo 此时被 obj2 调用,this 指向 obj2
// 但是由于 obj.foo 函数被当作参数传入,导致 this 隐式丢失
// 隐式丢失 与包裹着的函数 this 没有关系,也就是和 obj2 没有关系
// 所以指向 window,打印 window 中的 a,而不是 obj2 中的 a
obj2.doFoo(obj.foo) // 2

 严格模式下:

"use strict"
 
function foo () {
  console.log(this.a)
}
 
function doFoo (fn) {
  // 被 obj2 调用,this 指向 obj2
  console.log(this) // { a:3, doFoo: f }
 
  // 函数当参数,发生 this 隐式丢失,隐式丢失和 函数参数 的 外层函数this 没有关系
  // 因此指向 window,也就是 window.a = 2
  // 又因为 严格模式 函数内的 this 始终为 undefind,因此获取不到 a 变量,报错
  fn() // Uncaught TypeError: Cannot read property 'a' of undefined
}
 
var obj = { a: 1, foo }
 
var a = 2
 
var obj2 = { a: 3, doFoo }
 
obj2.doFoo(obj.foo)

setTimeout:

var obj2 = {
  a: 2,
 
  foo1: function () {
    // 普通函数内的 this 指向调用它的对象
    console.log(this.a) // 2
  },
 
  foo2: function () {
    // setTimeout 回调中的 this(对象调用绑定的 this),会丢失,因此指向 window
    setTimeout(function () {
      console.log(this) // window
      console.log(this.a) // 3
    }, 0)
  }
}
 
// 全局变量
var a = 3
 
obj2.foo1()
// 虽然是被 obj2 调用的,但是由于定时器接收函数作为参数,导致 this 丢失
// 因此指向的 obj2 丢失,因此最终指向了 window
obj2.foo2()

new构造函数绑定

this指向新生的对象

var name = 'aaa';
 
// 构造函数
function Person (name) {
  this.name = name
  this.foo1 = function () {
    console.log(this.name)
  }
  this.foo2 = function () {
    return function () {
      console.log(this.name)
    }
  }
}
 
var person1 = new Person('Test');
 
// 打印的是对象里的值
person1.foo1() // Test
 
// 执行匿名函数,是 window 调用的,因此打印 window 下的 name
person1.foo2()() // aaa

箭头函数绑定

箭头函数内的 this 始终由外层作用域决定;无法被 call 等方式修改;在定义时就已经确定 this 指向了,执行时无法改变 this 指向

var obj = {
 name: 'obj',
 
 // 箭头函数
 foo1: () => {
   console.log(this.name) // window
 },
 
 // 普通函数
 foo2: function () {
   console.log(this.name) // obj
   // 箭头函数
   return () => {
     console.log(this.name) // obj
   }
 }
}
 
// 全局变量
var name = 'window'
 
// 对象 obj 不属于作用域(作用域只有全局作用域、函数作用域、块级作用域)
// foo1() 箭头函数的外层作用域是 window,所以会打印出 window
obj.foo1()
 
// 首先会执行 obj.foo2(),这不是箭头函数,所以它里面的 this 指向调用它的 obj 对象
// 返回的匿名函数是一个箭头函数,它的 this 由外层作用域决定,也就是 foo2 的函数作用域
obj.foo2()()

 call(),apply(),bind()显示绑定

作用:主要用来改变this指向

call()的用法

调用fn.call时会将fn中的this指向修改为传入的第一个参数thisArg;将后面的参数传入给fn,并立即执行函数fn

fn.call(thisArg, arg1, arg2, arg3, ...)

let obj = {
        name: "xiaoming",
        age: 24,
        sayHello: function (job, hobby) {
            console.log(`我叫${this.name},今年${this.age}岁。我的工作是: ${job},我的爱好是: ${hobby}。`);
        }
    }
    obj.sayHello('程序员', '看书'); // 我叫xiaoming,今年24岁。我的工作是: 程序员,我的爱好是: 看书。


    let obj1 = {
        name: "lihua",
        age: 30
    }
    // obj1.sayHello(); // Uncaught TypeError: obj1.sayHello is not a function
    obj.sayHello.call(obj1, '设计师', '画画'); // 我叫lihua,今年30岁。我的工作是: 设计师,我的爱好是: 画画。

apply()的用法

fn.apply的作用和call相同:修改this指向,并立即执行fn。区别在于传参形式不同,apply接受两个参数,第一个参数是要指向的this对象,第二个参数是一个数组,数组里面的元素会被展开传入fn,作为fn的参数。

apply(thisArg, [argsArr])

let obj = {
        name: "xiaoming",
        age: 24,
        sayHello: function (job, hobby) {
            console.log(`我叫${this.name},今年${this.age}岁。我的工作是: ${job},我的爱好是: ${hobby}。`);
        }
    }
    obj.sayHello('程序员', '看书'); // 我叫xiaoming,今年24岁。我的工作是: 程序员,我的爱好是: 看书。


    let obj1 = {
        name: "lihua",
        age: 30
    }
    
    obj.sayHello.apply(obj1, ['设计师', '画画']); // 我叫lihua,今年30岁。我的工作是: 设计师,我的爱好是: 画画。

 bind()的用法

bind(thisArg, arg1, arg2, arg3, ...)

 fn.bind的作用是只修改this指向,但不会立即执行fn;会返回一个修改了this指向后的fn。需要调用才会执行:bind(thisArg, arg1, arg2, arg3, ...)()。bind的传参和call相同。

let obj = {
        name: "xiaoming",
        age: 24,
        sayHello: function (job, hobby) {
            console.log(`我叫${this.name},今年${this.age}岁。我的工作是: ${job},我的爱好是: ${hobby}。`);
        }
    }
    // obj.sayHello('程序员', '看书'); // 我叫xiaoming,今年24岁。我的工作是: 程序员,我的爱好是: 看书。

    let obj1 = {
        name: "lihua",
        age: 30
    }
    
    obj.sayHello.bind(obj1, '设计师', '画画'); // 无输出结果
    obj.sayHello.bind(obj1, '设计师', '画画')(); // 我叫lihua,今年30岁。我的工作是: 设计师,我的爱好是: 画画。

bind()、call()、apply()的区别

相同点 1.三个都是用于改变this指向; 2.接收的第一个参数都是this要指向的对象; 3.都可以利用后续参数传参。 不同点 1.call和bind传参相同,多个参数依次传入的; 2.apply只有两个参数,第二个参数为数组; 3.call和apply都是对函数进行直接调用,而bind方法不会立即调用函数,而是返回一个修改this后的函数。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值