this

我的博客地址 →
this | The story of Captain,转载请申明出处。

问:this 是什么?

答:thiscall 要领的第一个参数,call 的第一个参数就是 this

完。

就这么简朴么?是的。

为什么如许说?由于一切的函数/要领挪用的时刻都能够 转换call 情势,call 的第一个参数显式的指清楚明了函数该次实行时刻的高低文。

本日我们深入探讨一下怎样肯定 this

怎样肯定 this

this 由函数的高低文肯定。

怎样肯定“高低文” ?

高低文分为 全局高低文(Global Context) 以及 函数高低文(Function Context)

全局高低文

在全局中,this 一概指向 全局对象 window。比方:

console.log(this === window); //; true

函数高低文

在函数中,高低文由函数被挪用的体式格局决议。

  • 简朴挪用

    以 “函数名( )” 情势挪用的函数即为简朴挪用,简朴挪用时高低文为全局高低文,因而 this === window

    举例一:

    function foo () {
      console.log(this === window);
    }
    foo(); // true

    举例二:

    function fn1 () {
      function fn2 () {
        console.log(this === window);
      }
      fn2();
    }
    fn1(); // true,由于 fn2 为简朴挪用

    举例三:

    let obj = {
      fn1: function () {
        console.log(this === window);
      }
    };
    let fn2 = obj.fn1; 
    fn2(); // true

    第三个例子中,为什么 fn2() 实行效果为 true ?由于实行了 let fn2 = obj.fn1 以后 fn2 为:

    fn2 = function () {
      console.log(this);
    }

    再实行 fn2() 时,为简朴挪用,因而 this === window

  • 要领挪用

    当函数作为一个对象的要领被挪用时,this 指向该对象。

    举例一:

    let obj = {
      fn1: function () {
        console.log(this === obj);
      }
    };
    obj.fn1(); // true

    obj.fn1() 情势挪用 fn1 时,是以要领情势挪用的,this 指向该函数所属的对象,即 obj

    举例二:

    let obj = {
      fn1: {
        fn2:function () {
          console.log(this === obj.fn1);
        }
      }
    };
    obj.fn1.fn2(); // true

    obj.fn1.fn2() 情势挪用 fn2 时,是以要领情势挪用的,this 指向该函数所属的对象,即 obj.fn1,很多人常误以为此处的 this 指向 obj,这是毛病的。

    举例三:

    let obj = {
      fn1: function () {
        return function () {
          console.log(this === window);
        }
      }
    };
    let fn2 = obj.fn1();
    fn2(); // true

    为什么 fn2() 的实行效果为 true ?由于实行了 let fn2 = obj.fn1() 以后 fn2 为:

    fn2 = function () {
      console.log(this === window);
    }

    再实行 fn2() 时,为简朴挪用,因而 this === window 。假如想要将 fn2 中的 this 指向 obj,可将指向 objthis 保存在中心变量,修改以下所示:

    let obj = {
      fn1: function () {
        let that = this;
        return function () {
          console.log(that === obj);
        }
      }
    };
    let fn2 = obj.fn1();
    fn2(); // true

    应用 let that = thisfn1 中的 this 保存在 that 变量中,然后 fn2() 的效果即为 true,固然这个中触及到了 闭包(closure) 的学问。

特别的 this

以下状况中的 this 须要举行特别影象。

箭头函数

箭头函数(arrow function,=>),箭头函数为 ES6 中引入的新的函数示意法,差别之处在于,箭头函数中没有 this,箭头函数中的 this 为其实行高低文中的 this,怎样明白?举例申明。

举例一:

() => console.log(this === window); // true

其实行高低文为全局高低文,this 指向 window

举例二:

function foo () {
  return () => console.log(this === window);
};
foo()(); // true

和要领挪用中的举例三相似。

举例三:

let obj = {
  fn1: () => console.log(this === window);
};
obj.fn1(); // true

