JS高級程序設計之高級技巧

1. 安全的類型檢測設計模式

// 安全類型檢測
/* 檢測是否是原生的函數 */
function isFunction(value) {
    return Object.prototype.toString.call(value) == "[object Function]";
}

 

2.做用域安全的構造函數數組

// 做用域安全的構造函數
function Person(name, age, job) {
    if (this instanceof Person) {
        this.name = name;
        this.age = age;
        this.job = job;
    } else {
        return new Person(name, age, job);
    }
}
var person1 = Person("sundily", 22, 'Software Engineer');

 

3.惰性載入函數:表示函數執行的分支僅會發生一次 (主要用在瀏覽器的兼容上 作一次判斷)兩種實現方法:
  ● 函數被調用時再處理函數。在第一次調用的過程當中,該函數就會被從新覆蓋爲另外一個按合適方法執行的函數
  ● 在聲明函數時就指定適當的函數。這樣第一次調用函數就不會損失性能了,而在代碼首次加載時會損失一點性能瀏覽器

/* 惰性載入函數  表示函數執行的分支僅會發生一次 */
// 原例
function createXHR() {
    if (typeof XMLHttpRequest != "undefined") {
        return new XMLHttpRequest();
    } else if (typeof ActiveXObject != "undefined") {
        if (typeof arguments.callee.activeXString != "string") {
            var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp"];
            for (var index = 0; index < versions.length; index++) {
                try {
                    new ActiveXObject(versions[index]);
                    arguments.callee.activeXString = versions[index];
                    break;
                } catch (error) {

                }

            }
        }
        return new ActiveXObject(arguments.callee.activeXString);
    } else {
        throw new Error("NO XHR object available");
    }
}

// 使用惰性函數以後
function createXHR() {
    if (typeof XMLHttpRequest != "undefined") {
        createXHR = function () {
            return new XMLHttpRequest();
        }
    } else if (typeof ActiveXObject != "undefined") {
        createXHR = function () {
            if (typeof arguments.callee.activeXString != "string") {
                var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp"];
                for (var index = 0; index < versions.length; index++) {
                    try {
                        new ActiveXObject(versions[index]);
                        arguments.callee.activeXString = versions[index];
                        break;
                    } catch (error) {
                        // skip
                    }
                }
            }
            return new ActiveXObject(arguments.callee.activeXString);
        };
    } else {
        createXHR = function () {
            throw new Error("no xhr object available");
        };
    }
    return createXHR();
}

// 第二種惰性函數    匿名的當即執行函數   返回匿名函數
var createXHR = (function () {
    if (typeof XMLHttpRequest != 'undefined') {
        return function () {
            return new XMLHttpRequest();
        };
    } else if (typeof ActiveXObject != "undefined") {
        return function () {
            if (typeof arguments.callee.activeXString != "string") {
                var versions = ["MSXML2.XMLHttp", "MSXML2.XMLHttp.6.0"];
                for (var index = 0; index < versions.length; index++) {
                    try {
                        new ActiveXObject(versions[index]);
                        arguments.callee.activeXString = versions[index];
                        break;
                    } catch (error) {
                        // skip
                    }
                }
            }
            return new ActiveXObject(arguments.callee.activeXString);
        };
    } else {
        return function () {
            throw new Error("no xhr object available");
        };
    }
})();

 

4 函數綁定——一個將函數綁定到指定環境的函數 bind()安全

/* 函數綁定 */
var handler = {
message: "event handler",
handlerClick: function () {
alert(this.message);
}
};
var btn = document.getElementById("mybtn");
btn.addEventListener("click", handler.handlerClick.bind(handler), false);

只要是將某個函數指針以值的形式進行傳遞,同時該函數必須在特定環境中執行,就應該用函數綁定
主要應用與事件處理程序以及setInterval() setTimeout() 比普通函數須要更多的內存,因此最好必要時用閉包

