这种JavaScript编码方式有什么问题? (闭包与原型)

时间:2013-02-06 01:11:42

标签: javascript closures prototype

我们一直在争论如何最好地处理我们的JS应用程序中的对象,研究Stoyan Stefanov的书,阅读关于“新”,“这个”,“原型”,闭包等的无尽SO帖子。(事实上有这么多,他们有如此多的竞争理论,表明没有完全明显的答案。)

所以让我们假设我们不关心私人数据。我们满足于信任用户和开发人员,不要在我们定义的方式之外乱搞对象。

鉴于此,除了它似乎无视数十年的OO风格和历史之外,这种技术会出错吗?

// namespace to isolate all PERSON's logic
var PERSON = {};

// return an object which should only ever contain data.
// The Catch: it's 100% public

PERSON.constructor = function (name) {
     return {
         name: name
     } 
}

// methods that operate on a Person
// the thing we're operating on gets passed in

PERSON.sayHello = function (person) {
     alert (person.name); 
}

var p = PERSON.constructor ("Fred");
var q = PERSON.constructor ("Me");

// normally this coded like 'p.sayHello()'
PERSON.sayHello(p);
PERSON.sayHello(q);

显然:

  1. 没有什么可以阻止某人在邪恶中改变'p' 方式,或简单地说PERSON的逻辑最终遍布整个地方。 (规范的'新'技术也是如此)。
  2. 将'p'传递给你的每一个功能都是一件轻微的麻烦 想要用它。
  3. 这是一种奇怪的方法
  4. 但这些是否足以让他们解雇呢?积极的一面:

    1. 高效,因为(可以说)与重复功能声明的闭包相对立。
    2. 似乎非常简单易懂,而不是摆弄 '这'到处都是。
    3. 关键点是隐私的前提。我知道我会因此受到抨击,但是,寻找任何反馈。欢呼声。

2 个答案:

答案 0 :(得分:9)

它没有任何内在错误。但它确实放弃了使用Javascript原型系统所固有的许多优点。

  • 除了它是一个对象文字之外,你的对象对它自身一无所知。因此instanceof无法帮助您确定其来源。只使用鸭子打字你就会陷入困境。
  • 您的方法本质上是命名空间静态函数,您必须通过传入对象作为第一个参数来重复自己。通过拥有原型对象,您可以利用动态调度,以便p.sayHello()可以为PERSONANIMAL执行不同的操作,具体取决于Javascript知道的类型关于。这是多态的一种形式。每次调用方法时,您的方法都要求您命名(并且可能会弄错)该类型。
  • 您实际上不需要constructor函数,因为函数已经是对象。您的PERSON变量也可以是构造函数。

您在此处所做的是创建模块模式(如命名空间)。

这是另一种模式,它可以保留你所拥有的,但具有上述优势:

function Person(name)
{
    var p = Object.create(Person.prototype);
    p.name = name; // or other means of initialization, use of overloaded arguments, etc.
    return p;
}
Person.prototype.sayHello = function () { alert (this.name); }

var p = Person("Fred"); // you can omit "new"
var q = Person("Me");
p.sayHello();
q.sayHello();
console.log(p instanceof Person); // true
var people = ["Bob", "Will", "Mary", "Alandra"].map(Person);
        // people contains array of Person objects

答案 1 :(得分:1)

是的,我并不是真的理解你为什么要试图躲避构造函数的方法,或者为什么他们甚至觉得需要在构造函数时将语法糖层叠在函数构造函数(Object.create和很快类)上对于OOP来说,无论有多少蹩脚的理由让Crockford不喜欢它们(因为人们忘记使用新关键字 - 认真吗?),它们本身就是一种优雅,灵活且完全合理的OOP方法。 JS是由功能驱动的,它的OOP机制也不例外。拥抱它比躲避它更好,IMO。

