第二章中 做者給了幾個簡單的斷言例子,思路與方向是極不錯的,創造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做者的光環,代碼風格竟然是如此的不謹慎。暫待我往下閱讀,指望可以有打臉回饋。