this this,你到底指向谁

1,082 阅读6分钟

一:this为什么存在?

1.1:利用this访问对象属性

阅读下面的代码,我们想要在在对象内部方法中使用对象内部的属性,但在js中没有下面这样的语法,必须将第五行的console.log(myname)改为console.log(this.myname)或者console.log(obj.myname),才能输出‘midsummer’

let obj={
    myname:'midsummer',
    age:18,
    bar: function(){//在对象内部方法中使用对象内部的属性
        console.log(myname);
    }
}
obj.bar()

在JavaScript等面向对象的编程语言中,this关键字允许对象中的函数(或方法)访问其所属的对象的属性。通过this,方法可以在不显式传递对象本身作为参数的情况下,引用并操作对象的内部状态。

1.2:使用this提升代码质量

this可以显著的提升代码质量,减少上下文参数的传递,请读者比较下面两份代码,其中toUpperCase()方法是把传入参数转为大写

function identify(context){
    return context.name.toUpperCase();
}

function speak(context){
    var greeting = identify(context) + ' is beautiful';
    console.log(greeting);
}

var me = {
    name: 'midsummer'
};

speak.call(me);

第一份代码,我们需要向identify函数和speak函数显式的传参。这样的写法不够优雅,在有多个函数进行该参数的传递时,倘若其中一环参数出了问题,会带来一系列的影响,用这种方法,难以维护。接下来我们用this来修改代码,可以显著的提升代码质量,减少上下文参数的传递

function identify(){
    return this.name.toUpperCase();
}

function speak(){
    var greeting = identify.call(this) + ' is beautiful';
    console.log(greeting);
}

var me = {
    name: 'midsummer'
};

speak.call(me);

this避免了在调用对象方法时显式传递对象本身作为参数,减少了代码的冗余,增强了代码的可读性和可维护性。此外,this还有助于实现封装和隐藏对象内部状态,增强数据的安全性。

二:this的绑定规则

this是一个关键字,是一个代词,也就是有指向的

2.1:默认绑定

  • 默认绑定:当一个函数独立调用,不带任何修饰符的时候
  • 函数在哪个词法作用域下生效,函数中的this就指向哪里
function foo(){//foo独立调用
    console.log(this);//this指代window(全局)
}
foo();

以上代码中,foo独立调用,foo的词法作用域在全局,故this指代window(全局)。这里的函数作用域与词法作用域相同,都是全局,但在更复杂一点的代码中,容易混淆,请读者们阅读下面代码,注意函数作用域与词法作用域的区别

词法作用域:由函数声明的位置来决定,跟函数在哪里调用没有关系

function foo(){//foo独立调用
    console.log(this);//this指代window(全局)
}
function bar(){
    foo()
}
function baz(){
    bar()
}
baz()

函数foo,bar,baz,的词法作用域都是全局,所以,这里的this,还是指代window(全局)。其实,只要在默认绑定规则下,读者只需要记住一句话:只要是默认绑定,this一定指向windows

2.2:隐式绑定

  • 隐式绑定:当函数的引用有上下文对象时(当函数被某个对象所拥有时)
  • 函数的this指向引用它的对象

请读者阅读下面代码,其中函数foo在对象obj中是引用,而非独立调用,属于隐式绑定,故函数的this指向对象obj,输出1

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

在隐式绑定中,要认准函数的this指向引用它的对象,上述代码中,函数foo在对象obj中引用,那对象obj是函数foo的‘老大’。倘若对象obj也有‘老大’,那就是函数foo‘老大的老大’,那this指向会指向‘老大的老大’吗?答案是不会的

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

2.3:显式绑定

同样一份代码,现在我们用显式绑定方法实现使函数foo中的this指向对象obj。foo.call(obj),利用call()方法,强行将foo的this指向对象obj。(至于call()是如何实现的也是我们需要掌握的,这涉及原型相关概念,会在之后的文章中详细解释。)

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

如果需要传入参数,显示绑定要如何实现?简单修改上例代码:

var obj = {
    a:1
}
function foo(x,y){
    console.log(this.a,x+y)
}
foo.call(obj,2,3)

以上展示的是用call进行显式绑定,并接收参数,实际上显示绑定有以下三种方法:

  • callfoo.call(obj,2,3),直接将参数以逗号隔开,接在obj后

  • applyfoo.apply(obj,[2,3]),与call唯一不同点就是参数以数组的形式展示

  • bind

    • var bar = foo.bind(obj,2,3,4) bar(),bind中放实参,只读取前几需要的参数
    • var bar = foo.bind(obj,2,3) bar() bind中放全部的参数
    • var bar = foo.bind(obj,2) bar(3,4)bind中放一部分,bar中放一部分,当参数多了,就近选择需要个数的代码,这个例子即选取2和3

2.4:new绑定

  • new绑定:this指向创建出来的实例对象

阅读以下代码,思考构造函数中的this指向谁

function Person(){
    this.name = 'midsummer'
}
let p = new Person()//实例对象

在这份代码执行时,会发生以下:

  1. new会在函数中创建一个对象,假设这个对象叫obj, var obj = { name:'midsummer'}
  2. Person.call(obj),让构造函数的this,显示绑定到对象上
  3. 执行构造函数中的this.name = 'midsummer'
  4. 实例对象继承构造函数原型上的方法Object_proto_ = Person.prototype
  5. 返回对象return obj

三:箭头函数与this

  • 普通函数:function foo(){}
  • 箭头函数:var bar = () => {}

this写在全局和函数体内,(箭头函数)不承认this,没有this这个概念,但可以有this

写在箭头函数中的this是它外层非箭头函数的this

根据以上准则,阅读以下代码,试判断,箭头函数中的this指向谁

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

分析可知,箭头函数中的this是b:function(){}的,即在问b:function(){}中的this指向哪?那自然是指向对象obj。obj在调用b时,执行console.log(this.a),相当于打印obj.a,所以输出1

四:知识点get:

  1. 为什么要有this:

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

    • 默认绑定:当一个函数独立调用,不带任何修饰符的时候

      • 函数在哪个词法作用域下生效,函数中的this就指向哪里(只要是默认绑定,this一定指向windows)
    • 隐式绑定:当函数的引用有上下文对象时(当函数被某个对象所拥有时)

      • 函数的this指向引用它的对象
    • 隐式丢失:当一个函数被多个对象链式调用时,函数的this指向就近的那个对象

    • 显式绑定:

      • call
      • apply
      • bind
    • new绑定:

      • this指向创建出来的实例对象
  3. 箭头函数 箭头函数没有this这个机制,写在箭头函数中的this也是它外层非箭头函数的this