this是Javascript语言的一个关键字。它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。
例如:
function test(){ this.x = 1; }
随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用函数的那个对象。进一步说,this和它声明环境无关,而完全取决于他的执行环境。
以下有四种情况可以概括this的用法:
第一种:纯粹的函数调用
这是函数的最通常用法,属于全局性调用,因此this就代表全局对象Global。
下面来看代码理解:
function test(){ this.x = 1; alert(this.x); } test();
执行函数test()之后会弹出什么呢?很明显,弹出1.
好,那么将代码改变一下,如下:
var x = 1; function test(){ alert(this.x); } test();
执行函数test()之后会弹出什么呢?还是一样的,依旧弹出1
好,继续改,如下:
var x = 1; function test(){ this.x = 0; } test(); alert(x);
执行函数test()之后接下来会弹出什么呢?这题很明显的是弹出0,因为函数test执行后里面的this.x=0这个值将全局变量x原先的值1给覆盖了(作用域链),所以最终输出的是0,但是this的指向一直没变,依旧指向window对象。
第二种:作为对象方法的调用
函数还可以作为某个对象的方法调用,这时this就指这个上级对象。下面来看代码进一步理解:
function test(){ alert(this.x); } var o = {}; o.x = 1; o.m = test; o.m();
执行完o.m()之后弹出什么呢?很明显弹出1,首先先声明了一个函数名为test的函数,然后声明了一个空的对象o,然后接下来给对象o添加一个属性x且值为1;随后又给对象o添加了一个方法o.m=test;现在test里面的this指向的就是o这个对象,因为test函数作为对象o的方法调用,然后调用对象o的方法m,于是弹出1.
第三种:作为构造函数调用
所谓构造函数(创建类[对象]的一种方式,若是不懂,可以查阅链接:js原型、原型链与继承的理解),就是通过这个函数生成一个新对象(object)。这时,this就指这个新对象。下面看代码理解:
function test(){ this.x = 1; } var o = new test(); alert(o.x);
弹出1,但是得注意这里的this并不是全局对象!再看一段代码理解:
var x = 2; function test(){ this.x = 1; } var o = new test(); alert(x);
输出为2,可证明this并不是全局对象,因为全局对象x的值没有发生变化。
第四种:apply调用
apply()是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象。因此,this指的就是这第一个参数。下面看代码理解:
var x = 0; function test(){ alert(this.x); } var o={}; o.x = 1; o.m = test; o.m.apply();
apply()的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象。
如果把最后一句改为:o.m.apply(o),输出应该为多少?
输出应该为1,说明这时this代表的是对象o。
讲完了this的4个使用方法,下面就该用试题考考大家到底有没有完全理解:
var name = '罗恩'; var aaa = { name: '哈利', say: function () { console.log(this.name); } } var bbb = { name: '赫敏', say: aaa.say } var ccc = aaa.say; aaa.say(); bbb.say(); ccc();
后面三句代码得到什么?
第一个aaa.say();输出“哈利”,这个没啥好讲的;
第二个bbb.say(),bbb对象声明自身的say方法只是把aaa对象的say方法引用过来,但注意,这里引用的是一个方法而非一个对象,而aaa.say存储的是一个匿名函数。
如果这样不好理解,那么我们可以改写下bbb对象的写法,如下:
var bbb = { name: '赫敏', say: function () { console.log(this.name); } }
这种写法与原来的代码没有什么区别,但是可以让人看的更明白些,输出“赫敏”;
第三个ccc()是在最外层执行,也就是在全局对象window下。所以ccc()执行的时候this指代的就是window对象。而在window对象下声明了name属性,就相当于window.name = '罗恩',输出的当然就是罗恩。
当然,也有特殊情况,那就是 setTimeout 和 setInterval 。
如果开头的aaa对象的声明改成:
var aaa = { name: '哈利', say: function () { setTimeout(function(){ console.log(this.name); },100) } }
再看原来的三行输出什么?即aaa.say();bbb.say(); ccc(); 输出什么?
会发现输出全是‘罗恩’,也就是说,三次this,指代的都是window对象。
但是为什么这样呢?查阅的链接里说,关于为什么会这样,因为涉及到JS异步回调(关于js异步回调)的知识,如果只是仅仅想快速熟悉this的用法,那么只要记住这个特殊情况即可。这个知识点曾经是阿里还是小米的面试题。
关于js异步回调的知识,博主会下次更新。
下面再来看看几道笔试题,进一步加深印象:
var foo = { bar:function (){ return this.baz; }, baz:1 }; (function (){ return typeof arguments[0](); })(foo.bar);
这题应该输出什么呢?是object还是“object”,“number”?还是“function”呢?亦或是“undefined”?
下面我们就来好好看看,上面我阐述了this的4个使用方法:
1.纯粹的函数调用
2.作为对象方法调用
3.作为构造函数调用
4.apply()调用
首先我们来看,下面的匿名函数外部有个(),代表立即执行,而括号当中是foo.bar,大家要明白,匿名函数后面括号中的值或者对象是以参数的形式传给匿名函数的,而每个函数都有这么一个默认属性arguments,而刚刚好foo.bar对应的是arguments[0],所以这里相当于是吧foo.bar这个方法赋值给了arguments[0],而很明显的是在匿名函数中arguments[0]是在全局环境下的,也就是说foo.bar中的this指向的是arguments[0],所以在执行baz的时候自然this就是window了,而window上面没有baz属性,于是返回undefined,而typeof undefined输出的是“undefined”.(注意:这里typeof输出的字符串!)
下面再做一道笔试题:
var foo = { bar:function (){ return this.baz; }, baz:1 }; typeof (f = foo.bar)();
这题输出多少呢?我们知道foo.bar的确是函数方法的调用,但是在赋值给f的时候,this的指向就改变了
如果不好理解,那么把函数拆分一下:
var f = foo.bar; typeof f();
这样在调用形式上又是普通函数调用,this指向的是window,所以答案是“undefined”。
好了,关于this的用法就到这里了,希望大家好好消化理解。