高级技巧

高级函数

安全的类型检测

使用typeof检测数组函数等返回都是object, 无法获取正确类型, 所以可以使用下面的方法获取正确的类型信息

1
2
var value = []
Object.prototype.toString.call(value)//"[object Array]"

安全构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Polygon(sides) {
if (this instanceof Polygon) {
this.sides = sides;
this.getArea = function() {
return 0;
}
} else {
return new Polygon(sides);
}
}

function Rectangle(width, height) {
Polygon.call(this, 2);
this.width = width;
this.height = height;
this.getArea = function() {
return this.width * this.height;
}
}

Rectangle.prototype = new Polygon();
var rect = new Rectangle(5, 10);
rect.sides;//2

惰性载入函数

有时候函数的具体逻辑, 会根据环境不同而不同, 因此我们可以使用惰性载入函数, 在页面加载的时候就能自运行获取正确的函数, 而不用再调用时在去判断生成

1
2
3
4
5
6
7
8
9
10
11
var createXHR = (function() {
if (typeof XMLHttpRequest != "undefined") {
return function() {
return new XMLHttpRequest();
}
} else {
return function() {
//...
}
}
})()

函数绑定

this指向被改变

1
2
3
4
5
6
7
8
var handler = {
message: 'event handle',
handleClick: function(event) {
console.log(this.message);
}
}

EventUtil.addHandler(btn, "click", handler.handleClick); // undefined

使用bind纠正this

1
2
3
4
5
6
7
8
9
10
11
12
var handler = {
message: 'event handle',
handleClick: function(event) {
console.log(this.message);
}
}
function bind(fn, context) {
return function() {
fn.apply(context, arguments)
}
}
EventUtil.addHandler(btn, "click", bind(handler.handleClick, handler)); // event handle

函数柯里化

1
2
3
4
5
6
7
8
9
10
function add(num1, num2) {
return num1 + num2;
}

function curriedAdd(num2) {
return add(5, num2);
}

console.log(add((2, 3)))//5
console.log(curriedAdd(3));//8

柯里化

1
2
3
4
5
6
7
8
9
10
function curry(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
fn.apply(null, finalArgs);
}
}
var curriedAdd = curry(add, 5);
console.log(curriedAdd(3));//8

防篡改对象

不可扩展对象

Object.preventExtensions可以使对象不能再扩展属性, 但可以对已有属性进行修改删除

1
2
3
4
5
6
7
var obj = {a: 'a', b: 'b', c:'c'};
Object.isExtensible(obj);//true

Object.preventExtensions(obj);
Object.isExtensible(obj);//false
obj.d = 'd';
consolelog(obj);//{a: 'a', b: 'b', c:'c'}

密封对象

seal的对象, [[Configurable]]被设置为false, 因此不能删除属性和方法, 也不能新增属性和方法, 但可以修改属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
var person = {name: 'zhichen'};
Object.isExtensible(person);//true
Object.isSealed(person);//false

Object.seal(person);
Object.isExtensible(person);//false
Object.isSealed(person);//true

person.age = 18;
person.name = 'lc'
delete person.name;
console.log(person);//{name: 'lc'}

冻结的对象

freeze的对象, 不能进行扩展, [[writable]]特性会被设置为false, 如果定义[[Set]]函数, 访问器属性仍可以写的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var person = {name: 'zhichen'};
Object.isExtensible(person);//true
Object.isSealed(person);//false
Object.isFrozen(person);//false

Object.freeze(person);
Object.isExtensible(person);//false
Object.isSealed(person);//true
Object.isFrozen(person);//true


person.age = 18;
person.name = 'lc'
delete person.name;
console.log(person);//{name: 'zhichen'}

高级定时器

setTimeoutsetInterval 加入设置时间为150ms, 不表示在150ms后立即执行, 而是在150ms后被加入到执行队列, 如果队列没有其他任务, 就立即执行, 如果有其他任务, 等其他任务执行完毕后才能执行

重复的定时器

setInterval为了避免因为任务等待导致在同一时间重复执行, 因此setInterval被加入进程队列前会先检测是否有次任务, 如果有此任务, 就不会再被添加到进程队列中, 所以有些间隔会被跳过

Yielding Processes

如果要操作一个数组里的数据, 会导致阻塞, 如果可以改成异步的话, 可以使用如下方式

1
2
3
4
5
6
7
8
9
10
11
function chunk(array, process, context) {
setTimeout(function() {
var item = array.shift();
process.call(context, item);

//如果还有数据 就再调自身循环
if (array.length > 0) {
setTimeout(arguments.callee, 100)
}
}, 100)
}