首先,您的积分列在"显然"

  1. 在JavaScript中几乎不值得一提。高度可变性是按设计的。我们在JavaScript中不怕自己或其他开发人员。私人与公共范式并不有用,因为它可以保护我们免于愚蠢,而是因为它更容易理解其他开发代码背后的意图。

  2. 调用的努力不是问题所在。当你不清楚为什么你做了你在那里做过的事情时,麻烦就会出现。我并没有真正看到你正在努力实现的目标,核心语言不会为你做得更好。

  3. 这是JavaScript。除了JS开发人员多年以来,所有人都很奇怪。如果您找到一种更好的方法来解决某个领域中的问题而不是更典型的解决方案,那么请不要大汗淋漓。在尝试替换之前,请确保您了解更典型方法的重点,因为许多人在从其他语言范例进入JS时都会这样做。使用JS很容易做一些简单的事情,但是一旦你想要获得更多的OOP驱动,你可以学习关于核心语言内容如何工作的一切,这样你就可以更加怀疑了流行的观点传播的是那些让生活中的JavaScript变得更加可怕,并且更加充满了致命的诱杀陷阱的人们所传播的。

  4. 现在你的积分在"正面,"

    1. 首先,重复的函数定义实际上只是在繁重的循环场景中需要担心的事情。如果你经常以足够快的速度生成足够数量的对象以使非原型公共方法定义成为一个性能问题,那么你可能会在短时间内遇到非平凡对象的内存使用问题。然而,我用过去时态说话,因为它不再是一个真正的相关问题。在现代浏览器中,由于现代JIT编译器的工作方式,在其他函数内定义的函数实际上通常是性能增强。无论您支持哪种浏览器,每个对象定义的一些func都不是问题,除非您期待成千上万的对象。

    2. 关于简单易懂的问题,不适合我,因为我不知道你在这里取得了什么样的胜利。现在不是要使用一个对象,而是必须同时使用对象和它的伪构造函数,如果我不查看定义,则会向我暗示与&一起使用的函数#39;新'构建对象的关键字。如果我对你的代码库不熟悉,我会浪费大量时间试图找出你为什么这样做,以避免打破我不理解的其他问题。

    3. 我的问题是:

      为什么不首先在构造函数中添加对象文字中的所有方法?那里没有任何性能问题,而且从来没有真正有过唯一的其他可能的胜利,那就是你希望能够在用它创建新对象之后为人们添加新方法,但是那个&#39 ; s我们在正确的构造函数上使用原型(原型方法btw对于旧浏览器中的内存很有用,因为它们只定义了一次)。

      如果你必须继续传递对象以获取方法以了解属性是什么,为什么你甚至想要对象?为什么不只是期望具有特定属性的简单数据结构类型对象的函数?它不再是OOP了。

      但我的主要批评意见

      你错过了OOP的要点,这是一种JavaScript比大多数语言更能避免隐藏人的事情。请考虑以下事项:

      function Person(name){
          //var name = name; //<--this might be more clear but it would be redundant
          this.identifySelf = function(){ alert(name); }
      }
      
      var bob = new Person();
      bob.identifySelf();
      

      现在,更改bob标识的名称,而不是覆盖对象或方法,这两件事只有在您明确表示您不想使用最初设计的对象时才会这样做和建造。你当然不能。这使得任何看到这个定义的人都清楚地知道在这种情况下该名称实际上是一个常量。在一个更复杂的构造函数中,它将确定唯一允许更改或修改名称的是实例本身,除非用户添加了一个非傻的setter方法,这将是愚蠢的,因为这基本上(看着你的Java Enterprise Beans)MURDER THE OOP的中心目的。

      明确责任分工是关键

      忘记他们在每本书中放入的关键词,并思考整个问题。在OOP之前,所有这些功能都只是一堆功能和数据结构。使用OOP,您通常会拥有一组与一组数据捆绑在一起的方法,只有对象本身才会发生变化。

      所以,让我们说输出出了问题:

      • 在我们严格程序性的一系列功能中,对可能搞砸了这些数据的牌数没有实际限制。我们可能会有很好的错误处理,但是一个功能可能会以原始罪魁祸首难以追踪的方式进行分支。

      • 在正确的OOP设计中,数据通常位于对象网守后面,我知道只有一个对象可以实际负责更改。

      大多数时候暴露所有数据的对象实际上只比旧的程序方法略好。所有真正做的就是给你一个名字,用松散相关的方法来分类。

      很多关于这个&#39;

      我从来没有理解过分注意到这个&#39;这个&#39;关键词混乱而且令人困惑。这真的没什么大不了的。 &#39;这&#39;标识您正在使用的实例。就是这样。如果该方法不被称为属性,则它不会知道要查找的实例,因此它默认为全局对象。这是愚蠢的(未定义本来会更好),但它在这种情况下不能正常工作应该是一种语言,其中函数也像数据一样可移植,并且可以非常容易地附加到其他对象。使用&#39;这个&#39;在函数中:

      • 它被定义并被称为实例的属性。

      • 它作为事件处理程序传递(将其称为被侦听事物的成员)。

      • 您正在使用call或apply方法暂时将其作为某个其他对象的属性进行调用,而不会将其分配。

      但请记住,这真的很重要。将一个公共方法分配给某个var并从该var调用将执行全局操作或在严格模式下抛出错误。在没有被引用为对象属性的情况下,函数只关心它们被定义的范围(它们的闭包)以及你传递它们的args。

相关问题