【翻译】理念:无冲突的扩展本地DOM原型
菜鳥翻譯,望大家多多指正哈
原文:http://lea.verou.me/2015/04/idea-extending-native-dom-prototypes-without-collisions/
理念:無沖突的擴展本地DOM原型
正如我昨天在博文中指出,我不喜歡使用jQuery的原因之一是因為它的包裝對象。對于jQuery來說,這是一個明智的決定:早在2006年它被第一次開發出來的時候,IE有一個非常討厭的內存泄漏bug,當我們給一個元素添加屬性時它便很容易被引發出來。哦,那時我們還沒有在IE瀏覽器訪問元素的原型,所以我們必須手動在每個元素上添加這些屬性。Prototype.js試圖走這條路但結果卻是一團糟:他們打算改變他們之前在Prototype2.0版和依附包裝對象的決定。有人曾寫過很長的文章來批判企圖擴展本地DOM元素是多么典型的錯誤想法。
??第一個暴露元素原型的IE瀏覽器是IE8:我們可以訪問Node.prototype,Element.prototype和其他幾種原型。有些是多變的,有些則不是。在IE9,我們得到了全部,包括HTMLElement.prototype及其后代節點,比如HTMLParagraphElement。內存泄漏bug在IE8時得到了改善,到IE9時則得到了修復。但我們還是不要擴展原生的DOM元素,理由很充分:有沖突的風險。沒有哪個函數庫想在元素上添加一堆方法,這種方式很糟糕, 就像被邀請到別人家做客,結果卻把人家家里弄的一團亂。
??但是,如果我們可在避免沖突的條件下對元素添加方法呢?(好吧,從技術上講,可能性很小)。我們只能對元素添加一個屬性,然后把我們所有的方法都附著上去。例如:如果我們的函數庫為yolo并有兩個方法:foo()和bar(),就像這樣:
var element = document.querySelector(".someclass"); element.yolo.foo(); element.yolo.bar(); //你甚至可以鏈式返回他們的元素 element.yolo.foo().yolo.bar();可以肯定,這比包裝對象更別扭,但是我認為使用本地DOM元素所帶來的好處要大于它。當然,你可能不這么認為。
??這基本上同我們做全局是完全一樣的:我們都知道,添加大量的全局變量是不可取的做法,所以每一個函數庫都只創建一個全局變量并把所有方法屬性都附著在這個全局上。? 然而,如果我們試圖以這種天真的方式來實施,我們會發現 用我們的命名空間函數來引用元素是有些難度的:
Element.prototype.yolo = {foo: function () {console.log(this); },bar: function () { /* ... */ }}; someElement.yolo.foo(); // Object {foo: function, bar: function}這里發生了什么?函數中的this指向的調用他們的對象,而不是對象所附著的那個元素,想要避開這個問題我們需要更聰明點。
??記住:Yolo里的this?指向我們試圖掛載方法的元素,但是我們沒有運行任何代碼,所以我們沒有利用它。除非我們能夠得到一個引用該對象的上下文。然而,運行一個function (例如element.yolo(). foo())會毀壞我們良好的API。
??稍等一下,我們可以通過ES5獲得權限!我們這樣做:
Object.defineProperty(Element.prototype, "yolo", {get: function () {return {element: this,foo: function() {console.log(this.element);}, bar: function() { /* ... */ }}},configurable: true,writeable: false}); someElement.yolo.foo(); // It works! (打印出該元素)這個方法奏效,但是這里有一個相當惱人的問題:我們每次生成該對象和重新定義函數都要調用該屬性。這是一個很壞的想法。理想情況下,我們需要生成該對象,然后返回生成的對象。我們也不想讓每個元素都有自己完全獨立的實例,我們想在原型上定義這些函數,并且用JS完美的繼承,因此,我們的庫也是動態可擴展的。幸運的是,有一種方法可以做到這一切:
var Yolo = function(element) {this.element = element; };Yolo.prototype = {foo: function() {console.log(this.element);},bar: function() { /* ... */ } };Object.defineProperty(Element.prototype, "yolo", {get: function () {Object.defineProperty(this, "yolo", {value: new Yolo(this)});return this.yolo;},configurable: true,writeable: false });someElement.yolo.foo(); // 成功! (打印出元素)// 它也是可以動態擴展的 Yolo.prototype.baz = function(color) {this.element.style.background = color; };someElement.yolo.baz("red") // 元素獲得了一個紅色背景注意,上面我們所提到的getter只執行一次。之后它用一個靜態值:一個yolo對象的實例重寫了yolo屬性。因為我們用 到Object.define Property()所以也不會遇到破壞枚舉的問題(for..in循環),這些屬性默認enumerable: false。這里任然有一個不足:這些方法需要用this.element來替代this。我們可以通過封裝他們來解決這一問題:
for (let method in Yolo.prototype) {Yolo.prototype[method] = function(){var callback = Yolo.prototype[method];Yolo.prototype[method] = function () {var ret = callback.apply(this.element, arguments);// 可鏈式返回元素!return ret === undefined? this.element : ret;}} }然而,現在你不能動態的在Yolo.prototype上添加方法并讓他們像element.yolo里的本地方法那樣自動運行。所以它是有點痛的可擴展性(當然,你仍然可以用this.element來添加方法,也是可行的)。?
??你的想法?
?
轉載于:https://www.cnblogs.com/chayangge/p/4515088.html
總結
以上是生活随笔為你收集整理的【翻译】理念:无冲突的扩展本地DOM原型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微软超融合私有云测试11-SCVMM20
- 下一篇: 模糊搜索神器FZF番外篇