面向对象语言中的this问题解析

44 阅读3分钟

在JavaScript中为什么会存在this,它是什么?有什么用?在JavaScript中为,this为什么会发生变化?

在面向对象的编程语言中,我们常常能够在函数或者方法中使用this或者self等关键词,在这里self/this指当前所在对象。
让我们先以一个实际例子开始:

面向对象语言中,以Objective-C为例,我们先定义一个类。

    main.m
  @interface ClassA : NSObject   
    @property(nonatomic,copy)NSString * name;   
    -(void)sayName;   
    +(void)SayHello;     
    @end   
    
    @implementation ClassA   
    -(void)sayName {  
    // 伪代码
        NSLog(self.name);   
    }   
    +(void)SayHello { 
        NSLog(@"class say hello"); 
    }   
    @end
    
    int main(int argc, const char * argv[]) {   
        @autoreleasepool {   
            NSLog(@"Hello, World!");   

            ClassA * obj = [[ClassA alloc]init];  
            obj.name = @"zhang san";
            [obj sayName];   
        
        }   
    return 0;   
    }

 

现说结果:上面的代码会打印zhang san
在这里,self就代表着obj,也就是方法sayName 所在的对象。 在Objective-C中,你可以在实例方法(-开头)中直接调用self,但是在类方法(+开头)中调用selfXcode会报错。
上面说了,在OC的类方法中是不能直接调用self关键字。因为OC被设计为一个纯粹面向对象的语言,是不存在函数这一概念的,只有对象和方法。
在JavaScript中就不一样了。JavaScript并不是一个纯粹的面向对象语言,它即支持面向对象编程又支持函数式编程的范式。因此,函数被设计为一等公民,可以直接执行,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。它可以独立于对象而存在
同时,javaScript为了支持面向對象的编程范式,因此在函数中内置了关键字this,通过this可以拿到函数当前的执行对象。代码如下:

function test () {
    console.log(this.name)
}
const name = 'li si'
test()

在浏览器中,上面的代码会打印li si。此时,函数没有处于对象中,那么他是如何找到this的呢?这里不得不提及一个概念,那就是环境。是的,在JS中,函数的执行可以不依赖对象,直接依赖环境。在这里,函数是在一个隐式的全局环境中被调用,这个环境不但定义了 test 函数和 name 变量,同时也调用了test,然后this的指向就呼之欲出了。

上面两段代码中无论是self还是this都没有作为参数,直接被传入,但是在方法/函数执行时,却能找到这个值,这是语言(运行时)引擎的功劳。我们可以看作selfthis被隐式传入了函数之中,引擎运行时会自动查找当前运行的对象/环境。
从上得知,以下几个问题的答案

  1. this是什么?答:语言内置的关键字,被隐式传入函数/方法中,运行时引擎会自动查找。
  2. this代表什么?答:当前方法/函数运行时所在的对象/环境。
  3. 在JavaScript中,this为什么会发生变化,而面向对象语言中不会?答:因为JS中,this的存在不受限制,函数的调用不受限制。this代表运行时所处对象,当执行时所处对象发生变化时,this也就发生变化了。而在面向对象语言中,self关键字只存在于对象的方法中,且只能被对象本身调用,所以只代表定义时所处的对象。

一些注意事项

  • 函数中定义函数,this不指向外部,而指向顶层对象。
  • 回调函数中的this指向会发生改变,因为回调函数的调用对象发生了变化
var o = {
  f1: function () {
    console.log(this);
    var f2 = function () {
      console.log(this);
    };
    return f2
  }
}

o.f1()()
// Object
// Window

var o = new Object();
o.f = function () {
  console.log(this === o);
}

// jQuery 的写法 
// 回调函数被DOM对象调用
$('#button').on('click', o.f);
// false