函数中this的指向-简单认识

在我们刚刚使用JS时,不知道您是否因为一些东西而思路直接混乱的情况,反正我是有过,曾经有段时间,我是尽量在所有能不使用this的地方,不去使用this,因为这个家伙总是让我不知道,它在一些地方,到底是代表的什么含义,现在,自认为有了一些认识,所以也敢于使用这个变量了,所以就有了本篇文章。

概述

首先有一点要先明确,那就是this是一个引用类型,也就是说,它是一个对象。OK,概述结束。

基础示例

首先,让我们先看下,this到底是指向的哪些对象呢?那就先看一些例子:


function testCallback(){
    if(this === window){
	console.log("this === window");
    }else if(this === document){
	console.log("this === document");
    }else if(this === a){
	console.log("this === a");
    }
}

testCallback();   //this===window

document.onclick = testCallback;    //this === document

var a = {};
a.callback = testCallback;
a.callback();   //this === a


上述三种最简单的情况,应该也算是我们最常见的了吧。说是三种,其实也只能算是两种吧,因为document.onclicka.callback的两种,其实质是相同的,只是对象名和属性名是不同的。

所以,我们可以先根据上面的情况,把this的指向,定义为两种:

1:一种是如果显示的在一个对象上直接调用了一个方法,那么其中的this就是指向这个显示的对象。

2:第二种是,如果没有显示的调用的话,比如之前代码中,直接使用testCallback()执行函数,那么其中的this,就是指向window对象的。

为什么没有显示的对象调用时,this是指向window对象呢?记得我们在最初学习JS时有过这样的描述吧,如果没有使用var定义一个变量,即便是在局部作用域定义,这也是一个全局变量,而全局变量是会被添加到window对象中,作为其中的一个属性的,所以,之前的testCallback方法,其实是存在于window对象中的一个方法。所以,这里不算是显示的调用,但是如果把这种调用方法写的完全的话,应该是window.testCallback()的写法,所以,按照显示调用的说法,也是正确的。

OK,上述的两种说法,只是在我们平常的一些基本的写法的,比如,在使用DOM2级事件绑定中,回调函数的内部,其this的指向,就和我们常说的有些区别。

例如:


function testCallback(){
    if(this === window){
	console.log("this === window");
    }else if(this === document){
	console.log("this === document");
    }
}

function addEvent(obj,type,callback){
    if(obj.addEventListener){
	obj.addEventListener(type,callback,false);
    }else if(obj.attachEvent){
	obj.attachEvent("on"+type,callback);
    }else{
	var fn = obj["on"+type];
	obj["on"+type] = function(){
	
	    fn && fn();
	    callback();
	}
    }
}

addEvent(document,"click",testCallback);


这个时候,当你点击当前页的任意位置,都可以触发该事件,这时你会发现些什么问题呢?这个时候,你就会发现一个问题,在IE9+的浏览器中,当你点击时,会出现"this === document"的显示,这个时候,也就是,我们这个事件在哪个HTML对象上绑定,那么回调函数中的this就是指向这个绑定的对象的,之前的代码中,点击事件是绑定在document对象上的,所以,this等于document是很正常的了,也是我们希望的结果。

而在IE9-的浏览器中,显示的结果却是“this === window”,这就导致了一个问题,那就是,我们并不能确认自己的代码只会在IE9+的浏览器中运行,所以这个时候,就不能直接使用this进行处理,但是如果不使用this,获取到对应的触发元素,又有些麻烦(这里只考虑原生的JS,不考虑框架中的实现),所以,为了减少这种错误的情况出现,只能放弃使用this了。

前面之所以在IE9-的浏览器事件的回调函数中,this指向window的原因,也是因为,在IE9-的浏览器中,HTMLDOM元素的对象,并不是继承自Object对象的,所以和其他浏览器下,DOM元素的对象继承自Object对象,是有很大的差距的。

只需要下面的一行代码,既可以验证DOM元素的对象是否继承自Object对象。


console.log(document instanceof Object);


练习一下

基础的东西,暂时就能想到这么些了,自我感觉应该也是差不多了,现在给几个例子,看下是否能正确的理解到this的指向问题:

例子1,请看其中注释处,分别打印出的结果是什么:


var a = {
    name:"zhang",
    sayName:function(){
	console.log("this.name="+this.name);
    }
};

var name = "ling";
function sayName(){
    var sss = a.sayName;
	
    sss();            //this.name = ?
    a.sayName();      //this.name = ?
    (a.sayName)();    //this.name = ?
    (b = a.sayName)();//this.name = ?
}

sayName();


例子2:


var sex = "male";
var saySex = {
    sex:"female",
    saySex:function(){
	function getSex(){
	    console.log("this.sex="+this.sex);
	}
	getSex();
    }
}

saySex.saySex();         //this.sex = ?
var ccc = saySex.saySex; 
ccc();//this.sex = ?


例子3:该例和例2只有一点点变化:


var sex = "male";
var saySex = {
    sex:"female",
    saySex:function(){
	function getSex(){
	    console.log("this.sex="+this.sex);
	}
		
        getSex.call(this);
	//与例2只有这个地方的变化
    }
}

saySex.saySex();         //this.sex = ?
var ccc = saySex.saySex; 
ccc();//this.sex = ?


例4:


var name = "ling";
function sayName(){
    var a = {
	name:"zhang",
	sayName:getName
    };
	
    function getName(){
	console.log(this.name);
    }
	
    getName();      //this.name = ?
    a.sayName();    //this.name = ?
    getName.call(a);//this.name = ?
}
sayName();


例5(当点击页面时,就会触发):


var name = "ling";
var obj = {
    name:"zhang",
    sayName:function(){
	console.log("this.name="+this.name);
    },
    callback:function(){
	var that = this;
	return function(){
	    var sayName = that.sayName;
	    that.sayName();  //this.name = ?
	    sayName();//this.name = ?
	}
    }
}

function addEvent(obj,type,callback){
    if(obj.addEventListener){
	obj.addEventListener(type,callback,false);
    }else if(obj.attachEvent){
	obj.attachEvent("on"+type,callback);
    }else{
	var fn = obj["on"+type];
	obj["on"+type] = function(){
	    fn && fn();
	    callback();
	}
    }
}

addEvent(document,"click",obj.callback());


这里,暂时就能想到这么些使用的地方,其实也可以归纳一下:
首先,不管是函数最初定义在哪里,它其中的this指向,只和这个函数的调用方法有关。

fn()这种写法的this,肯定是指向window对象的。

obj.fn();这样写法的this,肯定是指向obj对象的。

这里不考虑callapply强行改变this指向的关系,如果您对上述两点说明有疑问,请参考前面的5个示例。

对于这个我认为的结论,本篇文章中,这是第二遍提及,也是希望,有人能给与一些指正,或者是在您看完示例之后,让您能有更深的印象以及理解。

因为个人思想的限制,有些问题,可能没有考虑到,如果您有新的想法,请指教!

总结

本篇文章时写分几天写的,之前有个目录是写“进阶”的,但是现在却想不起来,当时进阶是想写哪部分内容了,所以就改为现在想的“练习一下”了~~所以啊,还是一气呵成的好,要么就算是有目录,可是目录也是当时写的嘛~~后面还是记不住为啥当时定下这个目录的~~~

本文地址:http://www.zhangyunling.com/?p=251

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>