最近在着手把手上的Nodejs代码全面升级到strong模式,strong模式是V8实现的一种新的模式,主要的变化我在前面的文章中已经写过。
然而事情并没有想象中顺利,将所有文件第一行的'use strict'换成'use strong'之后,一运行,立马出现一个报错TypeError: In strong mode, accessing missing property 'NODE_ENV' of #<Object> is deprecated。
查看代码发现是这行
constisProduction=process.env.NODE_ENV==='production';
看起来似乎是很莫名其妙的错误,就换了个模式而已,好端端的代码怎么报这样一个闻所未闻见所未见的奇葩错误呢?
然而我已决定深究下去,将错误信息翻译过来就是,在strong模式下,访问对象不存在的属性已被废弃,而且会报TypeError的错误。
看到strong mode下的报错可以断定是V8引起的了,但是网上关于strong mode的介绍确实没有提起过这个变化。
于是我在V8的源码里面找了一圈,果然很快在 头文件messages.h 找到这行
T(StrongPropertyAccess,"In strong mode, accessing missing property '%' of % is deprecated")
为了方便说明,下文就将这个变化点叫做StrongPropertyAccess。
等等,看到这句话是不是很伤心。访问不存在的属性?JS程序员不是天天都干这事吗?这也会报错?简直颠覆三观。
相信几乎所有写过JS的都有写过类似这样的代码,特别在处理默认值的时候肯定会这样写
假设这是一行在处理ajax请求的默认方法的代码
vartype=option.method||'GET';
如果option没有method这个属性(或者getter),那么顺理成章的,type的值就是'GET'。 但是,在strong模式下,这行代码行不通了,而且会报错,这不是完全颠覆了我们的代码习惯了吗。
看来V8这是要彻底改变我们的编码习惯啊。
网上对StrongPropertyAccess也是议论纷纷,有支持的也有反对的,但我没找到官方对StrongPropertyAccess这样改的详细理由。
猜测的话,可能是为了避免这种易混淆情况
varobj={ a:undefined }; obj.a// undefined obj.b// undefined
先温习一下访问对象obj的属性a的过程:
如果obj确实有属性a,则返回a的值;如果obj没有属性a,则自底而上查找原型链上的对象,直到找到属性a并返回,否则直到原型链最顶层(通常是Object.prototype)也没找到属性a,则返回undefined。
这时候在访问obj的属性a和b的时候表现是一样的,如果没看到代码,我们很可能会认为属性a和b都不存在,但事实上属性a是存在的。这种代码就容易造成混淆,而StrongPropertyAccess就很好的区分开了这两种情况,在访问属性b的时候就会报错。
另外值得一提的是,TypeScript在编译阶段就会认为这是一种错误。
诚然,strong mode避免了这样的混淆,不过同时也让JS丧失了一些灵活性。
strong mode的诞生,很大程度上是为了提升V8性能来的,但StrongPropertyAccess在性能方面,从代码和原理方面来理解的话似乎不会有所提升。
但如果选择了strong mode,就得适应新的编码习惯。
再看看这段代码,假设在处理ajax请求的timeout参数
varoption={ timeout:0 }; vartimeout=option.timeout||1000;
这段代码的想法可能是,接受参数传的timeout的值,如果没传则默认是1秒。 但是由于疏忽,忘了考虑传0的情况(timeout等于0意味着没有超时机制),这样的话0就永远无法生效。
代码应该改成这样
vartimeout='timeout'inoption?option.timeout:1000;
这里只讨论timeout要么没传值,要么是合法值的情况。
用ES2015的话,有种更优雅的写法
var{timeout=1000}=option;
这就是解构赋值的语法,跟
vartimeout='timeout'inoption?option.timeout:1000;
完全等价
再废话一句吧,ES2015是趋势,新的语法简洁清晰,虽然现在支持的环境并不多,不过 搭配babel可以转换成主流环境都支持的语法 。