5 .函數柯里化(function currying)——用於建立已經設置好了一個或者多個參數的問題
函數柯里化基本方法和函數綁定時同樣的,使用一個閉包返回一個函數
動態建立柯里化:調用另外一個函數併爲他傳入要柯里化的函數和必要的參數app

/* 函數柯里化:調用另外一個函數併爲他傳入要柯里化的函數和必要的參數
主要工做 就是將返回函數的參數進行排序
concat() 方法用於鏈接兩個或多個數組。該方法不會改變現有的數組,而僅僅會返回被鏈接數組的一個副本。
*/
function curry(fn) {
var args = Array.prototype.slice.call(arguments, 1);//arguments中第一個參數時fn 因此要從1開始slice()
return function () {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return fn.apply(null, finalArgs);//調用函數fn本身自己,apply的第一個參數就是null,finalArgs就是傳入的參數
}
}

es5中bind()方法也實現了柯里化
// bind()的柯里化
var handler={
message:"event handler",
handlerClick:function(name,event){
alert(this.message+":"+name+":"+event.type);
};
}
var btn=document.getElementById("mybtn");
btn.addEventListener('click',handler.handlerClick.bind(handler,"mybtn"));

 


6. 防篡改對象
  1. 不可擴展的對象dom

var person={name:"sundjly"};
Object.preventExtensions(person);//不能再給person添加屬性和方法 但仍然能夠修改 刪除已有的成員
// 使用Object.isExtensible()方法肯定對象是否能夠擴展
Object.isExtensible(person);//false

    1. 1密封的對象(sealed object):密封對象不可擴展,並且已有屬性方法不能刪除,函數

Object.seal(person);
Object.isSealed(person);//判斷對象是否被密封

    1.2凍結對象 Object.freeze(person);//沒法對對象作出操做性能

7. 高級定時器:
js是單線程的,由代碼隊列前後順序執行。js中沒有任何代碼是當即執行的,但一旦進程空閒則儘快執行。
setInterval:這種重複定時器的規則有兩個問題:
    1. 某些間隔會被跳過
    2. 多個定時器的代碼執行之間的間隔可能比預期小this

/* 避免setInterval()重複定時器的兩個缺點,可使用鏈式setTimeout()調用
這個模式鏈式調用setTimeout()函數,每次函數執行的時候都會建立一個新的定時器,第二個setTimeout調用arguments.callee
獲取當前函數的引用
優勢:在前一個定時器代碼執行以前,不會向隊列插入新的定時器代碼,確保不會有任何缺失的間隔,
並且它能夠保證在下次定時器代碼執行以前,至少等待間隔時間,避免了連續運行。
*/
setTimeout(function() {
// 處理函數
setTimeout(arguments.callee, interval);
}, interval);

具體的例子:

setTimeout(function () {
    var div = document.querySelector("#div");
    left = parseInt(div.style.left) + 5;
    div.style.left = left + "px";
    if (left < 200) {
        setTimeout(arguments.callee, 50);
    }
}, 50);

 

8.Yielding Processes
  1. 腳本長時間運行的的緣由:
    a. 過長的,過深嵌套的函數調用
    b. 進行大量處理的循環
  2. 若是處理沒必要同步完成,數據沒必要按順序完成,那麼可使用定時器分割循環——數組分塊(array chunking)
    a. 基本思路:爲要處理的項目建立一個隊列,而後使用定時器取出下一個要處理的項目進行處理,接着在設置另外一個定時器

/* 
數組分塊技術:shift() 方法用於把數組的第一個元素從其中刪除,並返回第一個元素的值。
array變量的本質就是一個「待辦事宜」列表,包含要處理的列表
*/
setTimeout(function () {
    // 取出第一個條目並處理
    var item = array.shift();
    process(item);
    // 若還有條目,再設置定時器
    if (array.length > 0) {
        setTimeout(arguments.callee, 100);
    }
}, 100);

 

  3.

function chunk(array, process, context) {
    setTimeout(function () {
        var item = array.shift();
        process.call(context, item);
        if (array.length > 0) {
            setTimeout(arguments.callee, 100);
        }
    }, 100);
}

