【问题标题】:Javascript OO syntaxJavascript OO 语法
【发布时间】:2026-01-13 18:50:01
【问题描述】:

在 JavaScript 中执行 OO 的方法似乎有很多种。

我喜欢:

function ClassA(){};
ClassA.prototype={
    someFunc:function(a,b,c){},
    otherFunc:function(){}
}
var c=new ClassA();

并且从未使用过超出此提供的功能(尽管是精通的 OOer)。我怀疑这是老式的,因为我经常看到新的闪亮变体,这让我怀疑我是否选择了最好的方法。例如,您可以在构造函数方法中使用魔术来创建私有变量和访问器方法,我认为(直到最近)这是不可能的。子类化呢?我不知道如何实现这一点,但它现在必须有某种通用模式。

你是怎么做的,为什么?

【问题讨论】:

    标签: javascript oop


    【解决方案1】:
    function foo() {
      var bar = function() { console.log("i'm a private method"); return 1; };
      var iAmAPrivateVariable = 1;
    
      return {
        publicMethod: function() { alert(iAmAPrivateVariable); },
        publicVariable: bar()
      }
    }
    
    //usage
    var thing = foo()
    

    这被称为函数式方法,因为您实际上是在利用闭包进行封装(这是在 javascript 中执行此操作的唯一方法)。

    一般来说,你不应该在 javascript 中做 OO,因为很多原因,它并不是一种很好的语言。考虑带有波浪括号和分号的方案,您将开始像专业人士一样编写语言。话虽如此,有时OO更适合。在这些情况下,上述方法通常是最好的选择

    编辑:将继承引入混合

    function parent() {
      return { parentVariable: 2 };
    }
    
    function foo() {
      var bar = function() { console.log("i'm a private method"); return 1; };
      var iAmAPrivateVariable = 1;
    
      me = parent();
      me.publicMethod = function() { alert(iAmAPrivateVariable); };
      me.publicVariable = bar();
    
      return me;
    }
    

    这使事情变得更加复杂,但在实现期望的最终结果的同时仍然采用面向 OO 概念的函数式方法(在这种情况下,使用装饰器函数而不是真正的继承)。我喜欢整个方法的一点是,我们仍然以这种语言的预期方式处理对象——一个你可以随意附加东西的属性包。

    EDIT2:

    只是想在应得的地方给予赞扬,这种方法对 doug crockford 在 Javascript: The Good Parts 中建议的内容进行了非常轻微的简化。如果您想将您的 js 技能提升到一个新的水平,我强烈建议您从那里开始。我想我从来没有从这么小的书中学到这么多。

    另一个注意事项是,这与您在大多数工作中看到的情况大不相同,而且通常很难解释 a) 发生了什么,b) 为什么会这样对同事来说是个好主意。

    【讨论】:

    • 我非常喜欢这个。是否可以弯曲提供继承而不使用原型?
    • bar 将成为全局对象 (window.bar) 的属性,因为它没有在 foo 函数的范围内使用 var 语句声明。同样在 ECMAScript 5 严格模式下,这会给你一个ReferenceError,所以总是使用var声明一个变量;)​​
    • 这个方法唯一的问题是它返回的匿名对象不能再被识别为foo的实例。
    • @casablanca:这完全正确,但在动态语言中 99.9% 的情况下也没有那么重要。你关心一个对象可以做某事,而不是它是某事。
    • 正是 Crockford 的一篇关于对私有变量使用闭包的文章启发了我提出这个问题。我非常怀疑创建的库试图过于接近地模拟基于类的 OO,因为我认为这不是 JS 的意义所在,否则现在会有语言支持。我真的很喜欢将函数式概念应用于 Javascript。我想我会接受你的书推荐。塔!
    【解决方案2】:

    Simple JavaScript Inheritance

    因为约翰·雷西格是这么说的。

    【讨论】:

    • 是的,但他也将 $ 作为变量名普及了......不可原谅。
    【解决方案3】:

    JavaScript中的“子类化”一般是指基于原型的继承,基本遵循这种模式:

    function Superclass() { }
    Superclass.prototype.someFunc = function() { };
    
    function Subclass() { }
    Subclass.prototype = new Superclass();
    Subclass.prototype.anotherFunc = function() { };
    
    var obj = new Subclass();
    

    这会从obj -> Subclass.prototype -> Superclass.prototype -> Object.prototype 构建一个“原型链”。

    几乎每个 JavaScript 的 OOP 库都建立在这种技术之上,提供抽象出大部分原型“魔法”的函数。

    【讨论】:

    • 这不是个好主意;它依赖于调用父构造函数来获取子构造函数的原型。最好做这样的事情(请参阅我的回复):function C(){}; function clone(obj){C.prototype=obj, return new C}; Subclass.prototype=clone(Superclass.prototype); ... 这样就不会调用父 ctor,这可能会产生不必要的副作用。
    【解决方案4】:

    我认为 joose 是一种在 javascript 中进行 OOP 的非常酷的方式
    http://code.google.com/p/joose-js/

    【讨论】:

      【解决方案5】:

      JavaScript 中的对象与几乎所有其他知名语言都不同。它们不是基于类的(如Java、C++、PHP 等),而是基于原型的。因此,必须对面向对象编程的基本范式进行大量修改。不能或不想重新思考这一点并坚持使用基于类的思维的人必须在 JavaScript 中构建基于类的逻辑,或者使用已经构建它的其他人的代码。

      【讨论】:

      • 完全同意。 JavaScript 中的 faking classes 试图将 java 硬塞到与它表面上相似的东西中。
      【解决方案6】:

      我喜欢做类似的事情

      // namespace "My"
      var My = new function {
      
        // private methods
        /**
         * Create a unique empty function.
         * @return {Function} function(){}
         */
        function createFn () {return function(){}}
      
        /** A reusable empty function. */
        function emptyFn () {}
      
        /**
         * Clone an object
         * @param {Object}  obj     Object to clone
         * @return {Object}         Cloned object
         */
        function clone (obj) { emptyFn.prototype=obj; return new emptyFn() }
      
        // public methods
        /**
         * Merge two objects
         * @param {Object} dst        Destination object
         * @param {Object} src        Source object
         * @param {Object} [options]  Optional settings
         * @return {Object}           Destination object
         */
        this.merge = function (dst, src, options) {
          if (!options) options={};
          for (var p in src) if (src.hasOwnProperty(p)) {
            var isDef=dst.hasOwnProperty(p);
            if ((options.noPrivate && p.charAt(0)=='_') || 
                (options.soft && isDef) || 
                (options.update && !isDef)) continue;
            dst[p]=src[p]; 
          }
          return dst;
        }
      
        /**
         * Extend a constructor with a subtype
         * @param {Function} superCtor      Constructor of supertype
         * @param {Function} subCtor        Constructor of subtype
         * @param {Object} [options]        Optional settings
         * @return {Function}               Constructor of subtype
         */
        this.extend = function (superCtor, subCtor, options) {
          if (!subCtor) subCtor=createFn();
          if (!options) options={};
          if (!options.noStatic) this.merge(subCtor, superCtor, options); 
          var oldProto=subCtor.prototype;
          subCtor.prototype=clone(superCtor.prototype);
          this.merge(subCtor.prototype, oldProto);
          if (!options.noCtor) subCtor.prototype.constructor=subCtor; 
          return subCtor;
        }
      
      }
      

      然后类似...

      // namespace "My.CoolApp"
      My.CoolApp = new function(){
      
        // My.CoolApp.ClassA
        this.ClassA = new function(){
      
          // ClassA private static
          var count=0; 
      
          // ClassA constructor 
          function ClassA (arg1) {
            count++;
            this.someParam=arg1;
          }
      
          // ClassA public static
          My.merge(ClassA, { 
            create: function (arg1) {
              return new ClassA(arg1);
            }
          }
      
          // ClassA public
          My.merge(ClassA.prototype, {
            doStuff : function (arg1) {
              alert('Doing stuff with ' + arg1);
            },
            doOtherStuff : function (arg1) {
              alert('Doing other stuff with ' + arg1);
            }
          }
      
          return ClassA;
        }
      
        // My.CoolApp.ClassB
        this.ClassB = new function(){
      
          My.extend(My.CoolApp.ClassA, ClassB);
          // ClassB constructor
          function ClassB () {
            ClassA.apply(this, arguments);
          }
      
          return ClassB;
        }
      
      }
      

      ...clone 函数是继承的关键。简而言之:

      • 通过将对象设为一次性函数的原型并使用“new”调用该函数来克隆对象。
      • 克隆父构造函数的原型,并将结果设置为子类的原型。

      【讨论】:

        【解决方案7】:

        用于 Canvas 的 JavaScript 中的 OOP

        看看 js 中的 OOP 在不同的情况下有多么有用...这让您可以绘制正方形和圆形 as objects,以便您可以返回并循环或随意操作它们。

        function Shape(x,y,color){
          this.x = x
          this.y = y
          this.color = color
        }
        
        function Square(height,width,color){
          Shape.call(this, event.x, event.y, color)
          this.height = height
          this.width = width
          this.x -= canvas.offsetLeft + (this.height/2)
          this.y -= canvas.offsetTop + (this.width/2)
        }
        
        Square.prototype = new Shape();
        Square.prototype.draw = function(color){
          ctx.fillStyle = color
          ctx.fillRect(this.x,this.y,this.height,this.width)
        }
        
        function Circle(color, width){
          Shape.call(this)
          this.x = event.x -60
          this.y = event.y -60
          this.width = width
         }
        
        Circle.prototype = new Shape();
        Circle.prototype.draw = function(color){
          ctx.beginPath()
          ctx.arc(this.x,this.y,this.width,0,2*Math.PI, false);
          ctx.fillStyle = color
          ctx.fill()
        }
        

        【讨论】: