<script type="text/javascript"> function ftn01(){ console.log("I am ftn01!"); } var ftn02 = function(){ console.log("I am ftn02!"); } </script>
上面是我们经常使用的两种定义函数的方式,第一种定义函数的方式在javascript语言称作声明函数,第二种定义函数的方式叫做函数表达式,这两种方式我们通常认为是等价的,但是它们其实是有区别的,而这个区别常常会让我们混淆this指针的使用,我们再看看下面的代码:
<script type="text/javascript"> console.log(ftn01);//ftn01() 注意:在firebug下这个打印结果是可以点击,点击后会显示函数的定义 console.log(ftn02); ftn01(){ console.log("I am ftn01!"); } var ftn02 = function(){ console.log("I am ftn02!"); } </script>
这又是一段没有按顺序执行的代码,先看看ftn02,打印结果是undefined,undefined我在前文里讲到了,在内存的栈区已经有了变量的名称,但是没有栈区的变量值,同时堆区是没有具体的对象,这是javascript引擎在预处理(群里东方说预处理比预加载更准确,我同意他的说法,以后文章里我都写为预处理)扫描变量定义所致,但是ftn01的打印结果很令人意外,既然打印出完成的函数定义了,而且代码并没有按顺序执行,这只能说明一个问题:
在javascript语言通过声明函数方式定义函数,javascript引擎在预处理过程里就把函数定义和赋值操作都完成了,在这里我补充下javascript里预处理的特性,其实预处理是和执行环境相关,在上篇文章里我讲到执行环境有两大类:全局执行环境和局部执行环境,执行环境是通过上下文变量体现的,其实这个过程都是在函数执行前完成,预处理就是构造执行环境的另一个说法,总而言之预处理和构造执行环境的主要目的就是明确变量定义,分清变量的边界,但是在全局作用域构造或者说全局变量预处理时候对于声明函数有些不同,声明函数会将变量定义和赋值操作同时完成,因此我们看到上面代码的运行结果。由于声明函数都会在全局作用域构造时候完成,因此声明函数都是window对象的属性,这就说明为什么我们不管在哪里声明函数,声明函数最终都是属于window对象的原因了。
关于函数表达式的写法还有秘密可以探寻,我们看下面的代码:
<script type="text/javascript"> function ftn03(){ var ftn04 = function(){ console.log(this);// window }; ftn04(); } ftn03(); </script>
运行结果我们发现ftn04虽然在ftn03作用域下,但是执行它里面的this指针也是指向window,其实函数表达式的写法我们大多数更喜欢在函数内部写,因为声明函数里的this指向window这已经不是秘密,但是函数表达式的this指针指向window却是常常被我们所忽视,特别是当它被写在另一个函数内部时候更加如此。
其实在javascript语言里任何匿名函数都是属于window对象,它们也都是在全局作用域构造时候完成定义和赋值,但是匿名函数是没有名字的函数变量,但是在定义匿名函数时候它会返回自己的内存地址,如果此时有个变量接收了这个内存地址,那么匿名函数就能在程序里被使用了,因为匿名函数也是在全局执行环境构造时候定义和赋值,所以匿名函数的this指向也是window对象,所以上面代码执行时候ftn04的this也是指向window,因为javascript变量名称不管在那个作用域有效,堆区的存储的函数都是在全局执行环境时候就被固定下来了,变量的名字只是一个指代而已。
这下子坏了,this都指向window,那我们到底怎么才能改变它了?
在本文开头我说出了this的秘密,this都是指向实例化对象,前面讲到那么多情况this都指向window,就是因为这些时候只做了一次实例化操作,而这个实例化都是在实例化window对象,所以this都是指向window。我们要把this从window变成别的对象,就得要让function被实例化,那如何让javascript的function实例化呢?答案就是使用new操作符。我们看看下面的代码:
<script type="text/javascript"> var obj = { name:"sharpxiajun", job:"Software", show:function(){ console.log("Name:" + this.name + ";Job:" + this.job); console.log(this);// Object {, job="Software", show=function()} } }; var otherObj = new Object(); otherObj.name = "xtq"; otherObj.job = "good"; otherObj.show = function(){ console.log("Name:" + this.name + ";Job:" + this.job); console.log(this);// Object {, job="good", show=function()} }; obj.show();//Name:sharpxiajun;Job:Software otherObj.show();//Name:xtq;Job:good </script>
这是我上篇讲到的关于this使用的一个例子,写法一是我们大伙都爱写的一种写法,里面的this指针不是指向window的,而是指向Object的实例,firebug的显示让很多人疑惑,其实Object就是面向对象的类,大括号里就是实例对象了,即obj和otherObj。Javascript里通过字面量方式定义对象的方式是new Object的简写,二者是等价的,目的是为了减少代码的书写量,可见即使不用new操作字面量定义法本质也是new操作符,所以通过new改变this指针的确是不过攻破的真理。
下面我使用javascript来重写本篇开头用java定义的类,代码如下: