第二章中 做者给了几个简单的断言例子,思路与方向是极不错的,创造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做者的光环,代码风格竟然是如此的不谨慎。暂待我往下阅读,指望可以有打脸回馈。