1.当代码块被try { ... }
包裹的时候,就表示这部分代码执行过程中可能会发生错误,一旦发生错误,就不再继续执行后续代码,转而跳到catch
块。catch (e) { ... }
包裹的代码就是错误处理代码,变量e
表示捕获到的错误。
最后,无论有没有错误,finally
一定会被执行。
catch
和finally
可以不必都出现
throw
语句主动抛出一个错误,让执行流程直接跳转到catch
块。
当我们用catch捕获错误时,一定要编写错误处理语句,哪怕仅仅把错误打印console.log
出来,也不要什么也不干。
如果在一个函数内部发生了错误,它自身没有捕获,错误就会被抛到外层调用函数,如果外层函数也没有捕获,该错误会一直沿着函数调用链向上抛出,直到被JavaScript引擎捕获,代码终止执行。
2.
function printTime() {
throw new Error();
}
try {
setTimeout(printTime, 1000);
console.log('done');
} catch (e) {
alert('error');
}
这段代码无法弹出error,原因就在于调用setTimeout()
函数时,传入的printTime
函数并未立刻执行!紧接着,JavaScript引擎会继续执行console.log('done');
语句,而此时并没有错误发生。直到1秒钟后,执行printTime
函数时才发生错误,但此时除了在printTime
函数内部捕获错误外,外层代码并无法捕获。(全部函数执行完毕,再处理1000ms后的函数)
所以,涉及到异步代码,无法在调用时捕获,原因就是在捕获的当时,回调函数并未执行。
类似的,当我们处理一个事件时,在绑定事件的代码处,无法捕获事件处理函数的错误。
3.underscore.js
实现map()
操作(可以作用于Object):
_.map([1, 2, 3], (x) => x * x); // [1, 4, 9]
_.map({ a: 1, b: 2, c: 3 }, (v, k) => k + '=' + v); // ['a=1', 'b=2', 'c=3']
4.every / some
'use strict';
// 所有元素都大于0?
_.every([1, 4, 7, -3, -9], (x) => x > 0); // false
// 至少一个元素大于0?
_.some([1, 4, 7, -3, -9], (x) => x > 0); // true
5.max / min
'use strict';
var arr = [3, 5, 7, 9];
_.max(arr); // 9
_.min(arr); // 3
// 空集合会返回-Infinity和Infinity,所以要先判断集合不为空:
_.max([])
-Infinity
_.min([])
Infinity
如果集合是Object,max()
和min()
只作用于value,忽略掉key:
_.max({ a: 1, b: 2, c: 3 }); // 3
6.groupBy()
把集合的元素按照key归类,key由传入的函数返回:
'use strict';
var scores = [20, 81, 75, 40, 91, 59, 77, 66, 72, 88, 99];
var groups = _.groupBy(scores, function (x) {
if (x < 60) {
return 'C';
} else if (x < 80) {
return 'B';
} else {
return 'A';
}
});
// 结果:
// {
// A: [81, 91, 88, 99],
// B: [75, 77, 66, 72],
// C: [20, 40, 59]
// }
7.shuffle / sample
shuffle()
用洗牌算法随机打乱一个集合:
'use strict';
// 注意每次结果都不一样:
_.shuffle([1, 2, 3, 4, 5, 6]); // [3, 5, 4, 6, 2, 1]
sample()
随机选择一个或多个元素:
'use strict';
// 注意每次结果都不一样:
// 随机选1个:
_.sample([1, 2, 3, 4, 5, 6]); // 2
// 随机选3个:
_.sample([1, 2, 3, 4, 5, 6], 3); // [6, 1, 4]
8.flatten()
接收一个Array
,无论这个Array
里面嵌套了多少个Array
,flatten()
最后都把它们变成一个一维数组:
_.flatten([1, [2], [3, [[4], [5]]]]); // [1, 2, 3, 4, 5]
9.first / last
var arr = [2, 4, 6, 8];
_.first(arr); // 2
_.last(arr); // 8
10.zip / unzip
var names = ['Adam', 'Lisa', 'Bart'];
var scores = [85, 92, 59];
_.zip(names, scores);
// [['Adam', 85], ['Lisa', 92], ['Bart', 59]]
var namesAndScores = [['Adam', 85], ['Lisa', 92], ['Bart', 59]];
_.unzip(namesAndScores);
// [['Adam', 'Lisa', 'Bart'], [85, 92, 59]]
11.object
var names = ['Adam', 'Lisa', 'Bart'];
var scores = [85, 92, 59];
_.object(names, scores);
// {Adam: 85, Lisa: 92, Bart: 59}
12.range
// 从0开始小于10: _.range(10); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // 从1开始小于11: _.range(1, 11); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] // 从0开始小于30,步长5: _.range(0, 30, 5); // [0, 5, 10, 15, 20, 25] // 从0开始大于-10,步长-1: _.range(0, -10, -1); // [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
13.bind. 如果你想用log()
取代console.log(),可以这样写:
var log = console.log;
// 调用call并传入console对象作为this:
log.call(console, 'Hello, world!')
// 输出Hello, world!
如果用bind,把console
对象直接绑定在log()
的this
指针上。可以这样写:
var log = _.bind(console.log, console);
log('Hello, world!');
// 输出Hello, world!
14.partial()
为一个函数创建偏函数
假设我们经常计算2y,每次都写Math.pow(2, y)
就比较麻烦,如果创建一个新的函数能直接这样写pow2N(y)
就好了,这个新函数pow2N(y)
就是根据Math.pow(x, y)
创建出来的偏函数,它固定住了原函数的第一个参数(始终为2):
var pow2N = _.partial(Math.pow, 2);
pow2N(3); // 8
pow2N(5); // 32
pow2N(10); // 1024
如果我们不想固定第一个参数,想固定第二个参数,比如,希望创建一个偏函数cube(x)
,计算x3,可以用_
作占位符,固定住第二个参数:
var cube = _.partial(Math.pow, _, 3);
cube(3); // 27
cube(5); // 125
cube(10); // 1000
15.memoize能把结果缓存下来,再次调用不需要计算:
var factorial = _.memoize(function(n) {
console.log('start calculate ' + n + '!...');
var s = 1, i = n;
while (i > 1) {
s = s * i;
i --;
}
console.log(n + '! = ' + s);
return s;
});
// 第一次调用:
factorial(10); // 3628800
// 注意控制台输出:
// start calculate 10!...
// 10! = 3628800
// 第二次调用:
factorial(10); // 3628800
// 控制台没有输出 但是,当你计算factorial(9)
的时候,仍然会重新计算
令每一次都保存:
var factorial = _.memoize(function(n) {
console.log('start calculate ' + n + '!...');
if (n < 2) {
return 1;
}
return n * factorial(n - 1);
});
factorial(10); // 3628800
// 输出结果说明factorial(1)~factorial(10)都已经缓存了:
// start calculate 10!...
// start calculate 9!...
// start calculate 8!...
// start calculate 7!...
// start calculate 6!...
// start calculate 5!...
// start calculate 4!...
// start calculate 3!...
// start calculate 2!...
// start calculate 1!...
factorial(9); // 362880
// console无输出
16.once()
保证某个函数执行且仅执行一次
17.delay()
可以让一个函数延迟执行
var log = _.bind(console.log, console);
_.delay(log, 2000, 'Hello,', 'world!');
// 2秒后打印'Hello, world!':
18.keys / allKeys
keys()
可以非常方便地返回一个object自身所有的key,但不包含从原型链继承下来的:
function Student(name, age) {
this.name = name;
this.age = age;
}
var xiaoming = new Student('小明', 20);
_.keys(xiaoming); // ['name', 'age']
allKeys()
除了object自身的key,还包含从原型链继承下来的
19.values
和keys()
类似,values()
返回object自身但不包含原型链继承的所有值
20.mapObject()
就是针对object的map版本:
var obj = { a: 1, b: 2, c: 3 };
// 注意传入的函数签名,value在前,key在后:
_.mapObject(obj, (v, k) => 100 + v); // { a: 101, b: 102, c: 103 }
21.invert()
把object的每个key-value来个交换,key变成value,value变成key:
var obj = {
Adam: 90,
Lisa: 85,
Bart: 59
};
_.invert(obj); // { '59': 'Bart', '85': 'Lisa', '90': 'Adam' }
22.extend()
把多个object的key-value合并到第一个object并返回:
var a = {name: 'Bob', age: 20};
_.extend(a, {age: 15}, {age: 88, city: 'Beijing'}); // {name: 'Bob', age: 88, city: 'Beijing'}
// 变量a的内容也改变了:
a; // {name: 'Bob', age: 88, city: 'Beijing'}
注意:如果有相同的key,后面的object的value将覆盖前面的object的value。
extendOwn()
和extend()
类似,但获取属性时忽略从原型链继承下来的属性。
23.clone()
方法会把原有对象的所有属性都复制到新的对象中。
注意,clone()
是“浅复制”。所谓“浅复制”就是说,两个对象相同的key所引用的value其实是同一对象:
source.skills === copied.skills; // true
修改source.skills
会影响copied.skills
。
24.isEqual()
对两个object进行深度比较,如果内容完全相同,则返回true
:
var o1 = { name: 'Bob', skills: { Java: 90, JavaScript: 99 }};
var o2 = { name: 'Bob', skills: { JavaScript: 99, Java: 90 }};
o1 === o2; // false
_.isEqual(o1, o2); // true
var o1 = ['Bob', { skills: ['Java', 'JavaScript'] }];
var o2 = ['Bob', { skills: ['Java', 'JavaScript'] }];
o1 === o2; // false
_.isEqual(o1, o2); // true
24.
_.filter(_.map([1, 4, 9, 16, 25], Math.sqrt), x => x % 2 === 1);
// [1, 3, 5]
等效于下面的链式:
_.chain([1, 4, 9, 16, 25])
.map(Math.sqrt)
.filter(x => x % 2 === 1)
.value();
// [1, 3, 5]