【问题标题】:A few questions about how JavaScript works关于 JavaScript 工作原理的几个问题
【发布时间】:2012-08-31 23:34:47
【问题描述】:

我最近一直在深入研究 JavaScript 以完全理解该语言,并且有一些我似乎无法找到答案的烦人问题(特别是处理面向对象的编程)。

假设如下代码:

function TestObject()
{
    this.fA = function()
    {
        // do stuff
    }

    this.fB = testB;

    function testB()
    {
        // do stuff
    }
}

TestObject.prototype = {
    fC : function 
    {
        // do stuff
    }
}

函数fAfB有什么区别?他们的行为范围和潜在能力是否完全相同?这只是惯例还是一种技术上更好或更合适的方式?

如果在任何给定时间只有一个对象的实例,那么在原型中添加一个函数,例如fC,是否值得?这样做有什么好处吗?只有在处理对象或继承的许多实例时,原型才真正有用吗?

从技术上讲,按照我上面的方式或每次调用TestObject.prototype.functionName = function(){} 向原型添加方法的“正确”方式是什么?

我希望让我的 JavaScript 代码尽可能简洁易读,但我也对该语言中对象的正确约定非常感兴趣。我来自 Java 和 PHP 背景,我试图不对 JavaScript 的工作方式做出任何假设,因为我知道基于原型的 JavaScript 是非常不同的。

【问题讨论】:

  • Javascript != 面向对象。正如您(正确地!)观察到的那样,Javascript 可以说是 更好 被描述为“原型”语言。强,强书推荐:Javascript, the Good Parts, Douglas Cockford。恕我直言...
  • 好问题,问错地方了。你最好在programmers.stackexchange.com 中问这种问题
  • paulsm4:我明白这一点,为了完整起见,我也只是想从这个角度理解 JavaScript。并感谢您的书籍​​建议! @JeremyJStarcher:谢谢!老实说,我什至不知道它的存在。
  • @paulsm4 - 为什么 JavaScript 不是面向对象的?语言不必具有类和基于类的继承才能被视为 OO。
  • @KayoticSully 您不应该在 Stack Exchange 网站上发布相同的问题。选择一个站点并运行它。 “购物问题”是不受欢迎的。您还在这里发布了这个问题:programmers.stackexchange.com/questions/164029/…

标签: javascript oop function prototype


【解决方案1】:

函数fA和fB有什么区别

实际上,什么都没有。函数表达式 (fA) 和函数声明 (fB) 之间的主要区别在于创建函数的时间(声明的函数在执行任何代码之前可用,而函数表达式在表达式实际执行之前不可用)。还有各种与function expressions 相关的怪癖,您可能会偶然发现。

在示例中,我将使用函数表达式,只是因为声明一个函数表达式,然后分配结果似乎有点抽象。但是这两种方法都没有“正确”或“错误”之分。

如果在任何给定时间只有一个对象的实例,那么在原型中添加一个函数(例如 fC)是否值得?

没有。几乎所有进行继承的人都发现普通对象通常更简单,因此“更好”。不过,原型继承对于修补内置对象非常方便(例如,在不存在的地方添加 Array.prototype.each)。

从技术上讲,向原型添加方法的“正确”方式是什么……

没有。用其他对象替换默认原型似乎有点浪费,但是分配由文字创建的对象可能更整洁,更容易阅读顺序分配。对于一两个任务,我会使用:

 Constructor.prototype.method = function(){…}

但是对于很多方法我会使用对象字面量。有些人甚至使用经典的扩展​​功能并这样做:

myLib.extend(Constructor.prototype, {
    method: function(){…}
});

如果已经定义了一些方法,这有利于添加方法。

看看一些库,然后决定你喜欢什么,一些混合搭配。做任何适合特定情况的事情,通常只需获得足够多的代码以使所有代码看起来都一样,那么无论您选择什么模式,它都会看起来很整洁。

