>我们每天使用大量工具。不同的图书馆和框架是我们日常工作的一部分。我们之所以使用它们,是因为我们不想为每个项目重新发明轮子,即使我们不了解引擎盖下的情况。在本文中,我们将揭示最受欢迎的图书馆中发生的一些神奇过程。我们还将查看是否可以复制他们的行为。
>在页面上,在页面上没有差异。但是,如果我们使用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>
有一个地图,我们必须找出最终想要的标签。以下代码从
提取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>
<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,显示了我们的实现:
参见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>
>
<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>
>
这是使用.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>
<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中文网其他相关文章!