为什么是 true ?要领挪用中的举例一中的 this 不是 obj 吗?没错,箭头函数 fn1 中是没有本身的 this 的,因而 this 不指向 obj ,继续向上找 obj 的上一级,直到找到有 this 的高低文为止,obj 处在全局高低文中, 全局高低文中有 this,因而箭头函数中的 this 为全局高低文中的 this,即 指向 window

举例四:

let obj = {
  fn1: function () {
    return () => console.log(this === obj);
  }
};
let fn2 = obj.fn1();
fn2(); // true

此处又和要领挪用的举例三差别,由于箭头函数中是没有本身的 this 的,箭头函数中的 this 为其上一级的 this ,因而,箭头函数中的 this 为其上一级,即 fn1 中的 thisfn1 中的 this 指向 obj,所以箭头函数中的 this 指向 obj。依据箭头函数的特征:箭头函数中的 this 保留了其上一级的 this 指向,那末要领挪用举例三的修改能够优化为本例所示,用一个箭头函数即可处理,省去了中心变量。

组织函数

当一个函数作为组织函数运用时,组织函数的 this 指向由该组织函数 new 出来的对象。举例申明:

function CreateNewPerson (name,gender,age) {
  this.name = name;
  this.gender = gender;
  this.age = age;
}
let me = new CreateNewPerson('daijt','male',18);
console.log(me.name); // 'daijt'
console.log(me.gender); // 'male'
console.log(me.age); // 18

实行 let me = new CreateNewPerson('daijt','male',18) 时,组织函数中的 this 直接指向由其 new 出来对象对象 me ,因而实行完该句后 me 的构造以下:

me = {
  name: 'daijt',
  gender: 'male',
  age: 18
}

原型链

举例一:

let name = new String('daijt');
name.toUpperCase(); // DAIJT

依据上文组织函数中的 this,实行 let name = new String('daijt') 时,String 组织函数中的 this 指向了 name,而 name__proto__ 属性,该属性指向一切 string 类的共有属性或许要领,而这些共有的属性和要领都保存在 String.prototype 中,即:

name.__proto__ === String.prototype; // true

因而 name 是有 toUpperCase 要领的(原型链继续而来),挪用 toUpperCase 时,toUpperCase 中的 this 指向 name,因而 name.toUpperCase() 的效果为 DAIJT

举例二:

let name = 'daijt';
name.toUpperCase.(); // DAIJT

为什么没有经由历程 new 出来的对象也具有 toUpperCase 要领呢?由于在实行 let name = 'daijt' 的历程当中,JS 有一个暂时转化的历程,比方:

let name = (function (string) {
  return new String(string);
})('daijt');

因而,name 也继续了 string 类共有的属性和要领,这也算是 JS 的一个语法糖吧。 固然,这触及到了其他的学问。

DOM EventHandle

举例:

let buttons = document.querySelector('button');
buttons.addEventListener('click', function (event) {
  console.log(this === event.currentTarget); // true
});

运用 addEventListener 绑定 DOM 时,监听函数中的 this 指向触发事宜的 currentTargetcurrentTarget 示意被绑定了监听函数的 DOM 元素。

注重:假如是经由历程冒泡触发监听函数的话,
event.target 不一定即是
event.currentTarget

jQuery EventHandle

HTML:

<ul id="father-ul">
  <li class='father-li'>father-ul的第1个li</li>
  <li class='father-li'>father-ul的第2个li
    <ul>
      <li>son-ul的第1个li</li>
      <li>son-ul的第2个li</li>
      <li>son-ul的第3个li</li>
    </ul>
  </li>
  <li class='father-li'>father-ul的第3个li</li>
</ul>

JavaSctipt:

$('#father-ul').on('click', '.father-li', function (event) {
  console.log(event.target); 
  console.log(event.currentTarget);
  console.log(this === currentTarget);
});

