走进JavaScript之this篇

前言

在这篇文章中,你将会弄清this关键字指的是什么,在为你讲解this同时,你还将学习到.call,.apply,.bindnew以及箭头函数等知识。数组

this是什么

this 是JavaScript中很特别的一个关键字,被自动定义在全部函数做用域中,可是它即不指向函数自身也不指向函数的词法做用域,它指向的是调用函数的对象。bash

this到底指向哪里

要判断this指向哪里,咱们必须理解每一个函数的this并非在声明时就被绑定的,而是在调用时被绑定。
正如上文所说this指向的是调用函数的对象,因此要判断this到底指向哪里,首先咱们要知道 “是哪一个对象调用了函数?”app

举个例子来帮助同窗们理解这一点:函数

例子1:学习

123function person(name) {
  console.log(name);
}
复制代码

能够看到例子里声明了一个person函数,接收一个name参数,想知道name会打印出什么,必须得看person函数调用过程当中传入参数是什么。一样的道理想判断this指向哪里,就得看函数调用方式是什么。ui

根据函数调用方式不一样,我将其分为下面👇几种状况:

一、首先就是最经常使用的函数调用方法:函数名直接调用

例2:this

123456789101112function person() {
  console.log(this);
}

function personStrict() {
  'use strict'
  console.log(this);
}

person(); //  window

personStrict(); // undefined
复制代码

能够看到,函数被直接调用时,就至关于全局对象调用的;这样它的 this就指向全局对象:windowspa

PS:可是在严格模式下,this不会指向全局对象,而是指向undefined,这是为了尽可能减小不严谨的出错行为,你只须要在你所写代码做用域的最顶端加上use strict;建议将其放在一个当即执行函数中,避免污染全局。

例3:code

1234567(function(){
  //'use strict'
  function getDoc() {
    console.log(this.document);
  }
  getDoc();
})();
复制代码


能够看到,若是程序在非严格模式下运行,不会有错误抛出,那是由于在全局对象window中存在一个名为document的属性,而在严格模式下,因为此时this指向undefined,因而抛出一个错误。cdn

二、做为对象的方法调用

例4:

12345678let name = 'window';
let person = {
  name : 'Heternally',
  getName : function(){
    console.log(this.name);
  }
}
person.getName(); // 'Heternally'
复制代码
123456789let name = 'window';
let person = {
  name : 'Heternally',
  getName
}
function getName(){
  console.log(this.name)
}
person.getName(); // 'Heternally'
复制代码

能够看到在上面👆例子中,getName函数是对象person调用的,因此打印出来的值是person中name的值

例5:

12345678910let name = 'window';
let person = {
  name : 'Heternally',
  getName : function () {
    console.log(this.name);
  }
}

let getName = person.getName;
getName(); // 'window'
复制代码

例5对例4作了一点点小改动,能够看到打印出的结果就是window了,这是由于getName函数最终仍是被window调用,因此这个this指向的是window

这又应了上文的话,this的指向不能在声明的时候肯定,而是取决于谁调用了它

例6:

12345678910111213141516171819202122232425let person1 = {
  name : 'person1',
  getThis: function () {
    console.log(this);
  }
}

let person2 = {
  name : 'person2',
  getThis: function () {
    return person1.getThis();
  }
}

let person3 = {
  name : 'person3',
  getThis: function () {
    var getThis3 = person1.getThis;
    return getThis3();
  }
}

person1.getThis(); // person1
person2.getThis(); // person1
person3.getThis(); // window
复制代码

看到这可能有同窗会问,不是说this的指向取决于谁调用了它吗,那为何person3.getThis()打印出来的是window呢,由于在person3.getThis里,调用this的函数是getThis3,因此此时this指向了window

由上面👆几个例子能够得出,this指向最后调用它的那个对象。

经常使用改变this指向方法

一、call、apply、bind

这三个函数的做用都是改变函数执行时的上下文,即改变函数运行时的this指向。有了这我的生,接下来咱们经过几个例子来学习如何使用这三个函数。
简单说一下三个方法的用法:

1.1 call
1fun.call(thisArg[, arg1[, arg2[, ...]]])
复制代码

它会当即执行函数,第一个参数时指定执行函数中this的上下文,若是不传或者传null、undefined,则表示this指向window,后面的参数是执行函数所需的参数;

1.2 apply
1fun.apply(thisArg[, [arg1, arg2, ...]])
复制代码

它会当即执行函数,第一个参数时指定执行函数中this的上下文,若是不传或者传null、undefined,则表示this指向window,第二个参数接收一个数组(这是与call惟一的区别);

1.3 bind
1var foo = fun.bind(thisArg[, arg1[, arg2[, ...]]]);
复制代码

它不会当即执行函数,而是返回一个心得函数,这个新的函数被指定了this上下文,接收的参数与call一致;

例7:

1234567891011let person = {
  name : 'Heternally'
}
var name = 'window';
function getName() {
  console.log(`Hello, my name is ${this.name}`);
}
getName(); // 'Hello, my name is window'
getName.call(person); // 'Hello, my name is Heternally'
getName.apply(person); // 'Hello, my name is Heternally'
getName.bind(person)(); // 'Hello, my name is Heternally'
复制代码

能够看到,使用call、apply、bind方法后,能够动态改变函数执行上下文的this指向

二、new关键字

每当使用new关键字调用函数时,会自动把this绑定在新对象上,而后再调用这个函数

例8:

12345678function Person(name) {
  this.name = name;
  console.log(this);
}
var people = Person('Heternally'); // window
var people1 = new Person('Heternally'); // Person {name: "Heternally"}
people.name; //Cannot read property 'name' of undefined
people1.name; // 'Heternally'
复制代码

三、ES6箭头函数

在ES6中,新加入了箭头函数,它和普通函数最不一样的一点就是,对于箭头函数的this指向,只须要看它是在哪里建立便可

例9:

123456789101112131415var person = {
  name : 'Heternally',
  getName1 : function () {
    setTimeout(function(){
      console.log(this);
    },1000)
  },
  getName2 : function () {
    setTimeout(()=>{
      console.log(this);
    },1000)
  }
}
person.getName1(); //window
person.getName2(); // {name:'Heternally',getName1:f,getName2:f}
复制代码

能够看到,在getName2方法的setTimeout函数中,没有跟getName1中setTimeout函数同样打印window,而是打印出person对象。
简单说:箭头函数中的this只和定义它时做用域有关,并不受在哪里及如何调用的影响;

优先级

了解了this指向及多种改变this指向的方法后,咱们还须要了解这多种方法的优先级,

1函数直接调用  < 对象方法调用 < call/apply < bind < new < 箭头函数
复制代码

总结

要判断this指向,须要找到是这个函数的直接调用位置,找到以后能够依据以下方法进行判断this的指向:

一、 是否由箭头函数调用 ?指向箭头函数外层非箭头函数的this指向 : ‘’

二、 是否由new调用 ?指向新建立的对象 : ‘’;

三、 是否由call || apply || bind 调用 ? 指向指定的对象 : ‘’;

四、 是否由对象调用 ? 指向这个对象 : ‘’;

五、 上面几种状况都不是:严格模式下指向undefined,非严格模式下指向全局对象

相关文章
相关标签/搜索