分类目录归档:JavaScript

express请求流中的错误捕捉处理

nodejs中的错误处理还是比较麻烦的,尤其遇到异步回调之类,多层嵌套,简单的try…catch无法处理。

目前在用expressjs,拿此举例说说怎样实践。
第一种,从一介布衣那儿看到的方法,可行:

const domain = require('domain');
app.use((req, res, next) => {
  const reqDomain = domain.create();
  // next抛出的异常在这里被捕获,触发此事件
  reqDomain.on('error', e => {
    // ... 这里统一处理错误,比如渲染或跳转到404,500页面
  });

  return reqDomain.run(next);
});

通过在route定义之前添加如上的中间件,可以捕捉到大部分的错误。

第二种,上面说到是大部分的错误,因为我碰到了一个没成功捕捉到的,就是使用swig模板渲染时的变量报错。

// 设置模板引擎
app.engine('swig', swig.renderFile);
app.set('view engine', 'swig');
app.set('views', `${__dirname}/views`);
app.set('viewExt', '.swig');
// 在某处渲染输出: 
res.render('template/filepath');
// 如果在filepath.swig文件中有变量报错,则第一种方法不能成功捕捉

这时在stackoverflow找到一个方法,

// 依然按第一种方法写错误捕捉
// 然后在route之后再写个中间件

// 最后的错误处理,比如发生在res.render中的错误
// 由于已经设置了响应,因此只能发送状态码,或跳转
// next不能去除,否则无法获取到错误
app.use((err, req, res, next) => {
  // res.sendStatus(500);
  log.error(err);
  // res.redirect(301, '/'); // 跳转到首页
  next();
});

ok,经过验证,上述方法已经能够处理目前所有遇到的问题。

第三种,为什么会有这一种方法呢,纯属个人思维发散,由于是express内部处理了模板引擎,导致res.render方法不能正常抛出错误被外围捕捉,那么我直接用swig方法渲染文件,并把渲染后的结果通过res.send方法响应是不是可以呢?

// 之前是这样渲染的
res.render('template/filepath');
// 改为
res.send(swig.renderFile('/...这里应该是物理绝对路径.../template/filepath.swig'));

经过上面改造,发现只用第一种方法也可以正常捕捉到错误,说明推论正确。

之所以在第二种还能解决的情况下还在寻找其他方案,是因为第二种的最终错误处理只能响应状态码,而不能再渲染页面了。终究不够完美,方法不够优雅。

像第三种方式,完全可以不必设置express的模板引擎了,用res.send + 模板自身的渲染方法即可,封装为一个方法,方便调用。

js浮点数的四则精确计算

js的浮点数类型值在参与四则运算时,有时候会产生不正确数据,这是编程语言的通病,因为计算机是二进制的。
比如一个简单的加法:268.34 + 0.83,js得出的结果是269.16999999999996。类似的加减乘除有很多。

因此需要一个精确计算的方法,来保证精确度。Github上有几个Math库,一般项目使用未免太大。这里贴出一个从http://www.cnblogs.com/junjieok/p/3306155.html而来的轻量方法,已足够使用。
代码如下:
Continue reading js浮点数的四则精确计算

IE9的输入框oninput事件不支持退格删除

IE9中,input输入框绑定的oninput事件,无法监听到键盘的backspace delete和右键菜单的剪切、撤销、删除对内容的改变。
虽然onkeyup可以解决键盘问题,但对右键还是无法解决。所以,有种解决方法如下:

<input id="myInput" type="text">

<script>
  // Get the input and remember its last value:
  var myInput = document.getElementById("myInput"), 
      lastValue = myInput.value;
  
  // An oninput function to call:
  var onInput = function() {
    if (lastValue !== myInput.value) { // selectionchange fires more often than needed
      lastValue = myInput.value;
      console.log("New value: " + lastValue);
    }
  };
  
  // Called by focus/blur events:
  var onFocusChange = function(event) {
    if (event.type === "focus") {
      document.addEventListener("selectionchange", onInput, false);
    } else {
      document.removeEventListener("selectionchange", onInput, false);
    }
  };
  
  // Add events to listen for input in IE9:
  myInput.addEventListener("input", onInput, false);
  myInput.addEventListener("focus", onFocusChange, false);
  myInput.addEventListener("blur", onFocusChange, false);
</script>

参考:http://www.matts411.com/post/internet-explorer-9-oninput/

js getter setter和defineProperty兼容情况

测试数据源于: http://robertnyman.com/javascript/javascript-getters-setters.html

兼容ie9+及其他标准浏览器的写法有二:

