关于javascript的this讲解的文章已经多如牛毛了,我本人在开发过程中也用到过很多次。这次趁有空,整理一下关于this的一系列问题:包括this的指向,严格模式和非严格模式下的区别,什么情况下可以改变this的指向以及在ES6下箭头函数中this的指向。
严格模式下:
function aa(){
'use strict';
console.log(this) //undefined
}
aa();
非严格模式下:
function aa(){
console.log(this) //window
}
aa();
MDN中对this的解释如下:
在绝大多数情况下,函数的调用方式决定了this的值。this不能在执行期间被赋值,并且在每次函数被调用时this的值也可能会不同。
简单点来说,this的指向在声明时是未定义的,只有在它被调用时才会被赋值,要搞清楚它到底指向什么,只要搞清楚到底谁调用它即可。
下面有几种情况可以帮助我们加快理解this的指向问题:
1.第一种情况:
function aa(){
console.log(this) //window
}
aa();
此时是window对象调用了aa()函数,所以this指向window。
2.第二种情况
var test = {
aa:"hello",
bb:function(){
console.log(this.aa) //"hello"
}
}
test.bb();
此时是test对象调用了this,所以this此时指向的bb对象中的aa;
var test = {
aa:"hello",
bb:function(){
console.log(this) //test (object)
}
}
test.bb();
此时就很清晰的能看到this指向的test对象。
3.第三种情况
var test = {
aa:"hello",
bb:{
aa:'hello2',
cc:function(){
console.log(this.aa) //"hello2"
}
}
}
test.bb.cc();
此时调用cc中this的对象是bb,而不是test,所以this指向的是bb。
以上三个例子已经比较清晰的解释了this的指向问题:
谁最后调用了this,谁就是this指向的对象。
这些情况大致可以分为两类:
1.关键字类。主要有new,return
2.函数类。 主要有call,apply,bind
第一类.关键字类
我们先看new的例子:
function aa(){
this.name = "Tom"
}
var bb = aa(); //bb is undefined;
var bb = new aa();
console.log(bb.name) //"Tom"
new关键字的关键在于实例化了构造函数aa()。使它的函数能够顺利的拿到里面的name。
return的例子:
function aa(){
this.name = "Tom";
return {};
}
var bb = new aa();
console.log(bb.name) //undefined
当return的内容是一个对象时,则this会指向这个对象。
function aa(){
this.name = "Tom";
return 'aaa';
}
var bb = new aa();
console.log(bb.name) //"Tom"
当return的内容是一个非对象时,则this的指向不会发生改变。
第二类.函数类
1.call()函数
老规矩,先关门放MDN。
call() 方法调用一个函数,其具有一个指定的this值和分别地提供的参数(参数的列表)。
简单来说就是可以通过call()函数来指定你想调用的this所造的环境,同时还可以追加多个参数。
以下是MDN上的官方示例:
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}
console.log(new Food('cheese', 5).name);
// expected output: "cheese"
如果没有调用call()函数,最后打印的应该是food,调用后才会改成cheese。如果觉得上面的例子还不够清晰,我们可以再看一个,如果觉得ok了,请跳过。
var aa = {
name:"tom",
bb:function(x, y){
console.log(this.name); //"tom"
console.log(x,y); // 1,2
}
}
var b = aa.bb
b.call(aa);
b.call(aa,1,2)
2.apply函数
apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。
该方法的作用和 apply()方法类似,只有一个区别,就是call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组。
var aa = {
name:"tom",
bb:function(x, y){
console.log(this.name); //"tom"
console.log(x,y); // 1,2
}
}
var b = aa.bb
b.apply(aa,[1,2])
3.bind函数
bind()方法创建一个新的函数, 当这个新函数被调用时其this置为提供的值,其参数列表前几项置为创建时指定的参数序列。
var module = {
x: 42,
getX: function() {
return this.x;
}
}
var unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined
var boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42
需要注意的是,bind函数与apply和call都有所不同,不同之处在于bind不是立即执行的。
bind() 函数会创建一个新绑定函数,绑定函数与被调函数具有相同的函数体(在 ECMAScript 5 中)。
我们看一个例子:
var aa = {
name:"tom",
bb:function(){
console.log(this.name)
}
}
var cc = aa.bb;
var dd = cc.bind(aa);
console.log(dd); //type function aa()
dd(); // "tom"
箭头函数与其他函数区别很大,我们直接看MDN:
箭头函数表达式的语法比函数表达式更短,并且没有自己的this,arguments,super或 new.target。
function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| 正确地指向person 对象
}, 1000);
}
var p = new Person();
当然,通过相关函数也不能改变=>中的this指向。
通过 call 或 apply 调用
由于 箭头函数没有自己的this指针,通过 call() 或 apply() 方法调用一个函数时,只能传递参数(不能绑定this—译者注),他们的第一个参数会被忽略。(这种现象对于bind方法同样成立—译者注)
总结一下,在箭头函数中,是没有它自己的this的,它只会从自己的作用域链的上一层继承this。
我们来看例子:
var obj = {
fn:()=>{
console.log(this) //window
}
};
obj.fn();
再看一个:
function obj(){
let aa = ()=>{
console.log(this);
}
aa();
}
obj() // window
var bb = new obj(); //obj type object
第二个例子中,new obj()后之所以打印出了obj(),是因为new关键字绑定了this到返回的新的对象object中。