JS面试(五):this详细介绍

五、this详细介绍

5.1、this到底是什么?

  • 当一个函数被调用时,会创建一个执行上下文(具体概念参见我之前的文章:《JS面试(三)详细介绍闭包概念》)。这个执行上下文会记录包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this就是记录的其中一个属性。
  • this在函数被调用时发生绑定,它指向什么完全取决于函数在哪里被调用

5.2、this的绑定规则(一)、默认绑定

  • 非严格模式下,函数被独立调用时,this指向全局对象(注:在严格模式下,会绑定到undefined)
    例1:

//非严格模式
var a = "window_a"
function foo(){
    console.log(this.a)
}
foo()//"window_a"

//严格模式
var a = "window_a"
function foo(){
    "use strict"
    console.log(this.a)
}
foo()//undefined
  • 再来举另外一个例子:
    例2:

var a = 'window'
function foo(){
    let a = 'inner'
    function innerfoo(){
        console.log(this.a)
    }
    innerfoo()
}

foo()//window
    • 在这里例子中,我们先调用foo函数,接在在它的执行上下文中调用了innerfoo函数,这时候innerfoo函数没有被任何对象引用,所以根据默认绑定,this被绑定到全局作用域对象中

5.3、this的绑定规则(二)、隐式绑定

  • 当有上下文对象引用该函数时,隐式绑定会把函数调用中的this绑定到这个上下文对象中
    例3:

function foo(){
    let a = 3
    console.log(this.a)
}

let obj = {
    a:2,
    foo:foo
}
obj.foo()//2
  • 注意:在对象的引用链中只有最后一层会影响函数的调用位置
    例4:

let obj2 = {
    a : 3,
    foo:foo
}

let obj1 = {
    a : 2,
    obj2:obj2
}

function foo(){
    console.log(this.a)
}

obj1.obj2.foo()//3
  • 下面讲一个比较坑的例子:
    例5:

1let obj = {
2:    a :'inner',
3:    foo:foo
4}
56var a = 'window'
78function foo(){
9:    chosole.log(this.a)
10}
1112let bar = obj.foo
13:bar()//window
    • 在这个例子中,在第12行中,我们只是将obj.foo定义的函数赋值给了bar,并没有调用。然后在第13行中,调用了bar函数
    • 调用bar函数的位置是在全局作用域中调用了,此时this是默认绑定,这时候它就被绑定到了全局对象中的a属性中

  • 我来再讲另一个比较坑的例子:
    例6:

1let obj = {
2:    a:'inner'3:    foo:foo
4}
5var a = 'window'
67function foo(){
8:    console.log(this.a)
9}
1011function dofoo(foo){
12foo()
13}
1415:dofoo(obj.foo)//"window""
    • 此时需要注意的一点便是,函数的参数传递是按值传递的,是一种值的赋值
    • 所以,在第11到13行中,相当于:
function dofoo(foo){
    let bar = foo
    bar()
}
    • 所以在第15行中,相当于,在全局作用域中,执行了foo定义的函数。与例5相同

8.4、this的绑定规则(三)、显示绑定

  • 在上面的隐式绑定中,this的绑定对象是根据函数调用的位置来决定的,这是一种间接的方式。如果我们想把this强制绑定到我们想绑定的对象上,我们可以使用apply()、call()、bind()方法,来实现(即显示绑定)

  • 我来讲一下apply()、call()、bind()三类方法的区别:

  • apply()方法调用一个函数,并为其指定一个this值。它接收两个参数,第一个参数为指定的this值,它可以是我们指定的对象,也可以是null或undefined(这时候this指向全局对象)
  • 第二个参数是一个数组对象,其中的数组元素将作为单独的参数传给调用的函数
    例7:

function foo(b){
    console.log(this.a,b)
    return this.a + b
}

let obj =  {
    a:2
}

var a = 10

let bar = function(){
    return foo.apply(obj,arguments)
}

let res = bar(3)//2 3
console.log(res)//5

  • call()与apply()基本相同,第一个参数都是指定的this值,但是它的第二个参数不是数组,它的第二个参数以及后面的参数,接收到的是若干个单独的参数,然后将这些参数传递给调用的函数
    例8:

function foo(b){
    console.log(this.a,b)
    return this.a + b
}

let obj =  {
    a:2
}

var a = 10

let bar = foo.call(obj,3)//2 3
console.log(bar)//5

  • bind()相当于一个语法糖,它创建一个新的函数,当被调用时,将this关键字设置为提供的值,在调用新函数时,在任何之前提供提供一个给定的参数序列。这个语法糖相当于:
function bind(func,obj){
    return function(){
        return func.apply(obj,arguments)
    }
}

例9:

function foo(b){
    console.log(this.a,b)
    return this.a + b
}

let obj =  {
    a:2
}

var a = 10

let bar = foo.bind(obj,3)
let baz = bar()//2 3 
console.log(baz)//5

8.5、this的绑定规则(四)、new绑定

  • 首先需要说明:使用new来调用函数时,JS引擎会自动执行下面的操作
    • 创建一个全新的对象
    • 这个新对象会被执行[[prototype]]链接(具体会在后面的关于对象和原型的文章中详细介绍)
    • 这个新对象会绑定到函数调用的this
    • 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这对象
      例10:

function foo(a) {
    this.a = a;
}
var bar = new foo(2);
console.log( bar.a ); // 2

8.6、箭头函数的this绑定规则

  • 上面的例子中,我们介绍了四种this绑定规则,但是这些对于箭头函数来说都不适用!!!
  • 箭头函数的没有this绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则this继承最近一层的非箭头函数的this,否则,this为undefined。
    例11:

var name = "windowsName";

var a = {
    name : "Cherry",

    func1: function () {
         console.log(this.name)     
    },

     func2: function () {
        setTimeout( () => {
            this.func1()
        },100);
    }

};

a.func2()     // Cherry
  • 在这个例子中,箭头函数被非箭头函数func2包含,所以箭头函数的this便是func2的this,因为func2在对象a中引用,所以fun2的this指向a,所以箭头函数的this也指向a
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值