为什么要使用this

  1. function identify(){
  2. return this.name.toUpperCase();
  3. }
  4. let me = {"name":"peter"};
  5. identify.call(me);//call()方法是函数原型对象属性的方法,调用者为函数对象可以将一个对象指定为第一个参数,
  6. 此时这个对象将会成为函数执行时的this,从第二个参数起依次传入实参

上段代码可以在不同的上下文对象中重复使用函数identify,不用针对每个对象编写不同版本的函数,如果不适用this,那就需要给identify()显式的传入一个对象。

  1. function identify(object){
  2. return object.name.toUpperCase();
  3. }
  4. let me = {};
  5. me.name = "peter";
  6. identify(me);

this提供了一种更优雅的方式来隐式”传递”一个对象的引用,因此可以将API设计的更加简洁并且更易复用。

误解

指向自身

人们很容易把this理解成指向函数自身,但是this并不是指向函数自身。

  1. function foo(num){
  2. console.log("foo:"+num);
  3. this.count++;
  4. }
  5. foo.count=0;
  6. for(let i = 0;i < 10;i++){
  7. foo(i);
  8. }
  9. console(foo.count)//0;
  10. console(this.count)//10;
  11. console.log(window.count)//10----WFT?为什么this.count挂在window上了?

通过上面的例子你会发现this.count和foo.count不是一个东西。。。天啊!

this指向函数的作用域

第二种常见误解是,this指向函数的作用域。这个问题有点复杂了,因为在某种情况下他是正确的,但是在某些情况下他确实错误的。
需要明确的是,this在任何情况下都不指向函数的词法作用域。在JavaScript内部,作用域和对象类似,但是作用域”对象”无法通过JavaScript代码访问,他存在于JS引擎内部。

this全面解析

调用位置

在理解this绑定过程前,首先要理解调用位置:调用位置就是函数在代码中被调用的位置(不是被声明的位置)。最重要的是分析调用栈(就是为了达到当前执行位置所调用的所有函数)

绑定规则

默认绑定

首先要介绍的是最常用的函数调用类型:独立函数调用。

  1. function obj() {
  2. console.log(this.a);
  3. }
  4. let a = 2;
  5. obj();
  6. //2-----WTF?this.a访问到了a。

声明在全局作用域中的变量a就是全局对象的一个属性。但是当我们调用obj时,this.a被解析成了全局变量a。这是因为函数调用时应用了this默认绑定,因此this指向了全局对象。
但是如果使用严格模式,全局对象将无法使用默认绑定,因此this将会绑定到undefined。

隐式绑定

  1. function foo(){
  2. console.log(this.a);
  3. }
  4. let obj = {
  5. a : 2,
  6. foo : foo
  7. }
  8. obj.foo();//2

调用位置会使用obj上下文来引用函数,因此你可以说函数被调用时obj对象拥有或包含他。当函数调用有上下文时,隐式绑定规则就会把函数调用中的this绑定到这个上下文中。并且对象属性引用链中只有最后一层会影响调用位置。

显式绑定

JavaScript中的函数都有一些有用的特性,可以用来解决这个问题,具体点说,可以使用函数的call()apply()方法。有些函数没有这两种方法,但是这种函数比较罕见。这两个方法怎么工作的?他们的第一个参数是一个对象,他们会把这个对象绑定到this,接着调用函数时会指定这个this,我们称之为显示绑定

  1. function foo(num){
  2. console.log(this.a,num);
  3. return this.a + num;
  4. }
  5. var obj = {
  6. a : 2
  7. }
  8. var bar = function(){
  9. return foo.call(obj)
  10. }
  11. bar();//2

new绑定

使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

  • 创建(或者说构造)一个全新的对象
  • 这个 对象会被执行到prototype上
  • 这个对象会被绑定到函数调用的this
  • 如果函数没有返回对象,那么new表达式种的函数调用会自动返回这个新对象。
    1. function foo(a){
    2. this.a = a;
    3. }
    4. let bar = foo(2);
    5. console.log(bar.a);//2

    反思

    this的指向在函数定义的时候是确定不了的,只有在函数执行的时候才会确定。this实际上指向最终那个调用它的对象。

    通过常量来改变this的指向

    1. let lesson ={
    2. site:["后盾人"],
    3. show:function(){//类方法
    4. //this指向的是调用的对象
    5. return this.site.map(function(){
    6. //this指向的是window(普通函数)
    7. })
    8. }
    9. }
    如果想要在类方法中的普通函数调用this指向调用它函数,就需要改变this的指向
    方法一:利用 变量 self = this
    方法二:map函数中有第二个参数,该参数会自动赋值给函数中的this。有些函数没有这个参数。注意
    1. let lesson ={
    2. site:["后盾人"],
    3. show:function(){//类方法
    4. //this指向的是调用的对象
    5. return this.site.map(function(){
    6. //this指向的是window(普通函数)
    7. },this)//this指向对象lesson
    8. }
    9. }

    箭头函数带来的this的变化

    箭头函数中this指向的是上下文,即指向父级作用域