>我們每天使用大量工具。不同的圖書館和框架是我們日常工作的一部分。我們之所以使用它們,是因為我們不想為每個項目重新發明輪子,即使我們不了解引擎蓋下的情況。在本文中,我們將揭示最受歡迎的圖書館中發生的一些神奇過程。我們還將查看是否可以復制他們的行為。
>在頁面上,在頁面上沒有差異。但是,如果我們使用Chrome的開發人員工具檢查生成的標記,我們將獲得一個有趣的結果:
<span>var text = $('<div>Simple text</div>'); </span> <span>$('body').append(text);</span>
>看起來我們的字符串todom函數僅創建了一個文本節點,而不是實際的
> jQuery通過創建正確的上下文並僅提取所需部分來成功解決問題。如果我們挖掘了庫的代碼,我們將看到這樣的地圖:
<span>var text = $('<div>Simple text</div>'); </span> <span>$('body').append(text);</span>需要特殊處理的每個元素都分配了一個陣列。這個想法是構造正確的DOM元素,並依賴嵌套的級別來獲取我們需要的東西。例如,對於
有一個地圖,我們必須找出最終想要的標籤。以下代碼從
提取TR
<span>var stringToDom = function(str) { </span> <span>var temp = document.createElement('div'); </span> temp<span>.innerHTML = str; </span> <span>return temp.childNodes[0]; </span><span>} </span><span>var text = stringToDom('<div>Simple text</div>'); </span> <span>document.querySelector('body').appendChild(text);</span>其餘的正在找到正確的上下文並返回DOM元素。這是函數stringTodom的最終變體:
<span>var tableRow = $('<tr><td>Simple text</td></tr>'); </span><span>$('body').append(tableRow); </span> <span>var tableRow = stringToDom('<tr><td>Simple text</td></tr>'); </span><span>document.querySelector('body').appendChild(tableRow);</span>請注意,我們正在檢查字符串中是否有標籤 - 匹配! = null。如果不是,我們只是返回文本節點。仍然有一個臨時
這是一個codepen,顯示了我們的實現:
參見codepen上的krasimir tsonev(@krasimir)的筆xlcgn。
讓我們繼續探索奇妙的AngularJS依賴注入。揭示agnularjs依賴注射
當我們開始使用AngularJs時,它的雙向數據綁定給人留下深刻的印象。我們注意到的第二件事是它的神奇依賴注入。這是一個簡單的示例:
>我們有一個JavaScript函數,可以在系統中顯示用戶。相同的功能需要訪問DOM元素才能放置生成的HTML和AJAX包裝器以獲取數據。為了簡化示例,我們將模擬數據和HTTP請求。
<span>var text = $('<div>Simple text</div>'); </span> <span>$('body').append(text);</span>
我們將使用
標籤作為內容持有人。 AjaxWrapper是模擬請求的對象,DatamOckup是包含我們用戶的數組。這是我們將使用的功能:<span>var stringToDom = function(str) { </span> <span>var temp = document.createElement('div'); </span> temp<span>.innerHTML = str; </span> <span>return temp.childNodes[0]; </span><span>} </span><span>var text = stringToDom('<div>Simple text</div>'); </span> <span>document.querySelector('body').appendChild(text);</span>
當然,如果我們運行顯示器(正文,AjaxWrapper),我們將在頁面上看到三個名稱,以及在我們的控制台中請求的 /api /用戶。我們可以說我們的方法具有兩個依賴性 - 身體和AjaxWrapper。因此,現在的想法是使該函數在不傳遞參數的情況下工作,即,我們必須通過調用DisplayUser()來獲得相同的結果。如果到目前為止,我們使用代碼執行此操作,則結果將是:
><span>var tableRow = $('<tr><td>Simple text</td></tr>'); </span><span>$('body').append(tableRow); </span> <span>var tableRow = stringToDom('<tr><td>Simple text</td></tr>'); </span><span>document.querySelector('body').appendChild(tableRow);</span>
>正常,因為未定義Ajax參數。
>噴油器。要使用依賴關係,我們需要在此處註冊。後來,在某個時候,我們的資源由同一模塊提供給應用程序的邏輯。 >
讓我們創建我們的噴油器:
<span>var wrapMap = { </span> <span>option: [1, '<select multiple="multiple">', '</select>'], </span> <span>legend: [1, '<fieldset>', '</fieldset>'], </span> <span>area: [1, '<map>', '</map>'], </span> <span>param: [1, '<object>', '</object>'], </span> <span>thead: [1, '<table>', '</table>'], </span> <span>tr: [2, '<table><tbody>', '</tbody></table>'], </span> <span>col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'], </span> <span>td: [3, '<table><tbody><tr>', '</tr></tbody></table>'], </span> <span>_default: [1, '<div>', '</div>'] </span><span>}; </span>wrapMap<span>.optgroup = wrapMap.option; </span>wrapMap<span>.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; </span>wrapMap<span>.th = wrapMap.td;</span>我們只需要兩種方法。第一個,註冊,接受我們的資源(依賴項)並在內部存儲它們。第二個接受我們注入的目標 - 具有依賴性和需要接收參數的功能。這裡的關鍵時刻是噴油器不應調用我們的功能。那是我們的工作,我們應該能夠控制這一點。我們可以在解決方法中做的是返回包裝目標並調用目標的封閉。例如:
<span>var match = <span>/<<span>\s*\w.*?></span>/g</span>.exec(str); </span><span>var tag = match[0].replace(<span>/</g</span>, '').replace(<span>/>/g</span>, '');</span>使用該方法,我們將有機會使用所需的依賴項調用該功能。同時,我們沒有更改應用程序的工作流程。噴油器仍然是獨立的,並且不具有與邏輯相關的功能。
>
當然,將顯示器的功能傳遞到Resolve方法無濟於事。>我們仍然遇到相同的錯誤。下一步是找出傳遞的目標需求。它的依賴性是什麼?這是我們可以從Angularjs中採用的棘手部分。我再次對框架的代碼進行了一些挖掘,並找到了以下內容:
<span>var stringToDom = function(str) { </span> <span>var wrapMap = { </span> <span>option: [1, '<select multiple="multiple">', '</select>'], </span> <span>legend: [1, '<fieldset>', '</fieldset>'], </span> <span>area: [1, '<map>', '</map>'], </span> <span>param: [1, '<object>', '</object>'], </span> <span>thead: [1, '<table>', '</table>'], </span> <span>tr: [2, '<table><tbody>', '</tbody></table>'], </span> <span>col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'], </span> <span>td: [3, '<table><tbody><tr>', '</tr></tbody></table>'], </span> <span>_default: [1, '<div>', '</div>'] </span> <span>}; </span> wrapMap<span>.optgroup = wrapMap.option; </span> wrapMap<span>.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; </span> wrapMap<span>.th = wrapMap.td; </span> <span>var element = document.createElement('div'); </span> <span>var match = <span>/<<span>\s*\w.*?></span>/g</span>.exec(str); </span> <span>if(match != null) { </span> <span>var tag = match[0].replace(<span>/</g</span>, '').replace(<span>/>/g</span>, ''); </span> <span>var map = wrapMap[tag] || wrapMap._default, element; </span> str <span>= map[1] + str + map[2]; </span> element<span>.innerHTML = str; </span> <span>// Descend through wrappers to the right content </span> <span>var j = map[0]+1; </span> <span>while(j--) { </span> element <span>= element.lastChild; </span> <span>} </span> <span>} else { </span> <span>// if only text is passed </span> element<span>.innerHTML = str; </span> element <span>= element.lastChild; </span> <span>} </span> <span>return element; </span><span>}</span>>
>我們故意跳過了一些零件,因為它們更像是實施細節。這是對我們來說很有趣的代碼。註釋函數類似於我們的解決方法。它將傳遞的目標函數轉換為字符串,刪除註釋(如果有),然後提取參數。讓我們使用它並查看結果:
<span>function <span>TodoCtrl</span>($scope<span>, $http</span>) { </span> $http<span>.get('users/users.json').success(function(data) { </span> $scope<span>.users = data; </span> <span>}); </span><span>}</span>
這是控制台中的輸出:
<span>var dataMockup = ['John', 'Steve', 'David']; </span><span>var body = document.querySelector('body'); </span><span>var ajaxWrapper = { </span> <span>get: function(path<span>, cb</span>) { </span> <span>console.log(path + ' requested'); </span> <span>cb(dataMockup); </span> <span>} </span><span>}</span>
如果我們獲得了argdecl數組的第二個元素,我們將找到所需依賴項的名稱。這正是我們需要的,因為擁有名稱我們將能夠從噴油器的存儲中傳遞資源。這是有效的版本並成功涵蓋了我們的目標:
<span>var text = $('<div>Simple text</div>'); </span> <span>$('body').append(text);</span>
請注意,我們正在使用.split(/,?/g)將字符串Domel,Ajax轉換為數組。之後,我們正在檢查依賴項是否已註冊,如果是,則將其傳遞給目標函數。噴油器外部的代碼看起來像:
<span>var stringToDom = function(str) { </span> <span>var temp = document.createElement('div'); </span> temp<span>.innerHTML = str; </span> <span>return temp.childNodes[0]; </span><span>} </span><span>var text = stringToDom('<div>Simple text</div>'); </span> <span>document.querySelector('body').appendChild(text);</span>
這種實現的好處是,我們可以在許多功能中註入DOM元素和Ajax包裝器。我們甚至可以這樣分發應用程序的配置。無需將對像從類傳遞到類。這只是寄存器和解決方法。
> 當然,我們的噴油器並不完美。仍然有一些改進的空間,例如支持範圍定義。目前,目標功能通過新創建的範圍調用,但通常我們將要通過自己的範圍。我們應該支持還發送自定義參數以及依賴關係。> 如果我們想在縮小後保持代碼工作,則噴油器會變得更加複雜。眾所周知,縮影替換了函數,變量甚至方法的參數的名稱。而且,由於我們的邏輯依賴於這些名稱,因此我們需要考慮解決方法。一種可能的解決方案再次來自AngularJS:
>而不是僅顯示顯示器的顯示器,我們正在傳遞實際依賴的名稱。
<span>var tableRow = $('<tr><td>Simple text</td></tr>'); </span><span>$('body').append(tableRow); </span> <span>var tableRow = stringToDom('<tr><td>Simple text</td></tr>'); </span><span>document.querySelector('body').appendChild(tableRow);</span>我們的示例在行動中:
參見codepen上的krasimir tsonev(@krasimir)的筆bxdar。
採用ember的計算屬性
Ember是當今最受歡迎的框架之一。它具有許多有用的功能。有一個特別有趣的 - 計算屬性。總而言之,計算的屬性是充當屬性的函數。讓我們看看一個簡單的示例從灰燼的文檔中獲取:
庫通過添加新屬性來調整全局函數對象的原型。這是在類的定義期間運行一些邏輯的好方法。
<span>var wrapMap = { </span> <span>option: [1, '<select multiple="multiple">', '</select>'], </span> <span>legend: [1, '<fieldset>', '</fieldset>'], </span> <span>area: [1, '<map>', '</map>'], </span> <span>param: [1, '<object>', '</object>'], </span> <span>thead: [1, '<table>', '</table>'], </span> <span>tr: [2, '<table><tbody>', '</tbody></table>'], </span> <span>col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'], </span> <span>td: [3, '<table><tbody><tr>', '</tr></tbody></table>'], </span> <span>_default: [1, '<div>', '</div>'] </span><span>}; </span>wrapMap<span>.optgroup = wrapMap.option; </span>wrapMap<span>.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; </span>wrapMap<span>.th = wrapMap.td;</span>>
ember使用getters和setter與對象的數據一起操作。這簡化了計算屬性的實現,因為我們之前還有一層可以達到實際變量。但是,如果我們能夠使用普通的JavaScript對象使用計算的屬性,將會更有趣。例如:
<span>var text = $('<div>Simple text</div>'); </span> <span>$('body').append(text);</span>
>名稱用作常規屬性,但實際上是一個獲取或設置firstName和lastname的函數。
>有一個JavaScript的建築功能,可以幫助我們實現這個想法。看看以下片段:
<span>var stringToDom = function(str) { </span> <span>var temp = document.createElement('div'); </span> temp<span>.innerHTML = str; </span> <span>return temp.childNodes[0]; </span><span>} </span><span>var text = stringToDom('<div>Simple text</div>'); </span> <span>document.querySelector('body').appendChild(text);</span>
object.defineproperty方法可以接受範圍,屬性的名稱,getter和setter。我們要做的就是寫這兩種方法的主體。就是這樣。我們將能夠運行上面的代碼,我們將獲得預期的結果:
><span>var tableRow = $('<tr><td>Simple text</td></tr>'); </span><span>$('body').append(tableRow); </span> <span>var tableRow = stringToDom('<tr><td>Simple text</td></tr>'); </span><span>document.querySelector('body').appendChild(tableRow);</span>
object.defineproperty正是我們所需要的,但是我們不想迫使開發人員每次編寫它。我們可能需要提供多填充,運行其他邏輯或類似的東西。在理想情況下,我們希望提供與Ember類似的界面。只有一個函數是類定義的一部分。在本節中,我們將編寫一個稱為Computize的實用程序函數,該函數將處理我們的對象,並以某種方式將名稱函數轉換為具有相同名稱的屬性。
><span>var wrapMap = { </span> <span>option: [1, '<select multiple="multiple">', '</select>'], </span> <span>legend: [1, '<fieldset>', '</fieldset>'], </span> <span>area: [1, '<map>', '</map>'], </span> <span>param: [1, '<object>', '</object>'], </span> <span>thead: [1, '<table>', '</table>'], </span> <span>tr: [2, '<table><tbody>', '</tbody></table>'], </span> <span>col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'], </span> <span>td: [3, '<table><tbody><tr>', '</tr></tbody></table>'], </span> <span>_default: [1, '<div>', '</div>'] </span><span>}; </span>wrapMap<span>.optgroup = wrapMap.option; </span>wrapMap<span>.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; </span>wrapMap<span>.th = wrapMap.td;</span>
>我們想將名稱方法用作設置器,同時與Getter一起使用。這類似於Ember的計算屬性。
> 現在>一旦添加上述行,我們將能夠將.compented()添加到每個函數定義的末尾:
<span>var match = <span>/<<span>\s*\w.*?></span>/g</span>.exec(str); </span><span>var tag = match[0].replace(<span>/</g</span>, '').replace(<span>/>/g</span>, '');</span>結果,名稱屬性不再包含函數,而是計算出等於true and func屬性的對象填充了舊功能。真正的魔術發生在計算助手的實現中。它通過對象的所有屬性並使用對象。 DefineProperty我們已經計算出屬性:
>
<span>var stringToDom = function(str) { </span> <span>var wrapMap = { </span> <span>option: [1, '<select multiple="multiple">', '</select>'], </span> <span>legend: [1, '<fieldset>', '</fieldset>'], </span> <span>area: [1, '<map>', '</map>'], </span> <span>param: [1, '<object>', '</object>'], </span> <span>thead: [1, '<table>', '</table>'], </span> <span>tr: [2, '<table><tbody>', '</tbody></table>'], </span> <span>col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'], </span> <span>td: [3, '<table><tbody><tr>', '</tr></tbody></table>'], </span> <span>_default: [1, '<div>', '</div>'] </span> <span>}; </span> wrapMap<span>.optgroup = wrapMap.option; </span> wrapMap<span>.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; </span> wrapMap<span>.th = wrapMap.td; </span> <span>var element = document.createElement('div'); </span> <span>var match = <span>/<<span>\s*\w.*?></span>/g</span>.exec(str); </span> <span>if(match != null) { </span> <span>var tag = match[0].replace(<span>/</g</span>, '').replace(<span>/>/g</span>, ''); </span> <span>var map = wrapMap[tag] || wrapMap._default, element; </span> str <span>= map[1] + str + map[2]; </span> element<span>.innerHTML = str; </span> <span>// Descend through wrappers to the right content </span> <span>var j = map[0]+1; </span> <span>while(j--) { </span> element <span>= element.lastChild; </span> <span>} </span> <span>} else { </span> <span>// if only text is passed </span> element<span>.innerHTML = str; </span> element <span>= element.lastChild; </span> <span>} </span> <span>return element; </span><span>}</span>請注意,我們正在刪除原始屬性名稱。在某些瀏覽器對像中。 defineProperty僅在尚未定義的屬性上起作用。
>
這是使用.competed()函數的用戶對象的最終版本。<span>function <span>TodoCtrl</span>($scope<span>, $http</span>) { </span> $http<span>.get('users/users.json').success(function(data) { </span> $scope<span>.users = data; </span> <span>}); </span><span>}</span>>
>返回全名的函數用於更改名稱和姓氏。這就是檢查通過的參數並處理第一個的想法。如果存在,我們將其拆分並將值應用於正常屬性。
>
我們已經提到了所需的用法,但讓我們再看一次:<span>var dataMockup = ['John', 'Steve', 'David']; </span><span>var body = document.querySelector('body'); </span><span>var ajaxWrapper = { </span> <span>get: function(path<span>, cb</span>) { </span> <span>console.log(path + ' requested'); </span> <span>cb(dataMockup); </span> <span>} </span><span>}</span>
以下codepen在實踐中顯示了我們的工作:
參見codepen上的krasimir tsonev(@krasimir)的筆Ahpqo。
>您可能已經聽說過Facebook的框架反應。它圍繞著一切都是組成部分的想法。有趣的是組件的定義。讓我們來看看以下示例:
<span>var text = $('<div>Simple text</div>'); </span> <span>$('body').append(text);</span>
>我們開始考慮的第一件事是這是JavaScript,但這是無效的。有一個渲染功能,它可能會丟失錯誤。但是,訣竅是將此代碼放在帶有自定義類型屬性的<script>標籤中。瀏覽器沒有處理它,這意味著我們免受錯誤的安全。 React有自己的解析器,將我們寫的代碼轉換為有效的JavaScript。 Facebook的開發人員稱為XML Like Language <ancland> jsx<em>。他們的JSX變壓器為390K,包含大約12000行代碼。因此,這有點複雜。在本節中,我們將創建一些簡單的方法,但仍然非常強大。以React風格解析HTML模板的JavaScript類。 > </script>
> Facebook採取的方法是將JavaScript代碼與HTML標記混合。因此,可以說,我們有以下模板:>
<span>var stringToDom = function(str) { </span> <span>var temp = document.createElement('div'); </span> temp<span>.innerHTML = str; </span> <span>return temp.childNodes[0]; </span><span>} </span><span>var text = stringToDom('<div>Simple text</div>'); </span> <span>document.querySelector('body').appendChild(text);</span>和使用它的組件:
>
<span>var tableRow = $('<tr><td>Simple text</td></tr>'); </span><span>$('body').append(tableRow); </span> <span>var tableRow = stringToDom('<tr><td>Simple text</td></tr>'); </span><span>document.querySelector('body').appendChild(tableRow);</span>的想法是,我們指出了模板的ID並定義了應應用的數據。我們實施的最後一部分是將兩個要素合併的實際引擎。讓我們稱其為引擎,然後以這樣的方式啟動:
<span>var wrapMap = { </span> <span>option: [1, '<select multiple="multiple">', '</select>'], </span> <span>legend: [1, '<fieldset>', '</fieldset>'], </span> <span>area: [1, '<map>', '</map>'], </span> <span>param: [1, '<object>', '</object>'], </span> <span>thead: [1, '<table>', '</table>'], </span> <span>tr: [2, '<table><tbody>', '</tbody></table>'], </span> <span>col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'], </span> <span>td: [3, '<table><tbody><tr>', '</tr></tbody></table>'], </span> <span>_default: [1, '<div>', '</div>'] </span><span>}; </span>wrapMap<span>.optgroup = wrapMap.option; </span>wrapMap<span>.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; </span>wrapMap<span>.th = wrapMap.td;</span>>我們正在獲取
以上是揭示JavaScript的魔力的詳細內容。更多資訊請關注PHP中文網其他相關文章!