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();