第二章中 作者给了几个简单的断言例子,思路与方向是极不错的,创造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);
};
})();
上面的代码改善了书里的小遗漏,仍然不够完美,因为初始的惰性加载,会有额外的性能损耗,下面再提供两种极改善方案。
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, '这是假币@');
上面这一则,取消了惰性加载,但是需要手动获取返回的方法。
下面使用重载:
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来指向全局对象,进而产生全局可访问的属性。
但这段代码是有着执行缺陷的,assert方法可以在test方法外调用,那么此时results是根级ul,还是分组Ul呢?而且动态查找节点的问题依旧没有改动。
代码改善:
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作者的光环,代码风格居然是如此的不谨慎。暂待我往下阅读,期望能够有打脸回馈。