转:http://www.cnblogs.com/tolg/p/4873000.html

没有哪种数据结构比JavaScript的对象更简单灵活了。作为一个弱动态类型语言,JavaScript对对象的属性没有任何约束, 这带来的结果就是,在使用的时候很爽,想加啥属性直接加上去就行了,根本没有类或模板的限制, 想读啥属性直接“点”出来就行了,写出来那是相当简洁;然而这样的代码在运行的时候呢……

JavaScript这种灵活性最大的一个问题也是没有约束。比如一个网店系统有两个部分,一部分产生订单对象, 另一部分拿到订单对象来展示。咱们前端程序员自然是干后面展示那部分事儿的,比如要在页面上展示个订单里面商品的价格, 就会掉order.product.price.sum。然而写产生订单哪部分代码的哥们儿不一定靠谱, 订单里有个赠品就是没价格,price字段干脆没有,前端还是调order.product.price.sum,得,js代码执行到这就算死了, 后面啥也出不来了。更可怕的是现在很多系统引入了node,把前后端分离界限往后移了一截, 这回在node上调用order.product.price.sum,得,整个页面都出不来了。

是不是觉得这个例子挺low的?估计你们公司已经规定好了订单数据里的产品一定会有price字段,哪怕真没价格后端大哥也会给你传个{}。 况且人家后端用的是java,强静态类型语言,各种属性早就定义好了,咋能丢呢? 再说上线前要测试那么长时间,怎么会在线上出这种问题呢?

现在JSON已经是横飞于网络之间的标准数据结构,即便你们系统后端是用java写的,前端调用这个写后端接口时得到的还是JSON。 可是这个JSON是从啥转过来的呢?你指望一定是某个类的对象吗?够呛。后端大哥也许早发现了Java太死板, 为了贴合灵活的JSON,人家早用map了。再说,即便后端老老实实的用class,如果你们的网店系统很大, 有好几个店不同种类的订单,这些订单恐怕不是出自于同一个class吧,字段难免有差别,你能保证你们开发团队文档完整到你能掌握所有类的属性吗?

反正我们没有这么全的文档,即使有时候有点文档,后端大哥也会善意的告诉我:以代码为准!

如果想在上线前把这种缺失字段的bug全都测出来,我觉得太难为QA兄弟了,后端的数据又不是他们说了算, 保不齐上线很长时间后突然真给少点啥。

人总是不靠谱的,还得从代码上想办法。

最直接的办法是小心翼翼步步为营:在每调用一个有风险的属性的时候都判断一下其父节点是否存在。 这判断谨慎到什么程度就要估计一下数据来源的靠谱程度了。对于前面商品总价的例子,一个订单里总不会连商品都没有吧, 所以代码可以是这个样子:

var showPrice
if(order.product.price)
showPrice = order.product.price

实际情况往往比较复杂,比如我现在要取的数据是订单中一款产品的生产厂家的联系方式里面若干电话中的第一个, 而且我不太信任后端接口,连订单中有没有产品都不敢保证,那么……

var temp, temp1, temp2, temp3, phone;
if ((temp = order.product) != null) {
if ((temp1 = temp.seller) != null) {
if ((temp2 = temp1.contact) != null) {
if ((temp3 = temp2.phone) != null) {
phone = temp3[0];
}
}
}
}

如此拙劣!如果JS要这么写我宁愿回头做后端去写Java!

并没有~~ ヾ(¯∇ ̄๑)

我用coffeescript。其实我们现在所做的项目没有构建与coffee之上的,但是我用coffee,怎么用是另一个话题, 反正用了coffee,这段代码就是这样:

phone = order.product?.seller?.contact?.phone?[0]

遗憾的是,我是个多少有些强迫症的人,每当看到那么多问号就会联想到编译后的那一堆if,我都替电脑累。 再说这问号很容易遗漏,悄悄地少个问号很难发现。总之我只会在我自认为有可能缺失的属性前面加个问号。

其实做这些判断要做的无非就是一旦调用链上某个属性缺失就直接返回undefined。js本身不提供这玩意儿,自己封装一个。

Object.prototype.getAttr = function(path){
attrLink = path.split('.');
var ref = this[attrLink[0]];
for(var i=1; i < attrLink.length; i++){
if(ref)
ref = ref[attrLink[i]];
else
return undefined;
}
return ref;
}

于是访问属性就成了这样:

order.getAttr('product.seller.contact.phone.0')

这样就安全了。可是调试的时候我宁愿让解释器用红色的异常告诉我到底是谁没定义或者是空也不想悄咪咪的就得到一个未定义值。 办法是有的,上面的代码在ref为空的时候输出个日志就行了。最后的问题是getAttr方法并非是获取属性的强制方法, 它不像Java,只要把属性私有了就只能用getter。别说保证其它小伙伴一定要用这个方法取属性,就连我自己可能都写着写着就直接点上了。 最终,这个Object.getAttr的办法我在实际开发中从未用过。

感觉上前面那种在cofee里面加问号的办法也就够了,如果我们自己都觉得这个属性必须得有而后端接口就是没传那也太那个啥了。 但真实情况是一切皆有可能,这也不能怪后端大哥,没准这订单数据是从其他系统发送过来的或者是很古老的奇葩数据。

咋闹?我的原则是尽可能把损失降到最低,也就是把异常影响的代码范围缩到最小。这就要靠try..catch。

在哪儿try要看代码的情况,总不可能逮哪try哪。不过还是有些规律的。

那些不靠谱的数据应该不是我们自己写的,而是来源于外部接口,对这个接口数据利用的代码肯定是有一定范围的, 最起码的情况是把这一段给try了。这还太粗糙。在看看数据的特征,大量外部数据往往是可迭代的, 比如会显示一个订单列表,对这个订单列表肯定会有个循环,那么针对循环内部try一下,这样顶多是一个订单无法显示。 还有局部数据有没有统一处理的地方,比如我做的node项目使用handlebars模板,模板会用到很多自定义的helper, 这些helper大多数是用于把数据处理成显示的结果,比如格式化价格什么的。比较巧的是这些helper都有一个统一的代码入口:

helpers =  _.extend(
require('./helper1'),
require('./helper2'),
require('./helper3')
)
// “_”是underscore或者lodash

其实也就是把各个文件中的helpers整合成了一个大对象,然后:

module.exports = _.reduce(helpers, function(memo, f, k){
memo[k] = function(){
try {
return f.apply(this, arguments)
} catch (e) {
console.error('handlebars helper error', e)
return ''
}
}
return memo
}, {})

在export前把所有helper函数都包裹在一个try...catch中。这样在一个helper中有字段缺失的错误也就会引起一小点东西显示不出来。

其实我把所有的helper集中到一个大对象中其初衷并非是为了这个,而是由于把每一个helper文件都注册一遍看起来重复太多,觉得不爽。 不是有一个著名的代码维护原则大概是说“任何重复的代码都应该避免”吗?这就看出来好处了。

好像看起来完美了,也不然。后来一哥们儿又写了个helper,没放到我的这些helpers里面,而是在别的文件中单独注册去了, 结果奇葩的数据真的出现了,悲剧真的防不胜防地上演了。。。

好吧,说到底想尽各种奇技淫巧也抵不过不按规矩出牌。其实要真的前后端所有同志把数据格式规定好了,并且严格执行, 我写的这一大堆全是废话。

JavaScript的对象——灵活与危险的更多相关文章

  1. JavaScript原生对象及扩展

    来源于 https://segmentfault.com/a/1190000002634958 内置对象与原生对象 内置(Build-in)对象与原生(Naitve)对象的区别在于:前者总是在引擎初始 ...

  2. javascript 全局对象--w3school

    JavaScript全局对象 1.  decodeURI()解析某个编码的URI. 2.decodeURInComponent()解析一个编码的URI组件. 3.encodeURI()把字符串编码为U ...

  3. JavaScript Json对象和Json对象字符串的关系 jsonObj<->JsonString

    JavaScript Json对象和Json对象字符串的关系 jsonObj<->JsonString 如下示例: 直接写的a1就是一个Json对象,a2 就是一个Json对象字符串; 通 ...

  4. 从零构建JavaScript的对象系统

    一.正统的类与继承 类是对象的定义,而对象是类的实例(Instance).类不可直接使用,要想使用就必须在内存上生成该类的副本,这个副本就是对象. 以Java为例: public class Grou ...

  5. 关于javascript自定义对象(来自网络)(最近几天不会的)

    javascript定义对象的几种简单方法 1.构造函数方式,全部属性及对象的方法都放在构造方法里面定义 优点:动态的传递参数 缺点:每创建一个对象就会创建相同的方法函数对象,占用大量内存 funct ...

  6. 据说每个大牛、小牛都应该有自己的库——JavaScript原生对象拓展

    在据说每个大牛.小牛都应该有自己的库——框架篇中我扬言要做个小牛,没想到一天没更新,小伙儿伴们就戏谑的问我,油哥是不是要太监了?其实事情是这个样子的,这不是太监的节奏,一是,关于写个自己的库的想法由来 ...

  7. JavaScript RegExp 对象

    JavaScript RegExp 对象 RegExp 对象用于规定在文本中检索的内容. 什么是 RegExp? RegExp 是正则表达式的缩写. 当您检索某个文本时,可以使用一种模式来描述要检索的 ...

  8. JavaScript String 对象

    JavaScript String 对象 String 对象 String 对象用于处理文本(字符串). String 对象创建方法: new String(). 语法 var txt = new S ...

  9. JavaScript Math 对象

    JavaScript Math 对象 Math 对象 Math 对象用于执行数学任务. Math 对象并不像 Date 和 String 那样是对象的类,因此没有构造函数 Math(). 语法 var ...

随机推荐

  1. Gengxin讲STL系列目录

    引言:有人催我写关于STL的博客#(滑稽)        STL嘛,昨晚有人一直逼问我STL名字的由来——STL = Standard Template Library,标准模板库,惠普实验室开发的一 ...

  2. Nvidia CUDA 6 Installed In Ubuntu 12.04

    环境:ubuntu 12.04 (x64) 如果不能够 service lightdm stop,显示:unknown service 或者其他的 sudo /etc/init.d/lightdm r ...

  3. 数值的N次方

    问题描述: 实现函数double Power(double base,int exponent),求base的exponent次方.不得使用库函数, 同时不需考虑大数问题. 思路分析: 要是你秒秒钟想 ...

  4. 基于jQuery UI的tabs选项卡美化

    很多朋友对JS望而生畏,但听很多朋友说jQuery很简单,因此开始使用jQuery,使用之后发现,只会写简单的功能,复杂的功能还是不太会写或者总是担心自己写的有性能问题,对前端人员来说只能通过不断学习 ...

  5. js 事件小结

    1,事件对象   e || window.event //ie   2, 取鼠标点击坐标 带有滚动条的   var top = document.documentElement.scrollTop | ...

  6. java.lang.ClassNotFoundException: org.apache.struts.action.ActionServlet

  7. Python计算机视觉3:模糊,平滑,去噪

    我是一名初学者,如果你发现文中有错误,请留言告诉我,谢谢 图像的模糊和平滑是同一个层面的意思,平滑的过程就是一个模糊的过程. 而图像的去噪可以通过图像的模糊.平滑来实现(图像去噪还有其他的方法) 那么 ...

  8. Stars(BIT树状数组)

    Stars Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submi ...

  9. VS_QT中配置qDebug输出

    在使用qt_create时可以使用qDebug进行调试输出.在VS中也可以使用.但需要配置.配置过程如下图所示: 一.首先右击工程名,选择最后一个选项“Properties” 二.然后选择Linker ...

  10. Delphi中的异常处理(10种异常来源、处理、精确处理)

    一.异常的来源 在Delphi应用程序中,下列的情况都比较有可能产生异常. 1.文件处理 2.内存分配 3.windows资源 4.运行时创建对象和窗体 5.硬件和操作系统冲突 6.网络问题 7.数据 ...