数组

javascript中并没有真正的数组,数组本质上也是对象

请看下面的例子:

js
1
2
3
4
5
var arr = [1,2,3,4,5,6]
console.log(arr.length) // 6
arr.abc = false // 给数组增加属性
console.log(arr) // [1, 2, 3, 4, 5, 6, abc: false]
console.log(arr.length) // 6

由运行的结果可以看出给数组添加了一个abc属性,尽管字面上的长度有所增加,但是数组的实际长度并没有改变!

js
1
typeof [] // 'object'

所以为了区分数组和对象我们应该可以采用以下的函数:

js
1
let isArray = value => !!value && typeof value === 'object' && value.constructor === Array

正则表达式

分组

观察以下匹配url的正则表达式:

js
1
2
3
4
5
6
7
8
9
10
11
12
13
'use strict'

let parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/

let url = 'http://www.ora.com:80/goods?q=1#fragment'

let result = parse_url.exec(url)

let names = ['url','schema','slash','host','port','path','query','hash']

for(let i = 0;i < names.length;i++){
console.log(names[i] + ':',result[i])
}

(?:([A-Za-z]+):)?这个因子匹配一个协议名,但仅当它之后跟随一个冒号(:)的时候才匹配(?:...)表示一个非捕获型分组(noncapturing group),通常用非捕获分组来代替少量不优美的捕获型分组是很好的方法,因为捕获会有性能上的缺失.后缀?表示这个分组是可选的.(...)表示一个捕获型分组(capturing group).一个捕获型分组将复制它所匹配的文本,并将其放入到result数组中.每个捕获型分组都将被指定一个编号,第一个捕获分组的编号是1,所以该分组所匹配的文本拷贝将出现在result[1]中;下一个因子(\/{0,3})是捕获分组2.

常用正则

匹配数字

js
1
pattern_number = /^-?\d+(?:\.\d*)?(?:e[+\-]?\d+)?$/i

正则表达式转义

\1指向分组1所捕获到的文本的一个引用,所以它能够再次匹配,例如我们用下面的正则表达式来搜索文本中重复的单词:

js
1
var doubled_words = /[A-Za-z\u00c0-\u1fff\u2800-\ufffd\-]+\s+\1/gi

正则表达式分组

捕获型

一个被包围在圆括号中的正则表达式选择.任何匹配这个分组的字符将被捕获.每个捕获分组都被指定了一个数字,第一个捕获(的是分组1,第二个捕获(的是分组2

非捕获型

有一个(?:前缀,仅做简单匹配,并不会捕获所匹配文本,会有微弱的性能优势,不会干扰捕获型分组的编号.

关于语言本身的一些探讨

位运算

由于javascript中所有数字都是浮点型,所以使用位运算会降低程序性能,因为需要将数字转化为整型后执行运算然后转化回去.

巧妙使用位运算可以创造很多的奇淫技巧。例如以下的转化为整数的代码:

js
1
2
3
4
5
let parseInt = num => num | 0
// 还可以这样
let parseInt = num => num >>> 0
// 甚至,我们可以这样
let parseInt = num => ~~num

以上的parseInt实现可以传入任意的数据类型,如果数据类型不匹配将会被转化为0,避免了原生的parseInt中的NaN的问题,但是局限在于只能处理32位的整数

生成随机字母数字字符串

js
1
2
3
4
5
function generateRandomAlphaNum(len) {
var rdmString = "";
for( ; rdmString.length < len; rdmString += Math.random().toString(36).substr(2));
return rdmString.substr(0, len);
}

toString(36)表示10个阿拉伯数字和26个小写英文字母

随机背景色

js
1
'#'+(0x1000000+(Math.random())*0xffffff).toString(16).substr(1,6)

实现 sleep

generator

js
1
2
3
4
5
6
7
8
9
10
11
function sleep(ms) {
return function (cb) {
setTimeout(cb, ms);
};
}

co(function *() {
var now = Date.now();
yield sleep(2000);
expect(Date.now() - now).to.not.be.below(2000);
})();

参见知乎

underscore.js中的_.after方法

该方法可以根据传入的times参数的不同生成需要调用多次才执行的实际函数的函数,这是偏函数的典型应用

js
1
2
3
4
5
6
7
8
9
10
11
var _ = require('underscore')
var count = 5;
var fiveCountCallback = _.after(count,function(){
console.log('after 5 count and execute this function')
})
var timer = 0
setInterval(function(){
console.log(timer++)
fiveCountCallback() // 每1s执行一次该函数,但是真正执行该函数却是在5s后
}, 1000)
console.log('this will print first')

其内部实现如下:

js
1
2
3
4
5
6
7
_.after = function(times, func) {
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
};