22.1 高级函数
22.1.1 安全的类型检测
typeof
,instanceof
并非完全可靠。解决办法,利用 Object.prototype.toString.call(value)
22.1.2 作用域安全的构造函数
function Person(name,age,job){
"use strict";
if(this instanceof Person){ //防止绑定到错误的作用域中
this.name = name;
this.age = age;
this.job = job;
} else {
return new Person(name,age,job);
}
}
22.1.3 惰性载入函数
问题
因为浏览器之间行为的差异,多数JavaScript代码包含了大量的if语句,将执行引导到正确的代码中。 每次调用时,都要多浏览器所支持的能力仔细检查,但是如果支持则一直支持,没必要每次都进行能力检查。
解决方案:惰性载入
- 第一种:在第一次调用的过程中,将函数覆盖为另外一个合适的方式执行的函数,这样任何对原函数的调用都不要再进过执行的分支了。
function createXHR(){
if(typeof XMLhttpRequest != "undefined"){
createXHR = function(){
//codeblocks
}
}else if(typeof ActiveXObject != "undefined"){
createXHR = function(){
//codeblocks
}
}else{
createXHR = function(){
throw new Error("no XHR object avaliable.");
}
}
return createXHR();
}
- 第二种:在声明函数时就指定适当的函数,但是在代码首次加载时会损失一点性能。
var createXHR = (function(){
if(typeof XMLhttpRequest != "undefined"){
return function(){
//codeblocks
}
}else if(typeof ActiveXObject != "undefined"){
return function(){
//codeblocks
}
}else{
return function(){
throw new Error("no XHR object avaliable.");
}
}
})()
22.1.4 函数绑定
var handler = {
message: "Event handled",
handleClick: function(event){
alert(this.message);
}
};
var btn = document.getElementById("my-btn");
btn.addEventListener("click",handler.handleClick,false);
输出 undefined
var handler = {
message: "Event handled",
handleClick: function(event){
alert(this.message);
}
};
var btn = document.getElementById("my-btn");
btn.addEventListener("click",bind(handler.handleClick,handler),false);
function bind(fn,context){ //bind函数绑定作用域
return function () {
return fn.apply(context,arguments);
}
}
22.1.5 函数柯里化
用于创建已经设置好了一个或者多个参数的函数。
function curry(fn){
"use strict";
var args = Array.prototype.slice.call(arguments,1);
return function () {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(null,finalArgs);
};
}
22.2 防篡改对象
22.2.1 不可扩展对象
Object.preventExtensions(person); // person这个对象禁止扩展
Object.isExtensible(person); //判断person这个对象是否可以扩展
22.2.2 密封的对象
Object.seal(person); //密封对象,不可扩展且不能删除已有属性和方法
Object.isSealed(person);
22.2.3 冻结的对象
Object.freeze();//最为严格,[[writable]]特性为false
Object.isFrozen();
22.3 高级定时器
除了主JavaScript线程外,还有一个需要在进程下一次空闲时执行的代码队列。在JavaScript中没有任何代码是立刻执行的, 当进程一旦空闲则尽快执行。
22.3.1 重复的定时器
setInterval()
的问题在于,定时器代码可能在代码再次添加到队列之前还没有完成执行,结果导致定时器代码运行好几次,而且之间没有停顿。 幸好,JavaScript引擎足够聪明,能够避免这个问题。当使用setInterval()
时,仅当没有该定时器的任何其他代码实例时,才将定时器代码添加到队列中。 这确保了定时器代码加入到队列中的最小时间间隔为指定间隔。
因此这样的重复定时器有两个问题:
- 某些间隔会被跳过
- 多个定时器的代码执行之间的间隔可能会比预期小。
为了解决以上问题,可以采用链式setTimeout()
调用
setTimeout(function () {
//codeblocks
setTimeout(arguments.callee,interval);
}, interval)
以上方式的好处在于,在前一个定时器代码执行完之前,不会向队列插入新的定时器代码,确保不会有任何 缺失的间隔。而且,可以保证在下一次定时器代码执行之前,至少等待指定的间隔,避免了连续的运行。
22.3.2 yieldng processes
长时间运行脚本的限制,如果代码运行超过特定的时间或者特定的语句数量就不让它继续执行。 主要产生原因有:过长的,过深嵌套的函数调用或者是进行大量处理的循环。
数组分块
function chunk(array,process,context){ //如果想原array不被修改,可以使用array.concat()
setTimeout(function () {
var item = array.shift();
process.call(context,item);
if(array.length > 0){
setTimeout(arguments.callee,100)
}
})
}
22.3.3 函数节流
浅谈javascript的函数节流 基本思想:某些代码不可以在没有间断的情况连续重复执行。
function throttle(method,context){
"use strict";
clearTimeout(method.tId);
method.tId = setTimeout(function () {
method.call(context);
},100);
}
22.4 自定义事件
事件是一种叫做观察者的设计模式。观察者模式主要由两类对象组成:主体和观察者。 主体负责发布事件,同时观察者通过订阅这些事件来观察主体。 自定义事件背后的概念是创建一个管理事件的对象,让其他对象监听那些事件。
function EventTarget(){
"use strict";
this.handlers = {};
}
EventTarget.prototype = {
constructor: EventTarget,
addHandler: function(type,handler){
"use strict";
if (typeof this.handlers[type] == "undefined"){
this.handlers[type] = [];
}
this.handlers[type].push(handler);
},
fire: function(event){
"use strict";
if (!event.target){
event.target = this;
}
if (this.handlers[event.type] instanceof Array){
var handlers = this.handlers[event.type];
for (var i= 0,len=handlers.length;i < len; i++){
handlers[i](event);
}
}
},
removeHandler: function(type, handler){
"use strict";
if (this.handlers[type] instanceof Array){
var handlers = this.handlers[type];
for (var i= 0,len=handlers.length;i<len; i++){
if (handlers[i] === handler){
break;
}
}
handlers.splice(i,1);
}
}
};
22.5 拖放
var DragDrop = function () {
"use strict";
var dragging = null,
diffX = 0,
diffY = 0;
function handleEvent(event){
var target = event.target;
switch(event.type){
case "mousedown":
if (target.className.indexOf("draggable") > -1){
dragging = target;
diffX = event.clientX - target.offsetLeft;
diffY = event.clientY - target.offsetTop;
}
break;
case "mousemove":
if (dragging != null){
dragging.style.left = (event.clientX - diffX) + "px";
dragging.style.top = (event.clientY - diffY) + "px";
}
break;
case "mouseup":
dragging = null;
break;
}
}
return {
enable: function(){
document.addEventListener("mousedown",handleEvent);
document.addEventListener("mousemove",handleEvent);
document.addEventListener("mouseup",handleEvent);
},
diable: function () {
document.addEventListener("mousedown",handleEvent);
document.addEventListener("mousemove",handleEvent);
document.addEventListener("mouseup",handleEvent);
}
}
}();
DragDrop.enable();