JavaScript小贴士
JavaScript中有很多值得学习和记住的小技巧和经典的代码片段,在这里做个小总结。
1. 链式方法
jQuery中 $dom.html().css().addClass() 这类的就叫链式方法,在对象方法中返回对象本身(this)就可以实现。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18function Chain(val){
this.val = val
this.print = function(){
console.log(this.val)
return this
}
this.setVal = function(val){
this.val = val
return this
}
}
let chain = new Chain('foo')
chain.print().setVal('bar').print()
//foo
//bar
2. 注意字符串连接
JavaScript中经常会有一些意想不到的类型转换,’+’是其中最常见的一种运算,它既可以做数字的加法,也可以做字符串连接,不注意使用可能会出现不想看到的结果。看下面的例子1
2
3
4
5
6let c1 = 12, c2 = 34, c3 = '56'
let result1 = c1 + c2 + c3 //'4656'
let result2 = ''.concat(c1, c2, c3) //'123456'
/*当然也可以这样,总是在前面加个''*/
let result3 = '' + c1 + c2 + c3 //'123456'
结果1中先做了加法,而非我们期望的字符串连接,使用后两种方法更安全一些。
如果是ES6,使用template string显然更方便。1
2
3let c1 = 12, c2 = 34, c3 = '56'
let result = `${c1}${c2}${c3}` //'123456'
3. 检查某个数是否为-1
要知道if(n)只有当n(数字类型)为0时才会判定为false,而对于~n只有当n为-1时会得到0,这在使用Array.prototype.indexOf()判断某一项是否在数组中时可以起到简化代码的作用,如下1
2
3
4const a = [1, 2, 3]
if (a.indexOf(4) !== -1) { // 或者a.indexOf(4) > -1
// 数组a中含有4
}
使用~我们可以像下面这样,代码会更简洁,只是可读性会稍稍降低1
2
3
4const a = [1, 2, 3]
if (~a.indexOf(4)) {
// 数组a中含有4
}
4. 双波浪运算(~~)
‘~~’表示执行两次’~’(按位非)运算,对于正数,它相当于Math.floor(),对于负数则相当于Math.ceil(),只是性能更高,写起来也更快,不过要注意它只能用于32位及以内的数值。1
2
3
4~12 //-13
~~12.34 //12
~~12.89 //12
~~-12.88 //-12
5. 使用!!总是返回Boolean类型
‘!!’表示执行两次非运算,经过类型转换之后总是会返回布尔值,这在有些场景下会很有用。1
2
3function isLogin (req) {
return !!req.session.user
}
6. Debouncing VS Throttling
Debouncing 和 Throttling 都是防止某个函数执行过于频繁,以提高性能。不同的是,Debouncing 表示把某一段时间内重复触发的事件归结到一次回调中执行,比如谷歌或百度的动态搜索框的文本输入事件,可以设置在键盘输入停止500ms才发起ajax请求;而 Throttling 表示的是在某一时间段内,限制某一函数只执行一次,比如一个动态加载的列表,它会在滚动到底部时加载新的列表项,然而用户的滚动事件的触发太频繁,我们可以限制它每500ms最多执行一次加载操作。更详细的说明可以参考这篇文章。
underscore和lodash都有对这两个高阶函数的实现和拓展,不考虑拓展的基本实现如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var debounce = function (fn, delay) {
var timer
return function () {
var args = arguments,
self = this
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function () {
clearTimeout(timer)
fn.apply(self, args)
timer = null
}, delay)
}
}
1 | var throttle = function (fn, interval) { |
7. 惰性载入函数
由于浏览器之间的差异,在开发前端的时候,一些特性嗅探操作总是不可避免的,比如一个比较通用的添加事件的函数1
2
3
4
5
6
7var addListener = function (el, type, handler) {
if (window.addEventListener) {
el.addEventListener(type, handler, false)
} else {
el.attachEvent(type, handler)
}
}
上面代码的缺点是每次调用addListener都用执行里面的if分支判断,这种情况是完全可以避免的1
2
3
4
5
6
7
8
9
10
11
12var addListener = function (el, type, handler) {
if (window.addEventListener) {
addListener = function (el, type, handler) {
el.addEventListener(type, handler, false)
}
} else {
addListener = function (el, type, handler) {
el.attachEvent(type, handler)
}
}
addListener(el, type, handler)
}
上面的代码直接在函数内部重写了addListener函数,这样只有首次调用的时候需要做if判断,往后的调用都不需要再做判断。
8. html文本编码
对于插入dom中的文本,我们需要对’<>&”‘等特殊字符进行转义,通常可以像下面这样子做:1
2
3
4
5
6
7
8
9
10
11
12
13
14function htmlEscape (test) {
return text.replace(/[<>&"]/g, function (match) {
switch (match) {
case '<':
return '<'
case '>':
return '>'
case '&':
return '&'
case '"':
return '"'
}
})
}
其实在浏览器环境中,还可以写成下面那样:1
2
3function htmlEscape (text) {
return document.createElement('p').appendChild(document.createTextNode(text)).parentNode.innerHTML
}
9. 简单的类型判断
例如判断一个变量是否为数组?只是使用instanceof的话在多个iframe的页面,由于不同iframe全局执行环境不同,存在多个不同的Array构造函数,不一定可行;在es5中可以直接使用静态方法Array.isArray(),而比较通用的方法则如下所示:1
2
3function isArray (value) {
return Object.prototype.toString.call(value) === '[object Array]'
}
当然除了数组,JavaScript中的基本类型和其他内置对象也可以用这种方法。1
2
3function isNumber (value) {
return Object.prototype.toString.call(value) === '[object Number]'
}
10. 检测是否为本地代码
1 | function isNative (constructor) { |
比如检测当前作用域下的Promise构造器是否为浏览器自带的Promise实现。1
isNative(Promise)