搜索
首页web前端js教程揭示JavaScript的魔力

揭示JavaScript的魔力

Feb 21, 2025 am 09:38 AM

揭示JavaScript的魔力

>我们每天使用大量工具。不同的图书馆和框架是我们日常工作的一部分。我们之所以使用它们,是因为我们不想为每个项目重新发明轮子,即使我们不了解引擎盖下的情况。在本文中,我们将揭示最受欢迎的图书馆中发生的一些神奇过程。我们还将查看是否可以复制他们的行为。

>

钥匙要点

  • > jQuery(例如jQuery)简化dom操纵(例如,从字符串创建元素),正确处理复杂的情况(如嵌套元素)(如嵌套元素)。 ember.js使用计算属性增强JavaScript对象,允许属性像函数一样行为,在依赖性更改时自动更新。 React的JSX语法允许将HTML嵌入JavaScript中,然后由React的JSX Transformer处理以创建动态UI组件。
  • >本文展示了用于依赖注入和计算属性的自定义解决方案,展示了开发人员如何在其项目中实施相似的功能。
  • 了解流行的JavaScript框架的基本力学可以使开发人员能够编写更有效和可维护的代码。
  • >从字符串创建DOM元素
  • 随着单页应用程序的兴起,我们正在使用JavaScript做很多事情。应用程序逻辑的很大一部分已移至浏览器。在页面上生成或替换元素是一项常见的任务。与下面显示的相似的代码已经变得非常普遍。
  • 结果是在文档正文中添加了一个新的
    元素。这个简单的操作仅使用一行jQuery完成。没有jQuery,代码有些复杂,但没有太多:
  • >
  • 我们定义了创建临时
    元素的自己的实用程序方法字符串。我们更改了其InnerHTML属性,最后我们简单地退回了第一个孩子,实际上我们需要的是我们所需要的。它以同样的方式工作。但是,我们将通过以下代码观察不同的结果:

    >

    在页面上,在页面上没有差异。但是,如果我们使用Chrome的开发人员工具检查生成的标记,我们将获得一个有趣的结果:>

    <span>var text = $('<div>Simple text</div>');
    </span>
    <span>$('body').append(text);</span>

    >看起来我们的字符串todom函数仅创建了一个文本节点,而不是实际的

    标签。但与此同时,jQuery以某种方式设法这样做。问题在于,包含HTML元素的字符串通过浏览器中的解析器运行。解析器忽略了未放置在正确上下文中的标签,而我们只会得到一个文本节点。没有表格的表行对于浏览器无效。

    > jQuery通过创建正确的上下文并仅提取所需部分来成功解决问题。如果我们挖掘了库的代码,我们将看到这样的地图:>

    <span>var text = $('<div>Simple text</div>');
    </span>
    <span>$('body').append(text);</span>
    需要特殊处理的每个元素都分配了一个阵列。这个想法是构造正确的DOM元素,并依赖嵌套的级别来获取我们需要的东西。例如,对于 元素,我们需要用 child创建一个表。因此,我们有两个层次的嵌套。

    有一个地图,我们必须找出最终想要的标签。以下代码从 简单的文本

    提取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。如果不是,我们只是返回文本节点。仍然有一个临时
    的用法,但是这次我们传递了正确的标签,因此浏览器可以创建有效的DOM树。最后,通过使用一段时间循环,我们将越来越深,直到达到想要的标签为止。

    这是一个codepen,显示了我们的实现:

    参见codepen上的krasimir tsonev(@krasimir)的笔xlcgn。

    让我们继续探索奇妙的AngularJS依赖注入。

    揭示agnularjs依赖注射

    当我们开始使用AngularJs时,它的双向数据绑定给人留下深刻的印象。我们注意到的第二件事是它的神奇依赖注入。这是一个简单的示例:

    >这是一个典型的AngularJS控制器。它执行HTTP请求,从JSON文件中获取数据,然后将其传递给当前范围。我们没有执行todoctrl函数 - 我们没有机会通过任何参数。框架确实如此。那么,这些$范围和$ http变量来自哪里?这是一个超级酷的功能,非常类似于黑魔法。让我们看看它是如何完成的。

    >我们有一个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>', '</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>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>', '</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>/\s*\w.*?></span>/g</span>.exec(str);
    
      <span>if(match != null) {
    </span>    <span>var tag = match[0].replace(<span>/</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是当今最受欢迎的框架之一。它具有许多有用的功能。有一个特别有趣的 - 计算属性。总而言之,计算的属性是充当属性的函数。让我们看看一个简单的示例从灰烬的文档中获取:

    >有一个具有名称和姓氏属性的类。计算的属性fullname返回包含人的全名的串联字符串。奇怪的是,我们使用.property方法来针对应用于fullname的函数的部分。我个人在其他任何地方都没有看到。而且,快速查看框架的代码揭示了魔术:

    库通过添加新属性来调整全局函数对象的原型。这是在类的定义期间运行一些逻辑的好方法。
    <span>var wrapMap = {
    </span>  <span>option: [1, '<select 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>', '</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>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>', '</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>/\s*\w.*?></span>/g</span>.exec(str);
    
      <span>if(match != null) {
    </span>    <span>var tag = match[0].replace(<span>/</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>', '</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>
    >我们正在获取
    '], col: [2, '', '
    '],
    td: [3, '', '
    '],
    _default: [1, '
    ', '
    ']
    }; wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; var element = document.createElement('div'); var match = /\s*\w.*?>/g.exec(str); if(match != null) { var tag = match[0].replace(/, '').replace(/>/g, ''); var map = wrapMap[tag] || wrapMap._default, element; str = map[1] + str + map[2]; element.innerHTML = str; // Descend through wrappers to the right content var j = map[0]+1; while(j--) { element = element.lastChild; } } else { // if only text is passed element.innerHTML = str; element = element.lastChild; } return element; }

    >而不是创建复杂的解析器,几乎发明了我们可能使用纯JavaScript的新语言。我们要做的唯一一件事是使用新函数语法。

    >
    <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 text = $('<div>Simple text</div>');
    </span>
    <span>$('body').append(text);</span>

    控制台中的输出表明我们在正确的轨道上:>

    <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>', '</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>
    >我们不能仅仅使表达式加入,并期望列出颜色。因此,而不是将字符串附加到字符串中,而是将它们收集在数组中。这是Parse函数的更新版本:

    >

    <span>var match = <span>/<<span >\s*\w.*?></span>/g</span>.exec(str);
    <span>var tag = match[0].replace(<span>/</g</span>, '').replace(<span >/>/g</span>, '');</span>
    >填充代码阵列后,我们开始构建函数的主体。模板的每一行都将存储在阵列r中。如果该行是字符串,我们通过逃脱引号并删除新行和选项卡来清理一点。它通过推送方法添加到数组中。如果我们有代码段,则我们检查它是否不是有效的JavaScript操作员。如果是,那么我们不是将其添加到数组中,而只是将其删除为新行。末端输出的控制台。

    好,不是吗?正确格式化的工作JavaScript,在我们组件的上下文中执行将产生所需的HTML标记。
    <span>var stringToDom = function(str) {
    </span>  <span>var wrapMap = {
    </span>    <span>option: [1, '<select 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>/\s*\w.*?></span>/g</span>.exec(str);
    
      <span>if(match != null) {
    </span>    <span>var tag = match[0].replace(<span>/</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>
    剩下的最后一件事是我们虚拟创建的函数的实际运行:>

    >我们将代码包裹在语句中,以便在组件的上下文中运行它。毫无疑问,我们需要使用this.title和this.colors而不是标题和颜色。

    >

    这是一个codepen,演示了最终结果:
    <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>

    参见codepen上的krasimir tsonev(@krasimir)的笔

    摘要

    大型框架和图书馆后面的

    是明智的开发人员。他们发现并使用了棘手的解决方案,这些解决方案并非微不足道,甚至是神奇的。在本文中,我们揭示了一些魔术。在JavaScript世界中,我们能够从最好的代码中学习并使用其代码。

    本文中的代码可从github

    下载

    经常询问有关JavaScript

    魔法的问题(常见问题解答)

    > JavaScript中的魔术方法是什么?它们如何工作?它们不是直接调用的,而是在执行某些动作时被调用。例如,ToString()方法是一种魔术方法,当需要将对象表示为文本值时,它会自动调用。另一个示例是valueof()方法,当对象被表示为原始值时,该方法称为。

    >如何在JavaScript中使用魔术方法?例如,您可以在对象中定义toString()方法,以自定义对象将如何表示为字符串。这是一个简单的示例:

    让Person = {lastname:“ doe”, tostring:function() “ this.lastName;
    }
    };
    console.log(person.toString()); //“ John Doe”

    > JavaScript中魔术功能的意义是什么?它们可以使您的代码更加直观,更易于理解,并提供一种封装和保护您数据的方法。
    >
    您可以在JavaScript中提供一些魔术功能的示例?以下是JavaScript中魔术函数的一些示例:

    1。 ToString():此方法返回代表对象的字符串。

    > 1。 valueof():此方法返回对象的原始值。


    1。 HasownProperty():此方法返回一个布尔值,指示对象是否具有指定的属性。


      > >> >是否有任何限制或缺点,用于在Javascript中使用魔法方法?魔术方法可能非常有用,它们也有一些局限性。首先,它们可以使您的代码更加复杂和更难进行调试,尤其是如果您不熟悉它们的工作方式。如果不正确使用,它们也可以导致出乎意料的行为。
        与其他编程语言相比,JavaScript如何处理魔法方法?
        与其他一些编程语言不同,JavaScript没有正式的概念“魔法方法”。但是,它的确具有类似行为的某些方法,例如tostring()和valueof()。这些方法会在某些情况下自动调用,就像其他语言中的魔术方法一样。
          在JavaScript中使用魔法方法的一些最佳实践是什么?包括理解何时以及为什么使用它们,很少使用它们以避免复杂性,并始终彻底测试代码以确保其行为的行为。可以与诸如react或vue?

          之类的JavaScript框架一起使用,是的,可以将魔术方法与JavaScript框架(如React或Vue)一起使用。但是,使用它们的方式可能会因框架而异。始终最好参考特定框架的文档以进行指导。

          >

          >如何在JavaScript中了解有关魔法方法的更多信息?您可以从官方的JavaScript文档以及在线教程和课程开始。您还可以在自己的项目中练习使用它们来获得动手体验。

          >是否有任何工具或库可以帮助使用JavaScript中的魔法方法?可以在JavaScript中使用魔法方法的工具。例如,Lodash是一个流行的JavaScript实用程序库,它为使用数组,对象和其他类型的数据提供了有用的方法。

        以上是揭示JavaScript的魔力的详细内容。更多信息请关注PHP中文网其他相关文章!

        声明
        本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
        Python vs. JavaScript:开发人员的比较分析Python vs. JavaScript:开发人员的比较分析May 09, 2025 am 12:22 AM

        Python和JavaScript的主要区别在于类型系统和应用场景。1.Python使用动态类型,适合科学计算和数据分析。2.JavaScript采用弱类型,广泛用于前端和全栈开发。两者在异步编程和性能优化上各有优势,选择时应根据项目需求决定。

        Python vs. JavaScript:选择合适的工具Python vs. JavaScript:选择合适的工具May 08, 2025 am 12:10 AM

        选择Python还是JavaScript取决于项目类型:1)数据科学和自动化任务选择Python;2)前端和全栈开发选择JavaScript。Python因其在数据处理和自动化方面的强大库而备受青睐,而JavaScript则因其在网页交互和全栈开发中的优势而不可或缺。

        Python和JavaScript:了解每个的优势Python和JavaScript:了解每个的优势May 06, 2025 am 12:15 AM

        Python和JavaScript各有优势,选择取决于项目需求和个人偏好。1.Python易学,语法简洁,适用于数据科学和后端开发,但执行速度较慢。2.JavaScript在前端开发中无处不在,异步编程能力强,Node.js使其适用于全栈开发,但语法可能复杂且易出错。

        JavaScript的核心:它是在C还是C上构建的?JavaScript的核心:它是在C还是C上构建的?May 05, 2025 am 12:07 AM

        javascriptisnotbuiltoncorc; saninterpretedlanguagethatrunsonenginesoftenwritteninc.1)javascriptwasdesignedAsalightweight,解释edganguageforwebbrowsers.2)Enginesevolvedfromsimpleterterterpretpreterterterpretertestojitcompilerers,典型地提示。

        JavaScript应用程序:从前端到后端JavaScript应用程序:从前端到后端May 04, 2025 am 12:12 AM

        JavaScript可用于前端和后端开发。前端通过DOM操作增强用户体验,后端通过Node.js处理服务器任务。1.前端示例:改变网页文本内容。2.后端示例:创建Node.js服务器。

        Python vs. JavaScript:您应该学到哪种语言?Python vs. JavaScript:您应该学到哪种语言?May 03, 2025 am 12:10 AM

        选择Python还是JavaScript应基于职业发展、学习曲线和生态系统:1)职业发展:Python适合数据科学和后端开发,JavaScript适合前端和全栈开发。2)学习曲线:Python语法简洁,适合初学者;JavaScript语法灵活。3)生态系统:Python有丰富的科学计算库,JavaScript有强大的前端框架。

        JavaScript框架:为现代网络开发提供动力JavaScript框架:为现代网络开发提供动力May 02, 2025 am 12:04 AM

        JavaScript框架的强大之处在于简化开发、提升用户体验和应用性能。选择框架时应考虑:1.项目规模和复杂度,2.团队经验,3.生态系统和社区支持。

        JavaScript,C和浏览器之间的关系JavaScript,C和浏览器之间的关系May 01, 2025 am 12:06 AM

        引言我知道你可能会觉得奇怪,JavaScript、C 和浏览器之间到底有什么关系?它们之间看似毫无关联,但实际上,它们在现代网络开发中扮演着非常重要的角色。今天我们就来深入探讨一下这三者之间的紧密联系。通过这篇文章,你将了解到JavaScript如何在浏览器中运行,C 在浏览器引擎中的作用,以及它们如何共同推动网页的渲染和交互。JavaScript与浏览器的关系我们都知道,JavaScript是前端开发的核心语言,它直接在浏览器中运行,让网页变得生动有趣。你是否曾经想过,为什么JavaScr

        See all articles

        热AI工具

        Undresser.AI Undress

        Undresser.AI Undress

        人工智能驱动的应用程序,用于创建逼真的裸体照片

        AI Clothes Remover

        AI Clothes Remover

        用于从照片中去除衣服的在线人工智能工具。

        Undress AI Tool

        Undress AI Tool

        免费脱衣服图片

        Clothoff.io

        Clothoff.io

        AI脱衣机

        Video Face Swap

        Video Face Swap

        使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

        热门文章

        热工具

        VSCode Windows 64位 下载

        VSCode Windows 64位 下载

        微软推出的免费、功能强大的一款IDE编辑器

        记事本++7.3.1

        记事本++7.3.1

        好用且免费的代码编辑器

        WebStorm Mac版

        WebStorm Mac版

        好用的JavaScript开发工具

        SublimeText3汉化版

        SublimeText3汉化版

        中文版,非常好用

        适用于 Eclipse 的 SAP NetWeaver 服务器适配器

        适用于 Eclipse 的 SAP NetWeaver 服务器适配器

        将Eclipse与SAP NetWeaver应用服务器集成。