北京时间今天凌晨,来自Twitter的前端工程师Angus Crol,在柏林举办的JSConf会议上,进行了题为"Break all the Rulez"的演讲,主要讲了一些我们通常认为是错误的不该使用的东西,其实是有用的.本文最下面有演讲用的slides.远在美国的JavaScript之父看了slides也说:我同意其中大部分观点(看来还是有问题?).
下面我把要点简单翻译一下,不做扩展解释.
with语句
为什么不去使用它?
1.意外的运行结果,可能隐式创建全局变量
2.闭包作用域解析过多消耗
3.后期编译
有人说,ES5的严格模式可以防止隐式创建全局变量(不用var),这样能减少with的一个问题.但是...
严格模式也不能使用with啊.
为什么说它是有用的?
1.构建浏览器开发者工具
- //Chrome Developer Tools
- IS._evaluateOn =
- function(evalFunction, obj, expression) {
- IS._ensureCommandLineAPIInstalled();
- expression =
- "with (window._inspectorCommandLineAPI) {\
- with (window) { " + expression + " } }";
- return evalFunction.call(obj, expression);
- }
2.模拟块级作用域
- //是的,还是这个老掉牙的问题
- var addHandlers = function(nodes) {
- for (var i = 0; i < nodes.length; i++) {
- nodes[i].onclick =
- function(e) {alert(i);}
- }
- };
- //你可以通过在外面包一个函数来解决
- var addHandlers = function(nodes) {
- for (var i = 0; i < nodes.length; i++) {
- nodes[i].onclick = function(i) {
- return function(e) {alert(i);};
- }(i);
- }
- };
- //或者使用'with'来模拟块级作用域
- var addHandlers = function(nodes) {
- for (var i = 0; i < nodes.length; i++) {
- with ({i:i}) {
- nodes[i].onclick =
- function(e) {alert(i);}
- }
- }
- };
#p#
eval语句
为什么不去使用它?
1.代码注入
2.无法进行闭包优化
3.后期编译
为什么说它是有用的?
1. JSON.parse不可用的时候
有人在Stack Overflow上说:
“JavaScript的eval是不安全的,使用json.org上的JSON解析器来解析JSON”
还有人说:
“不要使用eval来解析JSON!用道格拉斯写的json2.js!”
可是:
- // From JSON2.jsif (/^[\],:{}\s]*$/ .test(text.replace(/*regEx*/, '@') .replace(/*regEx*/, ']') .replace(/*regEx*/, ''))) { j = eval('(' + text + ')');}
2.浏览器的JavaScript控制台都是用eval实现的
在Webkit控制台或JSBin中执行下面的代码
- >(function () { console.log(String(arguments.callee.caller))})()function eval() { [native code]}
John Resig说过:
“eval和with是被轻视的,被误用的,被大部分JavaScript程序员公然谴责的,但如果能正确使用的话,可以用它们写出一些奇妙的,无法用其他功能实现的代码”
Function构造函数
为什么说它是有用的?
1.代码运行在可预见的作用域内
2.只能动态创建全局变量
3.不存在闭包优化的问题
用在了什么地方?
1. jQuery的parseJSON
- // jQuery parseJSON
- // Logic borrowed from http://json.org/json2.js
- if (rvalidchars.test(data.replace(rvalidescape,"@")
- .replace( rvalidtokens,"]")
- .replace( rvalidbraces,""))) {
- return ( new Function( "return " + data ) )();
- }
2.Underscore.js的字符串内插
- //from _.template
- // If a variable is not specified,
- // place data values in local scope.
- if (!settings.variable)
- source = 'with(obj||{}){\n' + source + '}\n';
- //..
- var render = new Function(
- settings.variable || 'obj', '_', source);
#p#
==运算符
为什么不去使用它?
1.强制将两边的操作数转换为相同类型
为什么说它是有用的?
1.强制将两边的操作数转换为相同类型
2.undefined == null
- //这样写是不是很麻烦
- if ((x === null) || (x === undefined))
- //完全可以下面这样写
- if (x == null)
3.当两边的操作数类型明显相同时使用
- typeof thing == "function"; //typeof运算符肯定返回字符串
- myArray.length == 2; //length属性肯定返回数字
- myString.indexOf('x') == 0; //indeOf方法肯定返回数字
真值不一定==true,假值不一定==false
- if ("potato") {
- "potato" == true; //false
- }
Array构造函数
为什么不去使用它?
1.new Array()也是evil的?JSLint也推荐使用[].
为什么说它是有用的?
- //From prototype.js extension of
- //String.prototype
- function times(count) {
- return count < 1 ?
- '' : new Array(count + 1).join(this);
- }
- 'me'.times(10); //"memememememememememe"
其他
不要扩展原生的原型对象
(es 5 shims都这么干)
在for/in遍历时总要使用hasOwnProperty
(在没有扩展对象原型的前提下没有必要这么做)
把所有的var语句放在顶部
(for语句还是放在初始化表达式中好)
要在调用函数之前先声明函数
(在优先考虑实现细节时使用)
不要使用逗号运算符
(在使用多个表达式时的时候可以使用)
使用parseInt时总要把第二个参数指定为10
(除非字符串以‘0’或‘x’开头,否则没必要)
#p#
译者注
上面说了这么多,我自己也想到一个被误解的东西,那就是escape.网上有不少人说:"不要使用escape".
为什么说它是有用的?
1.escape转义的字符更多,有时候需要转义后两个函数不转义的字符.
ASCII char | escape() | encodeURI() | encodeURIComponent() |
---|---|---|---|
! | %21 | ! | ! |
# | %23 | # | %23 |
$ | %24 | $ | %24 |
& | %26 | & | %26 |
' | %27 | ' | ' |
( | %28 | ( | ( |
) | %29 | ) | ) |
+ | + | + | %2B |
, | %2C | , | %2C |
/ | / | / | %2F |
: | %3A | : | %3A |
; | %3B | ; | %3B |
= | %3D | = | %3D |
? | %3F | ? | %3F |
@ | @ | @ | %40 |
~ | %7E | ~ | ~ |
2.将字符串转换为UTF8编码,通常用在base64的时候.
escape相当于是在utf16编码的字符串上转义,
encodeURIComponent
相当于是先把utf16的字符串转换成utf8编码后再escape.
encodeURIComponent
(str) === escape(UTF16ToUTF8(str))
可以推导出UTF16ToUTF8(str) === unescape( encodeURIComponent( str )
然后就能用在base64编码的时候,比网上看到的那些简单多了吧.注意btoa和atob有兼容问题.
- function base64Encode(str) {
- return btoa(unescape(encodeURIComponent(str)));
- }
- function base64Decode(str) {
- return decodeURIComponent(escape(atob(str)));
- }