首页 > 代码库 > 扑朔迷离的 this关键字
扑朔迷离的 this关键字
在Java,C#等面向对象的语言中,this的含义是明确且具体的,固定指向运行时的当前对象。而由于Javascript的动态性(边解释边执行),this的指向在运行时才能确定,跟定义在哪儿无关。
this总是返回一个对象,就是属性或方法 当前 所属的对象。一般来讲,就是“.”前的那个对象(个人认为这种说法是非常容易误导人的)
var name = ‘张三‘; var obj = { name: ‘李四‘, introduce: function(){ console.log(‘my name is‘ + this.name); } }; obj.introduce(); var f1 = obj.introduce; f1();
在这个例子中,obj.introduce() "."左边是obj,所以this指向obj这个对象。introduce方法里面的this.name沿着作用域链往上找就是“李四”;
我们都知道函数后面带括号表示执行,不带括号就是把函数指针赋值给变量,但不执行函数,所以f1其实就是指代 function(){console.log(‘my name is‘ + this.name);},不妨可以console出来验证一下。f1()是在全局window对象中,所以这里的f1()其实就是window.f1(),也就是说"f1()"打印输出的应该是全局变量“张三”
关于刚才提到的f1函数,我们可以进一步验证下
function f1(){ return this; }; console.log(f1() === window);
在这里,可以得出一个结论,如果一个函数运行在全局对象中,那么,this就是指向顶层对象,在浏览器中即为window对象。
基于前面的分析,我们把上例稍微改变一下
var obj = { name: ‘李四‘, describe: function () { console.log( ‘my name is‘+ this.name); } }; var obj2 = { name: ‘张三‘ }; obj2.describe = obj.describe; obj2.describe();
this指向obj对象,这点已经分析过了。这个例子中“obj2.describe = obj.describe;”说白了就是给obj2对象添加一个describe方法,this同样指向当前所在的对象,所以输入的就是“张三”,obj2对象实际变成了
obj2 = { name: ‘张三‘, describe: function () { console.log( ‘my name is‘+ this.name); } };
this的使用场合
1、全局环境 this--->指向全局对象 window
全局中的this就是顶层对象window
这个例子说明,不管this是不是在函数内部,只要是在全局环境下运行,this都指向window
这里需要稍微提示下,在严格模式下,全局中的this就不再是window对象了,而是undefined
2、构造函数 this--->指向新对象
构造函数中的this,指向生成的新对象
var a = 3; //全局变量a function Test(){ this.a = 5; //对象属性a } var obj = new Test(); console.log(obj.a);
上面代码定义了一个构造函数Test,同时创建了Test的实例对象obj,我们知道通过new操作符创建构造函数的新实例对象,主要经历了4个步骤:
(1)、创建一个新对象
(2)、将构造函数的作用域赋给新对象(因此this就指向了这个新对象)
(3)、执行构造函数中的代码(为这个新对象添加属性)
(4)、返回新对象
根据这4个步骤,obj对象其实就是
Test {a: 5}
所以,很显然obj.a打印出来的a就是5,而不是3了
再来看一个例子:
var Obj = function (p) { this.p = p; }; Obj.prototype.m = function() { return this.p; }; var o = new Obj(‘Hello World!‘); o.p; o.m();
首先创建了一个构造函数Obj,给Obj的原型对象添加了一个方法m,m同样返回属性p,然后创建一个Obj的实例对象o,基于前面的分析,最终输出的就应该是两个‘Hello World!‘了
3、对象的方法 this--->指向当前这个对象
当A对象的方法被赋予给B对象,该方法中的this就会从指向A对象变成了指向B对象。所以需要特别注意,将某个对象的方法赋值给另一个对象,会改变this的指向
先来验证第一句话,作为对象的方法被调用,this指向当前这个对象
由此说明,obj.foo方法执行时,它内部的this指向了当前对象obj。先来看一个例子:
function test(){ console.log(this.a); } var a = 3; //声明一个全局变量a var obj = {}; //创建一个对象obj obj.a = 5; //给obj添加一个属性a obj.m = test; //给obj添加一个方法m,并且把test指针指向了m console.log(obj); obj.m();
再来看一个例子:
var name = "sky"; var obj = { name:"zt", say:function(){ console.log("I am "+this.name); } } obj.say();//I am zt var fn = obj.say; fn();//I am sky
obj.say()这个就不需要多说,在对象自己的方法里面,this指向当前的对象obj,所以是zt
把obj对象的say方法赋予给另一个fn函数,所以this会从obj指向fn,而fn运行在全局环境中,所以输出的全局变量sky
4、内部函数(匿名函数) 匿名函数的执行环境具有全局性,this对象通常指向window
var name = ‘Window‘; var obj = { name: ‘My Object‘, getName: function(){ return function(){ return this.name; } } }; console.log(obj.getName()()); //Window
按照上面的说法,似乎可以很快确认输出的是Window。我们不妨做下拆解,执行obj.getName(),得到的是一个匿名函数function(){return this.name;},此时,这个匿名函数运行在全局环境中,再次执行这个函数,里面的this自然就指向了全局Window
在定时器里面的this是否也是指向window呢?
var name = "sky"; var obj = { name:"zt", say:function(){ setTimeout(function(){ console.log("I am "+this.name); },3000); } }; obj.say();
可以看到,执行say方法,其实就是一个一次性定时器,3s之后执行function(){console.log("I am"+this.name);},可能按照我们前面的说法“.”前的对象是obj,所以this指向obj,这个没错,但是,这是say方法里的this,定时器的代码是在全局作用域window对象中执行的,所以这里面的this指向window,当然严格模式下是undefined,所以最终输出的是全局的sky而不是obj对象里面的zt
从上面的两个例子中可以看到,getName和say两个方法中,内层函数中的this没有按预想的指向外层函数对象,而是指向全局对象window,那如果我们需要指向外层函数对象,怎么办呢?这里就需要用到我们常见的“留住this”
var name = "sky"; var obj = { name:"zt", say:function(){ var that = this; //把this保存到一个变量中 setTimeout(function(){ console.log("I am "+that.name); },3000); } }; obj.say(); //I am zt
var name = ‘Window‘; var obj = { name: ‘My Object‘, getName: function(){ var me = this; //this指向obj对象,我们把这个this放到一个变量中保存起来 return function(){ return me.name; //成功留住了this } } }; console.log(obj.getName()()); //My Object
最后,再来看一个经典的面试题
var x=5,o={ x:10, doit:function doit(){ var x=20; setTimeout( function(){ console.log(this.x); //5 }, 10); } }; console.log(o.doit());//undfined (function(){console.log(this.x)})();//5
参考上面的第4条,可以很容易得出结论 匿名函数 “function(){console.log(this.x)}”运行在全局window中,所以输出的两个5没有问题,关键是为什么o.doit()打印出来是undefined,原因很简单,doit方法没有return任何东西,它是没有返回值的。那如果给它加一个返回值呢?
var x=5,o={ x:10, doit:function doit(){ var x=20; setTimeout( function(){ console.log(this.x); //5 }, 10); return this.x; //用在对象的方法中,this指向当前的对象o } }; console.log(o.doit());//10 返回的是"return this.x;" (function(){console.log(this.x)})();//5
本文出自 “dapengtalk” 博客,请务必保留此出处http://dapengtalk.blog.51cto.com/11549574/1869458
扑朔迷离的 this关键字