【问题标题】:Pseudo-classical vs. "The JavaScript way"伪经典与“JavaScript 方式”
【发布时间】:2010-10-21 22:12:56
【问题描述】:

刚读完 Crockford 的“JavaScript: The Good Parts”,我有一个关于他对伪古典与原型方法的立场的问题。其实我对他的立场并不感兴趣;我只是想了解他的论点,以便建立自己的立场。

在书中,Crockford 似乎推断构造函数和“all that jazz”不应该在 JavaScript 中使用,他提到了 'new' 关键字的实现很糟糕——即非构造函数可以用'new' 关键字,反之亦然(可能导致问题)。

我以为我明白他来自哪里,但我想我不明白。

当我需要创建一个新模块时,我通常会这样开始:

function MyModule(something) {
    this.something = something || {};
}

然后我会在它的原型中添加一些方法:

MyModule.prototype = {
    setSomething : function(){},
    getSomething : function(){},
    doSomething : function(){}
}

我喜欢这个模型;这意味着我可以在需要时创建一个新实例,并且它有自己的属性和方法:

var foo = new MyModule({option1: 'bar'});
// Foo is an object; I can do anything to it; all methods of the "class"
// are available to this instance.

我的问题是:如何使用更适合 JavaScript 的方法实现上述目标?换句话说,如果“JavaScript”是一个人,她会建议什么?

另外:Crockford 说一种特定的设计模式比另一种“更具表现力”是什么意思?

【问题讨论】:

    标签: javascript


    【解决方案1】:

    见:Is JavaScript's “new” Keyword Considered Harmful?

    重要的是要记住,Crockford 和许多其他 JavaScript 程序员一样,最初接触该语言时着眼于“修复”它 - 使其更像其他(所谓的“经典”)OO 语言。所以编写了大量的结构化代码,构建了库和框架,然后......然后他们开始意识到这并不是真的必要;如果你按照自己的方式处理 JS,你可以相处得很好。

    【讨论】:

    • 谢谢!通读该线程已为我提供了所需的所有信息(尤其是您的答案)!
    【解决方案2】:

    在我的理解中,您的示例的原型变体如下所示:

    Object.beget = function (o) { /* Crockfords replacement for the new */ }
    
    var myModule = {
        something : null,
        getSomething : function () {},
        setSomething : function () {},
        doSomething : function () {}
    };
    

    然后你可以这样做:

    var foo = Object.beget(myModule);
    foo.something = bar;
    

    更新:您还可以使用构建器模式来替换构造函数,如下所示:

    var myModuleBuilder = {
        buildMyModule : function (something) {
            var m = Object.beget(myModule);
            m.something = something || {};
            return m;
        }
    }
    

    那么你可以这样做:

    var foo = myModuleBuilder.buildMyModule(something);
    

    【讨论】:

      【解决方案3】:

      您的实现是有问题的,因为您正在替换整个原型对象,丢失了继承的函数原型的属性,并且如果您以后使用继承的能力也会中断,或者至少使其变得更加困难其他类也是这样写的。

      更适合 Javascript 的方法是:

      var MyClass = function (storeThis) {
       this.storage = storeThis
      }
      
      MyClass.prototype.getStorage = function (){
         return this.storage;
      }
      
      MyClass.prototype.setStorage = function (newStorage){
        this.storage = newStorage;
      }
      

      使用它:

      var myInstance = new MyClass("sup");
      alert("myInstance storage: " + myInstance.getStorage());
      myInstance.setStroage("something else");
      

      至于 'new' 关键字和 Crawford 的问题,我无法真正回答,因为我没有读过这本书,但我可以看到如何通过使用 new 调用任何函数来创建新对象关键字,包括应该是类方法的函数。

      当某人说某事(例如设计模式)更具“表现力”时,他或她通常意味着该设计模式清晰易懂,易于理解它的目标和实现方式。

      【讨论】:

      • 感谢您的回答。我的例子并不是很重要,它是我正在询问的设计模式本身。感谢您解释“富有表现力”:)
      • 您的第一点含糊不清,似乎具有误导性。这个被替换的原型具有而 J-P 的替换原型没有的“与您正在覆盖的每个对象相关联的内置功能”是什么?
      • 好吧,我的措辞很糟糕,但是从函数派生的原型具有对象字面量所没有的属性和属性。以 toString 为例。
      • 不正确。函数的原型以对象的形式开始,就像使用new Object() 创建的一样。使用{} 创建的对象确实有toString 方法。试试({}).toString(),例如(对象字面量周围的括号以确保它被解释为对象而不是块)
      • Hrm,我知道他们都有一个 toString 方法。我的观点是他们做了不同的事情。我只是尝试将对象文字设置为函数的原型,但它并没有像我想象的那样覆盖它。
      【解决方案4】:

      Javascript 不是一个人,所以她不能真正建议你做什么。

      上面的答案都没有提到简单的老式函数式继承,我倾向于发现它是最简单的。

      function myModuleMaker(someProperty){
        var module = {}; //define your new instance manually
      
        module.property = someProperty; //set your properties
      
        module.methodOne = function(someExternalArgument){
          //do stuff to module.property, which you can, since you have closure scope access
        return module;
        }
      }
      

      现在创建一个新实例:

      var newModule = myModuleMaker(someProperty);
      

      您仍然可以通过这种方式获得伪经典的所有好处,但您会遇到一个缺点,即每次实例化时都会创建所有实例方法的新副本。这可能仅在您开始拥有数百个(甚至数千个)实例时才有意义,这是大多数人很少遇到的问题。如果您要创建真正庞大的数据结构或使用许多实例进行硬核动画,则最好使用伪经典。

      我认为很难说这两种方法本身都是“不好的做法”。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-12
        • 1970-01-01
        • 2016-04-10
        • 1970-01-01
        • 2013-01-20
        相关资源
        最近更新 更多