抽象 jQuery

Posted

技术标签:

【中文标题】抽象 jQuery【英文标题】:Abstracting jQuery 【发布时间】:2013-04-08 14:04:12 【问题描述】:

在Nicholas Zakas 和Addy Osmani 的这些谈话中,他们讨论了在构建大型 javascript 应用程序时使用外观模式作为沙箱的想法,以便将应用程序与底层基础库分离。

理论上,这种解耦可以让您切换一个基础库,而无需重写您的应用程序模块。但在实践中,这似乎更难以实施。

这个提议的架构有具体的实现,例如AuraJS。然而,从源代码来看,沙箱似乎仍然存在通过从其某些方法返回 jQuery 对象的泄漏抽象。

我并不特别关心 AuraJS,但更关心的是尝试抽象像 jQuery 这样的库而不丢失太多功能的一般概念。

例如,假设我的外观/沙盒有一个 dom 方法.find(selector)。对于它可能返回的结果,我可以想到 3 个选项:

    一个 jQuery 对象 - 这会将 jQuery 泄漏到消费模块中。

    一个原始的 dom 元素 - 功能丧失,没有人真正想要使用它!没有链接。

    自定义的类 jQuery 包装器 - 可能相当复杂,但似乎是理想的解决方案。

所以我的问题是,如何在不丢失太多功能的情况下抽象出一个像 jQuery 这样的库,以便在将来的某个时候以最小的努力替换它?

【问题讨论】:

jQuery 内部相当复杂,创建一个可以被任何(甚至一个)其他库替换的抽象似乎非常困难。有什么意义呢?如果您认为将来可能要交换库,也许您一开始就不应该使用您正在使用的库。 @RobG 我同意这似乎很难,这就是我问这个问题的原因。不管比我聪明得多的人提出这些建议并为它们提供正当理由(请参阅提供的会谈链接)。 在 Nicholas 的架构中,如果您更改基础库,您还必须重新编写应用程序核心。一旦你超越了核心,你就不再编写使用该库的代码了。如果您使用 jQuery,编写模块的人不会编写 any jQuery。 @RobG 我明白这一点。在核心中编写方法来抽象基础库是直截了当的。从这些抽象中返回什么并通过沙箱公开是问题所在。例如如果核心中有一个 dom 模块,如果不是 jQuery 对象,应该从 .find() 返回什么? .deferred() 之类的呢? 模块应该只处理native objects和核心提供的方法。你可能有var x = new Widget(); container.add(x);,然后是x.display('hellow world')。小部件对象可能封装了一个主机(例如 DOM)对象,但您永远不会直接引用它,也不会指定它如何添加到容器中(它可能会被附加或替换某些东西)。您也不会通过设置 innerhtml、innerText、textContent 或添加文本节点或任何其他方式来显示文本。这一切都由核心处理。 【参考方案1】:

我认为您是在询问是否要编写更多模块化代码,但 jquery 不是一个好案例。

异步模块定义 http://addyosmani.com/writing-modular-js/

【讨论】:

请查看我提供的会谈链接。这不仅仅是编写模块化代码。【参考方案2】:

这是一个使用模块作为架构的非常简单的示例:

<!DOCTYPE html>
<title>Module play</title>
<body>
<script>

// myCore provides all functionality required by modules
// Could use a library in here
var myCore = 

  getContainer: function() 
    // code in here to find a suitable container in which to put widgets
    // This is where different client capabilities will be tested to ensure the
    // widget behaves in it's user agent context - desktop, phone, tablet, pad, etc.

    // very simple shortcut
    return 
            element: document.body,

            // This function could use a general purpose library
            add: function(widget) 
              this.element.appendChild(widget.getElement());
            
    ;

  ,

  // This function could use a general purpose library
  getNewWidget: function() 
    var element = document.createElement('div');

    return 

      getElement: function() 
        return element;
      ,

      display: function(text)  

        // Tightly couple to itself or not? 
        this.getElement().innerHTML = '<em>' + text + '</em>';

        // or
        element.innerHTML = '<em>' + text + '</em>';
      
    
  
;

// Missing sandbox layer...

// Add a module - only uses myCore API (access should be controlled by
// the sandbox), does not deal with underlying library or host objects
(function() 

  // Get a container to add a widget too
  var container = myCore.getContainer();

  // Create a widget
  var widget = myCore.getNewWidget();

  // Add the widget to the container
  container.add(widget);

  // Give something to the widget to display
  widget.display('Hello World');

());

</script>
</body>

所以你可以看到,在模块级别,你并不关心宿主环境或底层库,你只是在编写普通的 ECMAScript。你可以采取真正的防御措施并做以下事情:

(function() 
    var container, widget;

    if (!myCore) return;

    if (myCore.getContainer)  // Some would include an isCallable test too

      container = myCore.getContainer();
    

    // getWidget could be a method of container instead so that
    // everything you need is either a method or property of container
    // or widget
    if (myCore.getWidget) 
      widget = myCore.getWidget();
    

    ...

等等,所以一切都经过测试和检查。我省略了错误处理,但希望这个例子就足够了。

【讨论】:

感谢您的详细回答,我知道您来自哪里。但是,您似乎本质上是将小部件的功能和行为推入核心,而将实际模块作为真正愚蠢的消费者。我认为这限制性太强,并且不认为它适用于企业应用程序。我的理解是,核心应该提供一组 功能(即 dom 操作/ajax/mediator)来隐藏基本库实现,然后通过一个沙箱公开,该沙箱充当一个一致的“胶水”层模块和核心。 参见 Nicholas 演讲的幻灯片 40/51/66。没有任何迹象表明模块无法在自己的范围内控制 dom。为了做到这一点,核心/沙箱需要某种形式的 dom 操作“服务”。有关我的意思的示例,请参见 Addy 演讲的幻灯片 105-109。本质上,沙箱应该为模块提供一组“服务”。我发现难以抽象的是那些服务调用的返回值(如果有的话)。 还可以查看AuraJS 的源代码(Addy 对 Nicholas 架构的实现)。您将看到沙箱是如何从 base.js 创建的,以及我所看到的沙箱方法(主要是 jQuery 对象)返回的内容中的泄漏抽象。顺便说一句,谢谢你的时间! 我认为您误解了这些幻灯片 - “不要访问沙箱外的 DOM 元素”并不意味着“使用 DOM 方法在沙箱中使用 DOM 元素”。允许一个 DOM 对象离开核心意味着你的模块必须进行特征检测,所以你又回到了原点。如果在我的示例中用于显示文本的元素从 DIV 更改为 textarea 会怎样?您的模块现在必须设置 value 属性,而不是 innerHTML(或等效)属性。或者也许功能检测 textContent/innerText。这就是您要避免的。 好的。所以澄清一下,每个模块都有自己的沙箱,作为模块的一个非常高级的“接口”?例如“选项卡”小部件可能在其沙箱中有一个 .showTab() 方法,然后调用核心中的 dom 操作抽象?出于兴趣,您对 Aura 中的沙盒实现有何看法?

以上是关于抽象 jQuery的主要内容,如果未能解决你的问题,请参考以下文章

java 读取文件流

抽象类

10.Dart-抽象类

抽象类VS接口

C#抽象类和抽象方法

抽象类抽象方法