前言
在这篇文章中,你将会弄清this关键字指的是什么,在为你讲解this
同时,你还将学习到.call
,.apply
,.bind
和new
以及箭头函数等知识。
this是什么
this
是JavaScript中很特别的一个关键字,被自动定义在所有函数作用域中,但是它即不指向函数自身也不指向函数的词法作用域,它指向的是调用函数的对象。
this到底指向哪里
要判断this
指向哪里,我们必须理解每个函数的this
并不是在声明时就被绑定的,而是在调用时被绑定。
this
指向的是调用函数的对象,所以要判断this
到底指向哪里,首先我们要知道 “是哪个对象调用了函数?” 举个例子来帮助同学们理解这一点:
例子1:
123function person(name) { console.log(name);}复制代码
可以看到例子里声明了一个person
函数,接收一个name
参数,想知道name
会打印出什么,必须得看person
函数调用过程中传入参数是什么。同样的道理想判断this
指向哪里,就得看函数调用方式是什么。
根据函数调用方式不同,我将其分为下面?几种情况:
1、首先就是最常用的函数调用方法:函数名直接调用
例2:
123456789101112function person() { console.log(this);}function personStrict() { 'use strict' console.log(this);}person(); // windowpersonStrict(); // undefined复制代码
可以看到,函数被直接调用时,就相当于全局对象调用的;这样它的 this
就指向全局对象:window
;
PS:但是在严格模式下,this不会指向全局对象,而是指向undefined,这是为了尽量减少不严谨的出错行为,你只需要在你所写代码作用域的最顶端加上use strict;建议将其放在一个立即执行函数中,避免污染全局。
例3:
1234567(function(){ //'use strict' function getDoc() { console.log(this.document); } getDoc();})();复制代码
可以看到,如果程序在非严格模式下运行,不会有错误抛出,那是因为在全局对象window
中存在一个名为document
的属性,而在严格模式下,由于此时this
指向undefined
,于是抛出一个错误。
2、作为对象的方法调用
例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(); // person1person2.getThis(); // person1person3.getThis(); // window复制代码
看到这可能有同学会问,不是说this
的指向取决于谁调用了它吗,那为什么person3.getThis()
打印出来的是window
呢,因为在person3.getThis
里,调用this
的函数是getThis3
,所以此时this
指向了window
由上面?几个例子可以得出,this
指向最后调用它的那个对象。
常用改变this指向方法
1、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
指向
2、new关键字
每当使用new
关键字调用函数时,会自动把this
绑定在新对象上,然后再调用这个函数
例8:
12345678function Person(name) { this.name = name; console.log(this);}var people = Person('Heternally'); // windowvar people1 = new Person('Heternally'); // Person {name: "Heternally"}people.name; //Cannot read property 'name' of undefinedpeople1.name; // 'Heternally'复制代码
3、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(); //windowperson.getName2(); // {name:'Heternally',getName1:f,getName2:f}复制代码
可以看到,在getName2方法的setTimeout函数中,没有跟getName1中setTimeout函数一样打印window,而是打印出person对象。
简单说:箭头函数中的this只和定义它时作用域有关,并不受在哪里及如何调用的影响;优先级
了解了this指向及多种改变this指向的方法后,我们还需要了解这多种方法的优先级,
1函数直接调用 < 对象方法调用 < call/apply < bind < new < 箭头函数复制代码
总结
要判断this指向,需要找到是这个函数的直接调用位置,找到之后可以依据如下方法进行判断this的指向: