JavaScript 的几个 tip
1、JavaScript 中的继承
在 JavaScript 中实现继承的方法是:将子类的 prototype 属性设置为父类的对象。
例如,我有一个 Basket 类,继承 Hashtable 类:
如果不写上面第二句,以后 typeof 一个 Basket 的对象获得的就是 Hashtable 了。
但是需要注意的是,由于所有的子类(Basket)的对象实例共享同一个原型对象(Hashtable),因此在父类对象的构造函数中只能保存对所有子类对象均相同的实例变量。对每个子类不同的实例变量必须放在子类的的构造函数中。很多时候父类对象没有任何自己的实例变量,而只有一些方法,操作的是子类对象的实例变量。
2、JavaScript 中变量的作用域
alert 出来的是什么?你肯定会说是 "aaa",错,alert 出来的是 "undefined"。为什么?
因为 JavaScript 为了简化处理,变量实际上只有两种作用域,全局变量和函数的内部变量。你在函数内部任何一个地方定义的变量其作用域都是整个函数体。而在 alert 的时候函数的内部变量 v 已经定义了,并且覆盖掉了同名的全局变量 的定义,只是还没有被初始化,所以 alert 出来的是 "undefined"。
3、undefined 和 null
有些开发人员误以为在 JavaScript 中,
return;
返回的就是 null,也就是
return null;
这是一个误解,在 JavaScript 中,
return;
返回的是 undefined,而不是 null。这两个值是有区别的,请看《JavaScritp 权威指南》的相关内容。最好明确地写为:
return null;
这个问题同样在传递参数时候也会出现,例如:
alert 显示 v2 的值是 undefined。
虽然在 JavaScript 中:
undefined == null;
得到的结果是 true,将一个 typeof 为 "undefined" 的标识符与 null 做等值比较结果也是 true,似乎这两个值可以混用。但是为了理解的清晰,最好还是把这两个值区分开。
还有一个需要注意的是在 SP2 以前版本的 IE5 中,不能直接拿 undefined 与其它值比较,例如:
在 SP3 以上版本的 IE5、IE6 都没问题,但是在 SP2 版本前的 IE5 中无法执行。必须要写成:
4、当你为一个类添加一个方法时,你经常这样写:
你可能想当然地认为 f1 就是这个方法的名称。你想错了,f1 只是这个类的对象的一个普通属性而已,它的值可以是任何类型。这个方法是匿名的,在全局名字空间中并没有它的标识符。如果想在全局名字空间获得一个标识符,你应该采用如下两种方式:
或者
这样做的好处是你现在可以在多个类中重用相同的方法(可能性似乎很小,是吧?)。
我这里还是按照面向对象的习惯叫做方法。一般在 JS 中我把类中的 function 称做方法,而把全局范围的 function 称做函数。
5、重载父类的方法后如何调用父类的方法?
你现在已经知道 JS 完全可以实现继承了,当然也可以实现重载,否则还能叫面向对象语言吗?比如在父类 BaseClass 中有一个 f1 的方法,子类 SubClass 要重载这个方法,只需要这样写:
且慢,如果我还想调用父类的方法怎么办呢?不必担心,
这里加的一句就是调用父类的 f1 方法。apply 换成 call 也是可以的。
不过 JS 因为函数参数数量是可变的,所以它并不会把
function f1 (arg1, arg2, arg3)
和
function f1 ()
看做是两个函数,它会认为它们就是一个函数(小子,换了个马甲就以为我认不出你了?),后面出现的定义会覆盖前面的定义。所以即使你改变子类方法的签名,它仍然认为是重载了父类同名的方法。
那么如果我还想调用父类的构造函数怎么办呢?这样做:
6、JavaScript 开发如何做自动测试?
JavaScript 也是可以做自动测试的。xUnit 框架在 JS 领域的嫡系传人是 JsUnit(http://jsunit.berlios.de),基本上就是 JUnit 的 JS 版,连例子都是从 JUnit 的例子移植过来的。
如何用 TDD 方式做 JS 开发?这个话题可就大了,足够说上几天的,以后我会另开主题讨论这个问题。
待续...
7、为什么 type="file" 的 input 控件的 value 是只读的?
这次这个问题与 JS 本身没有多大关系。而是一个 Web 安全设计方面的问题。
我们来想想,假设这个 input 控件的 value 不是只读的会出现什么情况。
你上网冲浪的时候打开了一个页面,这个页面上有一个很好玩的 flash 游戏,你玩得非常开心。可是就在你愉快地玩游戏时在页面的背后静悄悄地发生了一些你所不愿意发生的事情。
在这个页面上有一个隐藏的 iframe,iframe 中有一个 form 表单,表单中有一个 type="file" 的 input 控件,有一段 JavaScript 或者 VBScript 代码把这个 input 控件的 value 设置成了你的机器上某个重要文件(比如,你是个懒人,从来不愿意记忆信用卡账号和密码,宁可把这些信息存在机器上的某个 txt 文件中)直接上传到了服务器。
现在那个服务器的管理员和你一样乐呢。而你就等着哭吧。
为什么要说这个问题,是因为我今天试图做一件事:不刷新页面上传文件。
很多聊天城一类的网站都可以不刷新页面提交数据,做到这一点有以下途径:
1、通过 XMLHTTP 提交数据。但是并不是每个人都很熟悉 XML。
2、在页面上面放一个不占空间(长宽都等于 0)的 IFrame,当需要提交数据的时候,将提交数据的 Form 复制到 IFrame 所在的 Document 中,然后在 IFrame 中完成提交。
3、使用 Applet 或者 ActiveX 控件直接向服务器提交数据。
我写了一个 upload 的控件,现在想通过第 2 种方式来实现不刷新页面上传文件的效果(这样用起来更方便,编程也更简单,不需要把很多东西放在 hidden 字段中)。事实证明通过这种方式是完全做不到的。
这确实是个好方法,谢谢了!这样做任何事情都不需要刷新页面了。
XMLHTTP 在缺省的安全级别不能 load 本地文件,只能 load 与运行脚本位于相同 domain 的文件。用 form 的多个 file 控件同样可以一次上传多个文件,form 方法是这个问题的正解。
在 JavaScript 中实现继承的方法是:将子类的 prototype 属性设置为父类的对象。
例如,我有一个 Basket 类,继承 Hashtable 类:
java代码: ![]() |
1 Basket.prototype = new Hashtable(); 2 Basket.prototype.constructor = Basket; 3 |
如果不写上面第二句,以后 typeof 一个 Basket 的对象获得的就是 Hashtable 了。
但是需要注意的是,由于所有的子类(Basket)的对象实例共享同一个原型对象(Hashtable),因此在父类对象的构造函数中只能保存对所有子类对象均相同的实例变量。对每个子类不同的实例变量必须放在子类的的构造函数中。很多时候父类对象没有任何自己的实例变量,而只有一些方法,操作的是子类对象的实例变量。
2、JavaScript 中变量的作用域
java代码: ![]() |
1 var v = "aaa"; ![]() 3 alert(v);
4 ... 5 var v = "bbb"; 6 } |
alert 出来的是什么?你肯定会说是 "aaa",错,alert 出来的是 "undefined"。为什么?
因为 JavaScript 为了简化处理,变量实际上只有两种作用域,全局变量和函数的内部变量。你在函数内部任何一个地方定义的变量其作用域都是整个函数体。而在 alert 的时候函数的内部变量 v 已经定义了,并且覆盖掉了同名的全局变量 的定义,只是还没有被初始化,所以 alert 出来的是 "undefined"。
3、undefined 和 null
有些开发人员误以为在 JavaScript 中,
return;
返回的就是 null,也就是
return null;
这是一个误解,在 JavaScript 中,
return;
返回的是 undefined,而不是 null。这两个值是有区别的,请看《JavaScritp 权威指南》的相关内容。最好明确地写为:
return null;
这个问题同样在传递参数时候也会出现,例如:
java代码: ![]() |
![]() 2 alert(v2); 4 func(1);
3 } |
alert 显示 v2 的值是 undefined。
虽然在 JavaScript 中:
undefined == null;
得到的结果是 true,将一个 typeof 为 "undefined" 的标识符与 null 做等值比较结果也是 true,似乎这两个值可以混用。但是为了理解的清晰,最好还是把这两个值区分开。
还有一个需要注意的是在 SP2 以前版本的 IE5 中,不能直接拿 undefined 与其它值比较,例如:
java代码: ![]() |
![]() 2 ... 4
3 } |
在 SP3 以上版本的 IE5、IE6 都没问题,但是在 SP2 版本前的 IE5 中无法执行。必须要写成:
java代码: ![]() |
![]() 2 ...
3 } |
4、当你为一个类添加一个方法时,你经常这样写:
java代码: ![]() |
![]() 2 ...
3 } |
你可能想当然地认为 f1 就是这个方法的名称。你想错了,f1 只是这个类的对象的一个普通属性而已,它的值可以是任何类型。这个方法是匿名的,在全局名字空间中并没有它的标识符。如果想在全局名字空间获得一个标识符,你应该采用如下两种方式:
java代码: ![]() |
![]() 2 ... 4 ClassName.prototype.f1 = func1;
3 } |
或者
java代码: ![]() |
![]() 2 ...
3 } |
这样做的好处是你现在可以在多个类中重用相同的方法(可能性似乎很小,是吧?)。
我这里还是按照面向对象的习惯叫做方法。一般在 JS 中我把类中的 function 称做方法,而把全局范围的 function 称做函数。
5、重载父类的方法后如何调用父类的方法?
你现在已经知道 JS 完全可以实现继承了,当然也可以实现重载,否则还能叫面向对象语言吗?比如在父类 BaseClass 中有一个 f1 的方法,子类 SubClass 要重载这个方法,只需要这样写:
java代码: ![]() |
![]() 2 ...
3 } |
且慢,如果我还想调用父类的方法怎么办呢?不必担心,
java代码: ![]() |
![]() 2 BaseClass.prototype.f1.apply(this, arguments);
3 ... 4 } |
这里加的一句就是调用父类的 f1 方法。apply 换成 call 也是可以的。
不过 JS 因为函数参数数量是可变的,所以它并不会把
function f1 (arg1, arg2, arg3)
和
function f1 ()
看做是两个函数,它会认为它们就是一个函数(小子,换了个马甲就以为我认不出你了?),后面出现的定义会覆盖前面的定义。所以即使你改变子类方法的签名,它仍然认为是重载了父类同名的方法。
那么如果我还想调用父类的构造函数怎么办呢?这样做:
java代码: ![]() |
![]() 2 BaseClass.call(this, arg1, arg2);
3 ... 4 } |
6、JavaScript 开发如何做自动测试?
JavaScript 也是可以做自动测试的。xUnit 框架在 JS 领域的嫡系传人是 JsUnit(http://jsunit.berlios.de),基本上就是 JUnit 的 JS 版,连例子都是从 JUnit 的例子移植过来的。
如何用 TDD 方式做 JS 开发?这个话题可就大了,足够说上几天的,以后我会另开主题讨论这个问题。
待续...
7、为什么 type="file" 的 input 控件的 value 是只读的?
这次这个问题与 JS 本身没有多大关系。而是一个 Web 安全设计方面的问题。
我们来想想,假设这个 input 控件的 value 不是只读的会出现什么情况。
你上网冲浪的时候打开了一个页面,这个页面上有一个很好玩的 flash 游戏,你玩得非常开心。可是就在你愉快地玩游戏时在页面的背后静悄悄地发生了一些你所不愿意发生的事情。
在这个页面上有一个隐藏的 iframe,iframe 中有一个 form 表单,表单中有一个 type="file" 的 input 控件,有一段 JavaScript 或者 VBScript 代码把这个 input 控件的 value 设置成了你的机器上某个重要文件(比如,你是个懒人,从来不愿意记忆信用卡账号和密码,宁可把这些信息存在机器上的某个 txt 文件中)直接上传到了服务器。
现在那个服务器的管理员和你一样乐呢。而你就等着哭吧。
为什么要说这个问题,是因为我今天试图做一件事:不刷新页面上传文件。
很多聊天城一类的网站都可以不刷新页面提交数据,做到这一点有以下途径:
1、通过 XMLHTTP 提交数据。但是并不是每个人都很熟悉 XML。
2、在页面上面放一个不占空间(长宽都等于 0)的 IFrame,当需要提交数据的时候,将提交数据的 Form 复制到 IFrame 所在的 Document 中,然后在 IFrame 中完成提交。
3、使用 Applet 或者 ActiveX 控件直接向服务器提交数据。
我写了一个 upload 的控件,现在想通过第 2 种方式来实现不刷新页面上传文件的效果(这样用起来更方便,编程也更简单,不需要把很多东西放在 hidden 字段中)。事实证明通过这种方式是完全做不到的。
eternalee 写道: |
用form target=iframename不行吗 |
这确实是个好方法,谢谢了!这样做任何事情都不需要刷新页面了。
xiaoyu 写道: |
如果是上传N个文件,xmlHttp是非常不错的选择(网上有例子),因为xml能描述二进制(不知道是否应该用描述这个词呢?)。 |
XMLHTTP 在缺省的安全级别不能 load 本地文件,只能 load 与运行脚本位于相同 domain 的文件。用 form 的多个 file 控件同样可以一次上传多个文件,form 方法是这个问题的正解。