《javascript忍者祕籍》補遺-01

第二章中 做者給了幾個簡單的斷言例子,思路與方向是極不錯的,創造JQ的大神,思想高度絕對沒法讓我質疑的,可是代碼的功底細節,實在是讓人不敢恭維。緩存

第一例:


function assert(value, desc) {
    var li = document.createElement('li');
    li.className = value ? 'pass' : 'fail';
    li.appendChild(document.createTextNode(desc));
    //爲什麼不直接使用textContent進行文本賦值呢?相比下,性能會更好!
    document.getElementById('result').appendChild(li);
    //每次執行斷言 都要從新動態查找一次result節點?
}

assert(true, '這是真幣!');
assert(false, '這是假幣@');

上述書中案例,我從雞蛋裏挑骨頭,選了兩處不妥之處,一個是反覆查找節點無緩存,另外一個是文本節點創造的低效率。閉包

改造代碼:

var assert = (function () {
    //經過閉包 緩存斷言的根ul節點
    var results = document.getElementById('result');
    return function (value, desc) {
        var li = document.createElement('li');
        li.className = value ? 'pass' : 'fail';
        //使用textContent屬性插入文本節點 提升效率
        li.textContent = desc;
        results.appendChild(li);
    };
})();

上面的代碼改善了書裏的小遺漏,仍然不夠完美,由於初始的惰性加載,會有額外的性能損耗,下面再提供兩種極改善方案。app

function getAssert() {
    //取消惰性加載
    var results = document.getElementById('result');
    return function (value, desc) {
        var li = document.createElement('li');
        li.className = value ? 'pass' : 'fail';
        li.textContent = desc;
        results.appendChild(li);
    };
};
//須要初始拿到返回方法
var assert = getAssert();

assert(true, '這是真幣!');
assert(false, '這是假幣@');

上面這一則,取消了惰性加載,可是須要手動獲取返回的方法。dom

下面使用重載:函數

var assert = function(value,desc)  {
    //保留做用域 緩存私有變量results
    var results = document.getElementById('result');
    //重賦值
    assert =  function (value, desc) {
        var li = document.createElement('li');
        li.className = value ? 'pass' : 'fail';
        li.textContent = desc;
        results.appendChild(li);
    };
    //第一次調用 手動調用
    assert(value,desc);
};


assert(true, '這是真幣!');
assert(false, '這是假幣@');

世界清靜了,代碼終於看似完美了。但實際的需求裏,可能咱們要將方法封閉起來,讓同事或者用戶使用,那麼results這個id,就有了至關大的侷限性了,fail與pass的類名也不夠靈活。這個場景下,咱們更應該使用再往上一個的方式,能夠給與咱們更大的diy空間。性能

第二例

(function () {
    
    var results;
    
    this.assert = function (value, desc) {
        var li = document.createElement('li');
        li.className = value ? 'pass' : 'fail';
        results.appendChild(li);
        if (value) {
            li.parentNode.parentNode.className = 'fail';
        }
        return li;
    };
    
    this.test = function (name, fn) {
        results = document.getElementById('results');
        results = assert(true, name).appendChild(
            document.createElement('ul');
    )
        fn();
    };
    
});

這段代碼是用來作將斷言測試分組的,代碼多了些,問題天然也更多了些。測試

首先做者使用了自執行方法封閉了做用域,使用this來指向全局對象,進而產生全局可訪問的屬性。this

但這段代碼是有着執行缺陷的,assert方法能夠在test方法外調用,那麼此時results是根級ul,仍是分組Ul呢?並且動態查找節點的問題依舊沒有改動。code

代碼改善:

var global = (function () {
    //嚴格模式下全局的this 沒法訪問 在此作一個防護措施
    return this ? this : window;
})();

(function (global) {
        //緩存根root節點
    var rootResults = document.querySelector('.test-root'),
        results;
    // 將assert私有化 外部不得訪問
    function assert(value, desc) {
        var domLi = document.createElement('li');
        domLi.className = value ? 'pass' : 'fail';
        domLi.textContent = desc;
        results.appendChild(domLi);
        if (!value) {
            domLi.parentNode.parentNode.className = 'fail';
        }
        return domLi;
    };
    
    global.test = function (name, fn) {
        results = rootResults;
        results = assert(true, name).appendChild(
            document.createElement('ul')
        );
        //回調函數能夠從參數裏調用assert
        fn(assert);
    }
    
})(global);

重複了緩存DOM節點的操做,爲this的指向作出回退機制,私有化assert方法,將assert方法入參到test方法的回調方法中,算是勉強完美了。對象

沒想到,久負盛名,豆瓣評分8+的大做,JQ做者的光環,代碼風格竟然是如此的不謹慎。暫待我往下閱讀,指望可以有打臉回饋。

相關文章
相關標籤/搜索