大无语事件!这个是哪个?this是这个...

220 阅读5分钟

emmmm,今天碰到一个让我脑子转圈圈的内容,this的指向。在打吃鸡的时候经常会碰到这种问题:队友被人打到了,我就在疯狂的问他,对面的人在哪里?队友说,在那里。在哪个那里?就在那个石头后面。在哪个石头后面?在树旁边的那个石头后面······服啦啊。没想到在代码中也能碰这样的问题,这让我一个没有方向感的人,很是为难啊!

没办法,为了以后不难受,还是要好好理清楚一下this的指向问题,不然以后都飞起来了找不到到底谁是谁了。

了解this

this是一个特殊的关键字,它在JavaScript中代表函数执行时的上下文环境,即函数调用时所属的对象。this的值不是在编写函数时决定的,而是在函数被调用时根据调用方式动态确定的。this有以下的作用

  • 为了让对象中的函数有能力访问对象有自己的属性
  • this可以显著的提升代码质量,减少上下文参数的传递

this的指向规则

this可以写在全局和函数体内。 写在全局的this就是window。

var a = 1
console.log(this.a);  //这里在浏览器中是1 和window.a 一样

将这段代码在浏览器中运行,打印出来的就是1。只要写在全局的this,他就是window。这个还是比较好记的,但是当this写到函数中就有很多门道了。 当我们要讨论函数中的this指向问题,我们就需要了解this指向的一些固定规则。

1. 默认绑定: 当一个函数独立调用时,即不带任何修饰符的调用,就看这个函数在那个词法作用域中生效,(就是函数调用在哪)this就指向那。但是可以很明确的告诉大家,当函数独立调用时,该函数的this必指向window ,如下面这段代码

function foo (){
    var a = 1
    console.log(this.a);  // this 是全局
}
foo()

我们的函数是独立调用的,大家看看这个打印出来是什么?不瞒大家说,我一开始就觉得这肯定打印出来的是1,但是结果是undefined。因为此时的this指向全局,而全局里面并没有a的定义。如果我们此时在全局中定义个a,那么打印出来的就是全局的那个a的值。

image.png

2.隐式绑定: 当一个函数被某个对象所拥有,或者函数被某个上下文对象调用时,该函数中的this指向该上下文对象

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

此时this指向obj这个对象,所以他可以打印出来为1

3.隐式丢失: 当一个函数被多个对象链调用时,this指向最近的那个对象

var obj = {
    a:1,
    foo:foo
}
var obj2 = {
    obj:obj,
    a:2
}
function foo(){
    console.log(this.a);
}
obj2.obj.foo()  // 就近原则

如果一个函数在对象中定义,而这个对象又在obj2中定义,那么此时foo中的this指向什么呢?这就跟我们学高中英语的时候学到的就近原则,指向obj对象,那么此时obj2就丢失了,这就叫隐式调用。

4.显示绑定: 通过call、apply、bind方法来改变this的指向。 在一些情况下我们需要人为的将this指向我们想要指向的对象中,我们就可以使用call、apply、bind方法来改变this的指向。

var obj = {
    a:1
}

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

function foo(){
    console.log(this.a);
}
foo.apply(obj)  

这两个方法都是定义在Function的原型上面的,且都可以改变this的指向到指定的对象中去。但是这两个还是有区别的。唯一的区别就是在参数传递上面, call的参数是比较正常的,就是我们常见的参数传递,用,隔开,像这样,如果foo还接收两个参数a,b,那么在函数调用时foo.call(obj,1,2)这样就可以了 。但是apply接收参数是以数组的形式,即foo.apply(obj,[2,3])

bind方法与call和apply就不相同了,他返回的是一个函数,需要调用才可以。

const bar = foo.bind(obj,1,2)
bar()

那有同学又要问了,嗯?我bar也可以传参数,bind也可以传参数,那我两个都传会怎么样呢?其实这里也符合就近原则,bar里面的参数就被忽略了。

5.new绑定:this指向新创建的实例对象

就是在我们的构造函数中添加属性的时候,使用this时,当我们创建这个实例对象时,this就指向我们新创建的实例对象。

function Phone() { 
    this.name = 'iphone 12';
} 
const iphone = new Phone();
console.log(iphone.name); // iphone 12

5.箭头函数:箭头函数没有自己的this这个机制,写在箭头函数中的this也是那外层非箭头函数的。

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

fn中没有this机制,所以到foo里面去找this,foo中有this,所以此时this就指向foo的词法作用域。所以此时打印出来的就是1。

总结

一个this也就只有有这么多的规则,实在理解不了就把这些规则记下来,哈哈哈哈哈。我一般是这样,但是还是建议大家把这个弄懂。我就是因为没弄懂这个,所以好多指向的问题都是稀里糊涂的,所以我就回过头来在看一看,总结总结。看懂了之后还是讲不出来,还是有点尴尬的哈。晚安啦,大家早点睡奥!