Javascript 面向对象化写法——语法篇
本编参考:http://www.cnblogs.com/sanshi/archive/2009/07/08/1519036.html 系列,总结如下
一.几个重要关键字
1.this —— 指当前的对象,若在全局范围,则指当前页面对象window,如果是函数中使用this,则指的是调用这个函数的对象。firebug一下demo:
function sayHi() { console.debug(this); } var obj = { sayHi: sayHi, sayHi2: function() { return function() { console.debug(this); } } }; sayHi(); // 结果为window对象 obj.sayHi(); // 结果为obj对象 obj.sayHi2()(); // 结果为window对象
解析:在javascript当中,一切皆为对象,function也是,所以可以看到全局函数sayHi可以像变量一般pass给obj的sayHi属性。三个函数调用的,前两个不用多解析,看第三个,其实等价于下面写法:
var f = obj.sayHi2(); f();
sayHi2函数返回一个函数对象,赋予变量f,f就是个函数了,然后 f 被window调用执行,如此就这么回事了。
再看一个demo:
function TestThis () { this.oham = 'oham'; console.debug(this); } TestThis(); // window对象 new TestThis(); // TestThis 对象实例本身
对于TestThis();的结果不奇特,而new TestThis();之神奇在于new,其内部机制在下就不懂了...只可以看出,通过new XXX();这样调用,XXX方法中的this就指向XXX构造出的对象实例本身了。
2.apply与call
接着上面的例子,介绍apply 与 call,它们均是全局函数,归Function所有(Function属于javascript内部对象,不是Function的对象是没有这两个方法的)。 接上文的demo:
... obj.sayHi2()(); //结果为window对象 obj.sayHi2().apply(obj); // 结果为obj对象 obj.sayHi2().call(obj); // 结果为obj对象
apply 与 call 的 作用是改变函数中this的指向,即使函数看起来好像被谁调用一样,其作用域也是那个谁的。
apply 与 call 的区别,仅仅是参数定义不同,请看如下demo:
function ml(me, mm) { console.log(this); if( this === window ) { console.log(me + ',' + mm + ' not possible in public'); }else { console.log(me + ',' + mm + ' so happy'); } } var hotel = {}; ml('me', 'mm'); ml.apply(hotel, ['me', 'mm']); ml.call(hotel, 'me', 'mm');
请实践一下,代码,便知知道,apply的定义的参数列表是个数组,call是参数列(估计是function call(obj, params...))。
**至此深觉有必要介绍javascript中闭包的概念。闭包,指的是语法上表示包含不被计算的变量的函数,也就是说,函数可以使用函数之外定义的变量。
demo(以下摘自w3cschool):
var sMessage = "hello world"; function sayHelloWorld() { alert(sMessage); } sayHelloWorld();
在上面这段代码中,脚本被载入内存后,并没有为函数 sayHelloWorld() 计算变量 sMessage 的值。该函数捕获 sMessage 的值只是为了以后的使用,也就是说,解释程序知道在调用该函数时要检查 sMessage 的值。sMessage 将在函数调用 sayHelloWorld() 时(最后一行)被赋值,显示消息 "hello world"。
再看看自己的一个demo:
var outSide = 'madom'; var room = { mm : 'mm', watch: function(me) { return function () { if(this.outSide) { //尝试 console.debug(me + ' can watch ' + this.outSide); }else { console.debug(me + ' can\'t watch outSide, but never mind.'); } if(this.mm) { //尝试 console.debug(me + ' can watch ' + this.mm); }else { console.debug(me + ' can\'t watch mm, not happy'); } } }, touch: function (me) { if(this.outSide) { //尝试 console.debug(me + ' can touch ' + this.outSide + ', but won\'t do that'); }else { console.debug(me + ' can\'t touch outSide, but never mind.'); } if(this.mm) { //尝试 console.debug(me + ' can touch ' + this.mm + ', so cool...'); }else { console.debug(me + ' can\'t touch mm, not happy'); } } }; var watchFunc = room.watch('I'); //全局调用 watchFunc(); console.log('-------------------------------------'); //room范围调用 watchFunc.apply(room); console.log('============================================'); //room范围调用 room.touch('I'); console.log('----------------------------------------'); //全局调用 room.touch.apply(this, ['I']);
结果为:
"I can watch madom" "I can't watch mm, not happy" "-------------------------------------" "I can't watch outSide, but never mind." "I can watch mm" "============================================" "I can't touch outSide, but never mind." "I can touch mm, so cool..." "----------------------------------------" "I can touch madom, but won't do that" "I can't touch mm, not happy"
这个结果好解析,this为谁调用就是谁,接着请尝试把demo中标有//尝试的if 语句中的"this."删除,
情况会如下:
"I can watch madom" "Uncaught ReferenceError: mm is not defined (line 15)"
执行到watch函数的if(mm) 这句出错了,无论apply部apply结果都一样,原因具体我就不清楚了,这里只是证实了,对于一个变量如name, name 并不是等价于this.name(跟java大不同) ,JS的解释程序不会为你做这个,当函数被调用的时候,它只会先从当前函数范围去找,若无命中变量,则直接到全局范围去找。可以把mm部分的代码去掉,只留outSide的,结果是无论apply与否,结果都能访问的变量outSide。
3.prototype——对该对象的对象原型的引用。对于所有的对象,它默认返回 Object 对象的一个实例(w3c定义)。
prototype本质上还是一个JavaScript对象,个人觉得上述定义不太妥当,实际上prototype感觉更像对象的一个模板,而非实例, 并且每个函数都有一个默认的prototype属性。 以demo为证:
var room = { mm: 'mm', watch: function () { } }; console.log(room.prototype); // undefined console.log(room.valueOf()); // room object