一旦某個函數須要花費50ms以上的時間完成,那麼最好考慮可否將任務分割爲一系列可使用定時器的小任務


9 函數節流
DOM操做比起非dom操做交互須要更多的內存和CPU時間,連續嘗試過多的DOM相關操做可能會致使瀏覽器掛起,崩潰。尤爲是IE中onresize事件,爲了繞開這個問題,可使用定時器對該函數進行節流。
基本思路:代碼能夠在間斷的狀況下連續重複的執行,目的只有在執行函數的請求中止一段時間以後才執行:

/* 函數節流:
 */
var processor = {
    timeoutID: null,
    // 實際進行處理的方法
    performProcessing: function () {
        // 實際執行的代碼
    },
    // 初始處理調用的方法
    process: function () {
        clearTimeout(this.timeoutID);
        var that = this;
        this.tomeoutID = setTimeout(function () {
            that.performProcessing();
        }, 100);
    }
};
// 嘗試開始執行
processor.process();
// 簡化
function throttle(method, context) {
    clearTimeout(method.tId);
    method.tId = setTimeout(function () {
        method.call(context);
    }, 100);
}
// 列子
function resizeDiv() {
    var div = document.querySelector("#div");
    div.style.height = div.offsetWidth + "px";
}
window.onresize = function () {
    throttle(resizeDiv);
}

 

只要代碼是週期性執行的,都要用函數節流,控制處理的頻率,確保瀏覽器不會再短期內進行過多的計算

10 自定義事件:
事件——一種叫觀察者的設計模式(建立鬆散耦合代碼的技術)
觀察者模式是由兩類對象組成:主體和觀察者。主體負責發佈事件,觀察者經過訂閱這些事件來觀察主體,在DOM中,DOM元素就是主體,你的事件處理程序就是觀察者
事件是與DOM交互最多見的方式。
自定義事件的適用場景:當代碼中存在多個部分在特定時刻互相交互的狀況下

/* 自定義事件 */
function EventTarget() {
    this.handlers = {};
}
EventTarget.prototype = {
    constructor: EventTarget,
    addHandler: function (type, handler) {
        if (typeof this.handlers[type] == "undefined") {
            this.handlers[type] = [];
        }
        this.handlers[type].push(handler);
    },
    fire: function (event) {
        if (!event.target) {
            event.target = this;
        }
        if (this.handlers[event.type] instanceof Array) {
            var handlers = this.handlers[event.type];
            for (var index = 0; index < handlers.length; index++) {
                handlers[index](event);
            }
        }
    },
    removeHandler: function (type, handler) {
        if (this.handlers[type] instanceof Array) {
            var handlers = this.handlers[type];
            for (var index = 0; index < handlers.length; index++) {
                if (handlers[index] == handler) {
                    break;
                }
            }
            handlers.slice(index, 1);
        }
    }
};

function handleMessage(event) {
    alert("message received" + event.message);
}
var target = new EventTarget();
target.addHandler("message", handleMessage); //添加事件處理程序
target.fire({
    type: "message",
    message: "hello world"
}); //觸發事件
target.removeHandler("message", handleMessage); //刪除事件處理程序

 

12 拖放

/* 拖放
<div class="draggable" style="position:absolute; background:red;"></div>
*/
var DragDrop = function () {
// DragDrop對象封裝了拖放的全部基本功能,這是一個單例,並使用了模塊模式來隱藏某些實現細節 可是該方法沒有提供任何方法表示拖動開始,正在拖動,或者已經結束
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.offsetWidth;
}
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, false);
document.addEventListener("mousemove", handleEvent, false);
document.addEventListener("mouseup", handleEvent, false);
},
disable: function () {
document.removeEventListener("mousedown", handleEvent, false);
document.removeEventListener("mousemove", handleEvent, false);
document.removeEventListener("mouseup", handleEvent, false);
}
}
}();
相關文章
相關標籤/搜索