《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做者的光环,代码风格竟然是如此的不谨慎。暂待我往下阅读,指望可以有打脸回馈。

相关文章
相关标签/搜索