1. 内联式绑定Dom元素的事件处理函数
<script type="text/javascript">
function sayHi(){
alert("当前点击的元素是" + this.tagName);
}
script>
<input id="btnTest" type="button" value="点击我" onclick="sayHi()">
打印出来的是undefined,因为this指针并不是指向input元素。这是因为当使用内联式绑定Dom元素的事件处理函数时,实际上相当于执行了以下代码:
<script type="text/javascript">
document.getElementById("btnTest").onclick = function(){
sayHi();
}
script>
在这种情况下sayHi函数对象的所有权并没有发生转移,还是属于window所有。
那么如果我们要引用元素本身怎么办呢?
我们知道,onclick函数是属于btnTest元素的,那么在此函数内部,this指针正是指向此Dom对象,于是我们只需要把this作为参数传入sayHi即可。
<script type="text/javascript">
function sayHi(el){
alert("当前点击的元素是" + el.tagName);
}
script>
<input id="btnTest" type="button" value="点击我" onclick="sayHi(this)">
等价于
<script type="text/javascript">
document.getElementById("btnTest").onclick = function(){
sayHi(this);
}
script>
2. 临时变量导致this指针消失
<script type="text/javascript">
var Utility = {
decode:function(str){
return unescape(str);
},
getCookie:function(key){
// ... 省略提取cookie字符串的代码
var value = "i%27m%20a%20cookie";
return this.decode(value);
}
};
alert(Utility.getCookie("identity"))
script>
如果我们通过Utility.getCookie去调用的话,那么没有问题,我们知道,getCookie内部的this指针指向的还是Utility对象,而Utility对象时包含decode属性的。代码可以成功执行。
但是有个人不小心这样使用Utility对象呢?
<script type="text/javascript">
function showUserIdentity(){
// 保存getCookie函数到一个局部变量,因为下面会经常用到
var getCookie = Utility.getCookie;
alert(getCookie("identity"));
}
showUserIdentity();
script>
这个时候运行代码会抛出异常“this.decode is not a function”。因为此时Utility.getCookie对象被赋给了临时变量getCookie,而临时变量是属于window对象的——只不过外界不能直接引用,只对Javascript引擎可见——于是在getCookie函数内部的this指针指向的就是window对象了,而window对象没有定义一个decode的函数对象,因此就会抛出这样的异常来。
解决此问题的办法有几个:
- 不引入临时变量,每次使用均使用Utility.getCookie进行调用
- getCookie函数内部使用Utility.decode显式引用decode对象而不通过this指针隐式引用(如果Utility是一个实例化的对象,也即是通过new生成的,那么此法不可用)
- 使用Funtion.apply或者Function.call函数指定this指针
前面两种都比较好理解,第三种需要提一下。正是因为this指针的指向很容易被转移丢失,因此Javascript提供了两个类似的函数apply和call来允许函数在调用时重新显式的指定this指针。
修正的代码如下:
<script type="text/javascript">
function showUserIdentity(){
// 保存getCookie函数到一个局部变量,因为下面会经常用到
var getCookie = Utility.getCookie;
alert(getCookie.call(Utility,"identity"));
alert(getCookie.apply(Utility,["identity"]));
}
showUserIdentity();
script>
3. 函数传参时导致的this指针丢失
<script type="text/javascript">
var person = {
name:"Kevin Yang",
sayHi:function(){
alert("你好,我是"+this.name);
}
}
setTimeout(person.sayHi,5000);
script>
当我们等了5秒钟之后,弹出的对话框显示的this.name却是undefined。
其实这个问题和上一个示例中的问题是类似的,都是因为临时变量而导致的问题。当我们执行函数的时候,如果函数带有参数,那么这个时候Javascript引擎会创建一个临时变量,并将传入的参数复制(注意,Javascript里面都是值传递的,没有引用传递的概念)给此临时变量。
对于函数作为参数传递导致的this指针丢失的问题,解方案如下:
- Prototype的解决方案——传参之前使用bind方法将函数封装起来,并返回封装后的对象
<script type="text/javascript">
var person = {
name:"Kevin Yang",
sayHi:function(){
alert("你好,我是"+this.name);
}
}
var boundFunc = person.sayHi.bind(person,person.sayHi);
setTimeout(boundFunc,5000);
script>
bind方法的实现其实是用到了Javascript又一个高级特性——闭包。我们来看一下源代码:
function bind(){
if (arguments.length < 2 && arguments[0] === undefined)
return this;
var __method = this, args = $A(arguments), object = args.shift();
return function(){
return __method.apply(object, args.concat($A(arguments)));
}
}
- 微软的Ajax库提供的方案——构建委托对象
<script type="text/javascript">
var person = {
name:"Kevin Yang",
sayHi:function(){
alert("你好,我是"+this.name);
}
}
var boundFunc = Function.createDelegate(person,person.sayHi);
setTimeout(boundFunc,5000);
script>
其实本质上和prototype的方式是一样的。
4. this的指向
this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向谁。
- 情况1:如果一个函数中有this,但是它没有被上一级的对象所调用,那么this指向的就是window,这里需要说明的是在js的严格版中this指向的不是window,但是我们这里不探讨严格版的问题,你想了解可以自行上网查找。
情况2:如果一个函数中有this,这个函数有被上一级的对象所调用,那么this指向的就是上一级的对象。
情况3:如果一个函数中有this,这个函数中包含多个对象,尽管这个函数是被最外层的对象所调用,this指向的也只是它上一级的对象
var o = {
a:10,
b:{
// a:12,
fn:function(){
console.log(this.a); //undefined
}
}
}
o.b.fn();
尽管对象b中没有属性a,这个this指向的也是对象b,因为this只会指向它的上一级对象,不管这个对象中有没有this要的东西。
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();
this永远指向的是最后调用它的对象,也就是看它执行的时候是谁调用的,例子4中虽然函数fn是被对象b所引用,但是在将fn赋值给变量j的时候并没有执行所以最终指向的是window,这和例子3是不一样的,例子3是直接执行了fn。
6. 当this遇到return
如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。
function fn()
{
this.user = '追梦子';
return {};
}
var a = new fn;
console.log(a.user); //undefined
function fn()
{
this.user = '追梦子';
return function(){};
}
var a = new fn;
console.log(a.user); //undefined
function fn()
{
this.user = '追梦子';
return 1;
}
var a = new fn;
console.log(a.user); //追梦子
function fn()
{
this.user = '追梦子';
return undefined;
}
var a = new fn;
console.log(a.user); //追梦子
还有一点就是虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊。
function fn()
{
this.user = '追梦子';
return null;
}
var a = new fn;
console.log(a.user); //追梦子
==注意:==
1.在严格版中的默认的this不再是window,而是undefined。
2.new操作符会改变函数this的指向问题,虽然我们上面讲解过了,但是并没有深入的讨论这个问题,网上也很少说,所以在这里有必要说一下。
function fn(){
this.num = 1;
}
var a = new fn();
console.log(a.num); //1
为什么this会指向a?首先new关键字会创建一个空的对象,然后会自动调用一个函数apply方法,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。
7. 应用中的this
(function(global){
......
})(this);
第一个括号内的匿名function可以有局部变量(也可写全局),后面的(this)代表传入this执行匿名function,好处是匿名funciton内的局部变量在执行完后系统自动回收不影响上下文,且变量名字不会冲突,this具体指什么要联系上下文