关于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中。