1、

var lost = {
	loc : "Island",
	get location () {
		return this.loc;
	},
	set location(val) {
		this.loc = val;
	}
};
lost.location = "Another island";

2、

var lost = {
	loc : "Island"
};	
Object.defineProperty(lost, "location", {
	get : function () {
		return this.loc;
	},
	set : function (val) {
		this.loc = val;
	}
});
lost.location = "Another island";

ToFishes系列jQuery插件 – Dropdown下拉列表

之前有发过一套插件jcuteForm中包含一个下拉列表的实现,相比之前的作品,此次的实现更加简洁和优雅(自认的)。

而以前的jCuteForm的demo页因为使用的Google jscdn 引入的jQuery挂了已经不能正常在线查看,不过还是可以下载的。

而这次的提升还是很不错的,需要的请自行:查看DEMO

源码里面有个注释,去掉就可以实现一个页面多个dropdown的显示互斥(点一个下拉,则其他打开的下拉就恢复原状,源码内的注释没解释清楚,恐误解),因为莫名的想法我给注释掉了,需要的请自行取消。

jQuery表单方法糖serializeArray

jQuery.serializeArray()和jQuery.param()两个表单相关的方法不知道用的人多不多,这两个是获取表单参数的最方便方法。

serializeArray()返回的是一个数组,数组由简单的“名称-值对”对象组成,可以直接当做jQuery.ajax各api方法的data参数穿进去。

例如

var params = $('form').serializeArray();// 返回类似: [{name: 'title', value: '标题'},  {name: 'content', value: '内容值'}]
$.post(url, params, function(){});

不过,在实际项目中可能有些变故,比如说我们项目后端要求 对同名参数的多个值是用逗号间隔合并一起的字符串,像checkbox类型,获取多个input[type=checkbox name=”hobby”], 使用serailizeArray会返回这样的值:
params = [{ name: “hobby”, value: “one” }, { name: “hobby”, value: “two” }, { name: “hobby”, value: “three” }]

直接用于ajax方法,传参会是这样的: hobby=one&hobby=two&hobby=three
而我们后端要求应该是这样传: hobby=one,two,three

因此直接使用serializeArray不能满足我们的需求,总不能放弃这么好的“方法糖”不吃吧?解决方法简单,我拿serializeArray封装一下,做了另外一个插件,满足了自己的需求,也没增加多少复杂度,具体代码如下:

// @tofishes
// var params = $('form').paramMap();  就可以获得整个表单的所有参数
$.fn.paramMap = function (opts) {
    opts = $.extend({
        'separator': ',' // 同名参数的分隔符,多用于checkbox的值
    })

    var params = this.serializeArray()
    ,   paramMap = {}

    ,   i = 0
    ,   l = params.length
    ,   param;

    for (; i < l; i++) {
        param = params[i];

        if (paramMap[param.name]) {
            paramMap[param.name] += opts.separator + param.value;
        } else {
            paramMap[param.name] = param.value;
        }
    };

    return paramMap;
};

善用方法,可以让自己的工作变简单很多。

HTML5表单验证有效状态对象

HTML5增加对表单内容类型验证的支持,而通过js获取验证有效状态的API就是ValidityState对象。

获取该对象方法:

var input = document.createElement('input');
console.info(input.validity)

浏览器支持度见: http://caniuse.com/#feat=form-validation

那么由此我们就可以写出更简单的验证框架。该对象拥有的属性如下:
Continue reading HTML5表单验证有效状态对象

JavaScript实现一个单程双向工作流

初衷源于项目中上一步,下一步这种交互需求,而每一步有可能或全部是异步操作,而且会有数据临时存储与获取的需要。

考虑了一些方案,受requirejs,seajs实现的启发,最终实现了这个单程双向工作流对象,而其源码看似相当简单。

那么怎么使用呢?当var flow = new Flow()实例化一个工作流以后,可以调用如下API实现:

1、流程添加API 3个:

// 参数action是一个function回调,比如
// function(i, data) {}
// i是当前步骤的索引,从0开始
// data是所有步骤存储的数据数组,因为本工作流提供数据存储,也自然提供获取
// data[i]即 第i步的数据
// action中的this指向为flow实例对象
flow.start(action); // start其实内部实现为调用next方法,因此start等同于next,其存在价值大概是为了语义
flow.next(action);  // next,添加下一步action
flow.done(action);  // done,完成所有步骤,该方法也是启动整个工作流的必须方法,也必须是最后调用。

Continue reading JavaScript实现一个单程双向工作流