這次帶給大家維護JS程式碼的三種方法,維護JS程式碼的注意事項有哪些,下面就是實戰案例,一起來看一下。
維護。在其他語言中,考慮將已存在的物件作為庫用來完成開發任務。在JS中,我們可以將已存在的物件視為一種背景,在這之上可以做任何事情。你應該把已存在的JS物件如一個使用工具函數庫一樣來對待。
不覆寫方法
不新增方法
不刪除方法
當專案中只有你一個開發者時,因為你了解它們,對它們有預期,這些種類的修改很容易處理。當與一個團隊一起在做一個大型的專案時,像這些情況的修改會導致大量的混亂,也會浪費很多時間。
不覆蓋方法
在JS中,有史以來最糟糕的實踐是覆蓋一個非自己擁有的物件的方法,JS中覆蓋一個已存在的方法是難以置信的容易。即使那個神聖的document.getElementById()方法也不例外,可以被輕易地覆蓋。也許你看過類似下面的模式(這種做法也叫「函數劫持」):
// 不好的写法document._originalGetElementById = document.getElementById;document.getElementById = function (id) { if (id === 'window') { return window; } else { return document._originalGetElementById(id); } }
上例中,將一個原生方法document.getElementById()的「指標」儲存在document._originalGetElementById中,以便後續使用。然後,document.getElementById()被一個新的方法覆寫了。新方法有時也會呼叫原始的方法,其中有一種情況不呼叫。這種「覆蓋加可靠退化」的模式至少和覆寫原生方法一樣不好,也許會更糟,因為document.getElementById()時而符合預期,時而不符合。在一個大型的專案中,一個此類問題就會導致浪費大量時間和金錢。
不新增方法
在JS中為已存在的物件新增方法是很簡單的。只需要建立一個函數賦值給一個已存在的物件的屬性,使其成為方法即可。這種做法可以修改所有類型的物件。
// 不好的写法 - 在 DOM对象 上增加了方法document.sayImAwesome = function () { alert("You're awesome."); }// 不好的写法 - 在原生对象上增加了方法Array.prototype.reverseSort = function () { return this.sort().reverse(); }// 不好的写法 - 在库对象上增加了方法YUI.doSomething = function () { // 代码}
幾乎不可能阻止你為任何物件添加方法(ES5新增了三個方法可以做到,後面會介紹)。為非自己擁有的物件增加方法一個大問題,會導致命名衝突。因為一個物件此刻沒有某個方法不代表它未來也沒有。更糟的是如果將來原生的方法和你的方法行為不一致,你將陷入一場程式碼維護的惡夢。
我們要從Prototype JS類別庫的發展歷史中學習。從修改各種各樣的JS物件角度而言Prototype非常有名。它很隨意地為DOM和原生的物件增加方法。實際上,庫的大多數程式碼定義為擴展已存在的對象,而不是自己創建對象。 Prototype的開發者將該程式庫視為對JS的補充。在小於1.6的版本中,Prototype實作了一個document.getElementsByClassName()方法。也許你認識該方法,因為在HTML5中是官方定義的,它標準化了Prototype的用法。
Prototype的document.getElementsByClassName()方法傳回包含了指定CSS類別名稱的元素的一個陣列。 Prototype在陣列上也增加了一個方法,Array.prototype.each(),它在該陣列上迭代並在每個元素上執行一個函數。這讓開發者可以編寫如下程式碼:
document.getElementsByClassName('selected').each(doSomething);
在HTML5標準化該方法和瀏覽器開始原生地實作之前,程式碼是沒有問題的。當Prototype團隊知道原生的document.getElementsByClassName()即將到來,所以他們增加了一些防守性的程式碼,如下:
if (!document.getElementsByClassName) { document.getElementsByClassName = function (classes) { // 非原生实现 }; }
故Prototype只是在document.getElementsByClassName()不存在的時候定義它。這看起來好像問題就此解決了,但還有一個重要的事實是:HTML5的document.getElementsByClassName()不回傳一個數組,所以each()方法根本不存在。原生的DOM方法使用了一個特殊化的集合類型稱為NodeList。 document.getElementsByClassName()傳回一個NodeList來符合其他的DOM方法的呼叫。
如果瀏覽器中原生實作了document.getElementsByClassName()方法,那麼由於NodeList沒有each()方法,無論是原生的或是Prototype增加的each()方法,在執行時都會引發一個JS錯誤。最後的結局是Prototype的使用者必須既要升級類別庫程式碼還要修改自己的程式碼,真是一場維護的惡夢。
从Prototype的错误中可以学到,你不可能精确预测JS将来会如何变化。标准已经进化了,它们经常会从诸如Prototype这样的库代码中获得一些线索来决定下一代标准的新功能。事实上,原生的Array.prototype.forEach()方法在ECMAScript5有定义,它与Prototype的each()方法行为非常类似。问题是你不知道官方的功能与原生会有什么样的不同,甚至是微小的区别也将导致很大的问题。
大多数JS库代码有一个插件机制,允许为代码库安全地新增一些功能。如果想修改,最佳最可维护的方式是创建一个插件。
不删除方法
删除JS方法和新增方法一样简单。当然,覆盖一个方法也是删除已存在的方法的一种方式。最简单的删除一个方法的方式就是给对应的名字赋值为null。
// 不好的写法 - 删除了DOM方法document.getElementById = null;
将一个方法设置为null,不管它以前是怎么定义的,现在它已经不能被调用到了。如果方法是在对象的实例上定义的(相对于对象的原型而言),也可以使用delete操作符来删除。
var person = { name: 'Nicholas'};delete person.name;console.log(person.name); // undefined
上例中,从person对象中删除了name属性。delete操作符只能对实例的属性和方法起作用。如果在prototype的属性或方法上使用delete是不起作用的。例如:
// 不影响delete document.getElementById;console.log(document.getElementById('myelement')); // 仍然能工作
因为document.getElementById()是原型上的一个方法,使用delete是无法删除的。但是,仍然可以用对其赋值为null的方式来阻止被调用。
无需赘述,删除一个已存在对象的方法是糟糕的实践。不仅有依赖那个方法的开发者存在,而且使用该方法的代码有可能已经存在了。删除一个在用的方法会导致运行时错误。如果你的团队不应该使用某个方法,将其标识为“废弃”,可以用文档或者用静态代码分析器。删除一个方法绝对应该是最后的选择。
反之,不删除你拥有对象的方法实际上是比较好的实践。从库代码或原生对象上删除方法是非常难的事情,因为第三方代码正依赖于这些功能。在很多案例中,库代码和浏览器都会将有bug或不完整的方法保留很长一段时间,因为删除它们以后会在数不胜数的网站上导致错误。
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上是維護JS程式碼的三種方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!