【讨论】:

    【解决方案2】:

    fAfB 实际上是相同的,只是约定问题。

    如果对象只有一个实例,我什至不会使用构造函数,而只是使用对象字面量,例如:

    var o = {
       fA: function () { ... },
       fB: function () { ... },
       fC: function () { ... }
    };
    

    至于将其添加到实例或原型中,如果您只有一个实例,则实例比将其添加到原型中效率稍高一些,但正如我所说,请改用字面量。

    我避免在构造函数中声明函数,因为构造函数的每次调用都会创建代表每个函数的新对象。这些对象不是很大,如果创建了许多对象,它们往往会加起来。如果可以将函数移到原型中,那么这样做会更有效率。

    至于加入原型,我赞成

    TestObject.prototype.functionName = function () { };
    

    风格,但这是一个偏好问题。我喜欢上面的内容,因为无论您是扩展原型还是创建初始原型,它看起来都一样。

    【讨论】:

    • 感谢您的意见,您对在原型中添加功能的建议风格提出了非常好的观点。有没有办法在原型中拥有“私有”功能?或者以一种不会在每个实例中都涉及副本的方式保护功能的方法?我不认为有,我一直认为这是权衡,但我想我不妨问问。
    • 试试 Douglas Crockford 的 Private Members in JavaScript
    【解决方案3】:

    还有任何权威的 JavaScript 样式指南或文档 关于 JavaScript 在底层是如何运作的?

    该死的任何 javascript 程序员都不应该错过"Professional JavaScript for Web Developers"。这是一本神奇的书,深入人心。它解释了对象、类仿真、函数、作用域等等。也是一个JS引用。

    从技术上讲,将方法添加到 原型我上面的方式或调用 TestObject.prototype.functionName = function(){} 每次?

    至于定义类的方式,我建议看一下各种 JS MVC 框架(如 Spine.js,它是轻量级的)。您不需要全部,只需要它们的类仿真库。主要原因是JS没有类的概念,而是纯粹由对象和原型组成。另一方面,可以完美地模拟类(请不要使用模拟这个词,因为它缺少一些东西)。由于这需要程序员的一些纪律,最好有一个类仿真库来完成这项工作并使您的代码更清晰。

    程序员应该期望类仿真库的标准方法是:

    // define a new Class
    var AClass = Class.create({
       // Object members (aka prototype), 
       // usually an initialize() method is called if it is defined 
       // as the "constructor" function when a class object is created
    }, {
       // Static members
    }); 
    
    // create a child class that inherits methods and attributes from parent
    var ChildClass = AClass.extend({
       // Child object members
    },{
       // Child static members
    }); 
    
    AClass.include({
       // Object amendments (aka mixin), methods and attributes 
       //   to be injected to the class object
    },{
      // Statics amendments, methods and attributes to be 
      //  injected as class static members
    });
    
    
    // check object type
    var aObj = new AClass();
    aObj instanceof AClass; //  true
    aObj instanceof ChildClass; //  false
    
    
    var cObj = new ChildClass();
    cObj instanceof AClass; //  true
    cObj instanceof ChildClass; //  true
    

    【讨论】:

      【解决方案4】:

      我回答第一部分:没有区别,当你声明函数不是变量时,它的声明会在块中上升,所以

      ...
      func();
      ...    
      function func () { ... }
      

      等于

      var func = function () { ... };
      ...
      func();
      ...
      

      所以你的代码

      function TestObject () {
        this.fA = function() { // do stuff };   
        this.fB = testB;    
        function testB() { // do stuff }
      }
      

      等于

      function TestObject () {
          var testB = function () { // do stuff };    
          this.fA = function () { // do stuff };
          this.fB = testB;
      }
      

      【讨论】:

      • 首先处理的声明与范围无关。
      • 你的英文没问题,“范围”在 ECMAScript 中有一个非常具体的含义。 :-)
      猜你喜欢
      • 2018-09-10
      • 2019-05-19
      • 1970-01-01
      • 2011-06-24
      • 1970-01-01
      • 2022-10-13
      • 1970-01-01
      • 2022-01-22
      • 2011-05-12
      相关资源
      最近更新 更多