• 作者:老汪软件技巧
  • 发表时间:2024-10-10 10:04
  • 浏览量:

this的身世

JavaSctipt中的this指向是一个非常让人困惑的问题,他是JavaScript中重要的关键字,也是面试官们爱翻牌的“宠妃”。

首先我们需要知道this他是一个代名词,不管他走到哪里,他都是代表着某个东西,他代表的东西,或者说他的含义会在不同的语境中有所不同。

我们先来看一段简单的代码

let obj = {
    myname: '段总 ',
    age: 18,
    say: function () {
         console.log(obj.myname);
        console.log(this.myname);
    }
}
obj.say()

可以看到我们的obj.name = this.name,this代表obj,是可以这样用,但是这个例子我们看不到this关键字的作用体现在哪。

我们再看一个例子,下述有两段代码

function identify(context) {
    return context.name.toUpperCase();
}
function speek(context) {
     var greeting = 'Hello i am ' + identify(context)
    console.log(greeting);
}
var p = {
    name: 'tom'
}
 speek(p)

上面的代码没有使用this,上下文中,参数p一直在传递,先传给speek(),再传递给identify()

function identify() {
    return this.name.toUpperCase();
}
function speek() {
    var greeting = 'Hello i am ' + identify.call(this)
    console.log(greeting);
}
var p = {
    name: 'tom'
}
speek.call(p)

下面的代码中使用了一个call()方法,我们先不讲解这个call方法(后面会提),我们能看到第二段代码减少了上下文参数的传递,而且很明显代码质量提升了,也很简洁.

this虽好,但是不是所有地方都可以使用这个关键字,呢么哪里可以使用this呢

哪里可以写this1.全局

this虽然可以使用在全局中,但是他在全局中的使用很鸡肋,因为它在全局中的使用,在node环境下就是global,在浏览器环境下就是window

  function foo() {
            var a = 1
            console.log(this);
        }
        foo()

2.函数体内

在函数体内就有很多门道了,下面我们一一来讲解

我们分this的指向来讨论

一.默认绑定

当一个函数被独立调用时,不加任何修饰符的调用

  var a =2
 function foo() {
    var a = 1
    console.log(this.a); //函数在哪个词法作用域里生效
}
foo()//独立调用  

什么是独立调用,就是一个函数被直接调用,而不是作为一个对象的方法被调用,当时独立调用的时候,就为默认绑定,this的指向取决于函数在哪个词法作用域里生效,词法作用域就是该函数声明的地方,他声明于全局中所以输出2

   var a = 2
 function foo() {
     var a = 1
     function bar() {
         console.log(this.a);
     }
     bar()// 在foo的作用域里生效的 在foo所处在的词法作用域里生效
 }
 foo()

上面的bar函数在foo的作用域里生效,所以bar在foo所处在的词法作用域里生效,所以this还是全局,输出仍为2

var a = 1
function foo() {
var a = 2
function bar() {
    var a = 3
    function baz() {
        console.log(this.a);
    }
    baz() //在bar的作用域里生效. baz this ==> bar this
}
bar()
}
foo()

因为baz在bar的作用域里生效. baz this ==> bar this又 bar在 foo的作用域里生效 bar this ==> foo this我们就可以这样理解 词法作用域是有指向性的 你在bar的词法作用域里,而bar在foo的词法作用域里,呢么你就在foo的词法作用域里,就像你在北京,呢么你也在中国.所以最后我们的指向都是全局,该所以输出的是1

最后我们可以得到一个结论

默认绑定 : 当一个函数独立调用时,不带任何修饰符的调用,该函数的this指向window

二.隐式绑定

当一个函数作为对象的方法被调用时,该函数中的"this"会隐式绑定到调用他的对象上 详细的说就是 当一个函数被某个对象所拥有,或者函数被某个上下文对象调用时,该函数中的this指向该上下文对象

const obj = {
 name: 'Alice',
 sayName: function() {
   console.log(this.name);
 }
};
obj.sayName(); // 输出 'Alice'

在这个例子中,函数sayName作为对象obj的方法被调用,所以在sayName函数内部,“this” 指向了调用它的对象obj。

三.隐式丢失

但是当一个函数被多个对象链式调用时,this指向最近的呢个对象,他也遵守隐式绑定.

function foo() {

        console.log(this.a);
    }
    var obj = {
        a: 1,
        foo: foo
    }
    var obj2 = {
        a: 2,
        obj: obj
    }
    obj2.obj.foo() //听老大的不听老大的老大的

输出的是离函数最近的对象

四.显示绑定

通过 call apply bind 将函数的this掰弯到一个对象中

我们先来看一段代码

var obj = {
    a: 1
}
function foo() {
    console.log(this.a);
}
foo()

毫无疑问foo是默认绑定,所以this是指向全局对象,全局对象里没有a,所以输出undefined,这个时候我们就非要打印出obj对象里a的值我们可以怎么做呢,这个时候就需要用到开头写的代码里的call().

 var obj = {
        a: 1
    }
    function foo() {
        console.log(this.a);
    }
    foo.call(obj)

call可以改变this的指向,foo.call(obj)就将foo的this从全局变量转向了对象obj

function.call(thisArg, arg1, arg2,...)

let obj = {
        num1: 1,
        num2: 1
    }
    function add(a, b) {
        console.log(this.num1 + this.num2 + a + b);
    }
    add.call(obj, 1, 1)

用法如上.

function.apply(thisArg, [argsArray])

apply()的用法与上述无异,就是第二个传入的参数,需要指定为数组

然后就是bind(),,它创建一个新的函数,这个新函数在被调用时会具有指定的this值和初始参数。就是他会返回一个函数对象,其他的与 call()无异

5.New绑定

我们先来看一段代码

function Person(){
this.name="彭于晏"
}
let p = new Person()

new Person()可以分为以下几个步骤

如果上述操作是返回obj,如果我在Person函数体内return 'HELLO'的话,他还会返回一个obj对象吗我们来试一试

返回的仍然是obj对象如果是return []数组呢

他就返回数组了再换成{},和function试试

所以我们可以得到一个结论,如果构造函数里如果有return,return的是引用类型就会覆盖掉new里面的return,不是引用类型就无关痛痒**

补充

如果出现了箭头函数,我们需要另行判断,首先我们需要知道匿名函数里没有this,他不承认有this这个关键字,但他有this这套机制.

var obj = {
    a: 1,
    foo: function () {
        const fn = () => {
            console.log(this.a);
        }
        fn()
    }
}
obj.foo()

他只能从外层的执行环境中继承this的值,所以他会继承foo()的,输出1

 const a = () => {
    const b = () => {
        const c = () => {
            console.log(this);
        }
        c()
    }
    b()
}
a()

这里会输出全局对象,因为全是箭头函数都不存在this,所以我们得到了这个结论

箭头函数中没有this这个机制,写在箭头函数中的this在也是外层非箭头函数的

Ending

讲到这里我们就学完了this的五种基本判断,相信你如果搞懂了,就不会再被他的指向问题搞得晕头转向了,有问题可以在评论区大家一起讨论!