当点击 <li class='father-li'>father-ul的第1个li</li> 时,控制台打印出:

<li class='father-li'>father-ul的第1个li</li>
<li class='father-li'>father-ul的第1个li</li>
true

当点击 <li>son-ul的第2个li</li> 时,控制台打印出:

<li>son-ul的第2个li</li>
<li class='father-li'>father-ul的第2个li
    <ul>
      <li>son-ul的第1个li</li>
      <li>son-ul的第2个li</li>
      <li>son-ul的第3个li</li>
    </ul>
</li>
true

因而能够得出结论:jQuery EventHandle 中的 this 指的是被代办事宜监听的 DOM 元素,也就是婚配一切选择器的 DOM 元素,即 .father-li ,详细诠释可参照 jQuery 文档

### 怎样转变 this

以上所述的 this 都为肯定的 this,那末怎样本身设置 this,转变 this 的指向呢?或许说怎样动态转变高低文呢?ES5 为我们供应了三个全局要领:call()apply()bind()。三个要领都能够动态的转变高低文,即 this 的指向,三者的区分能够参照 MDN,以 call() 为例举行申明。

var name = '全局高低文';
let me = {
  name: 'daijt',
  gender: 'male'.
  age: 23,
};
let myGirlFriend = {
  name: 'xiaofang',
  gender: 'female',
  age: 18
};
function printName() {
  console.log(this.name);
}
printName(); // window
printName.call(me); // daijt
printName.call(myGirlFriend); // xiaofang
  • 实行 printName() 时:

    简朴挪用,因而其内部的 this 指向 全局高低文,因而 this === window ,而运用 var 关键字在全局声明的变量会作为 window 对象的属性,因而 this.name === window.name === 全局高低文

  • 实行 printName.call(me) 时:

    由于 call() 的第一个参数为 thisArg ,因而运用 call() 显式的指清楚明了 printName 函数本次实行的高低文,即 me,因 this 指向高低文,所以 this === methis.name === me.name === daijt

  • 实行 printName.call(myGirlFriend) 与实行 printName.call(me) 同理。

技能

回到本文开首,一切的函数/要领挪用的时刻都能够 转换call 情势,call 的第一个参数显式的指清楚明了函数该次实行时刻的高低文,这就是推断 this 指向的技能,以代码为例举行演示:

举例一:

function foo () {
  console.log(this);
}
foo(); // window
foo.call(); // window

// non-strict mode
foo.call(undefined); // window
// strict mode
foo.call(undefined); // undefined
  • foo() 为简朴挪用,因而 this === window
  • foo.call() 中,call() 的第一个参数未指明,那末 this === window ,在全局高低文中,非严厉形式 下,undefined 即为 window严厉形式 下,undefined 不能指代 window ,所以严厉形式下 this === undefined

举例二:

let obj = {
  fn1: function () {
    console.log(this === obj);
  }
};
obj.fn1(); // true
obj.fn1.call(obj); // true

举例三:

let obj = {
  fn1: {
    fn2:function () {
      console.log(this === obj.fn1);
    }
  }
};
obj.fn1.fn2(); // true
obj.fn1.fn2.call(obj.fn1); // true

举例四:

let obj = {
  fn1: function () {
    return function () {
      console.log(this === window);
    }
  }
};
let fn2 = obj.fn1();
fn2(); // true
fn2.call(); // true
obj.fn1.call(obj).call(undefined); // true

以上三个例子中,怎样推断传给 call()this 呢?以举例四的末了一句代码为例举行剖析:

《this》

经由历程这张 call() 的图解,this 应当完整控制了,所以将函数的挪用改写为 call() 情势是最直接清楚明了推断 this 的要领。

看到这里,你搞懂 this 了吗?

参考链接:

更多精彩内容,请点击我的博客 →
The story of Captain

    原文作者:CaptainInPHW
    原文地址: https://segmentfault.com/a/1190000013014285
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