JavaScript - 这是一个好的实践吗?

3

在我们公司,我们正在启动一个新的网页项目。我是这家公司的新员工,之前已经有一些应用程序使用以下方式来准备文档(顺便提一下,我们正在使用jQuery)。

var AccountingMovements = function AccountingMovements() {

    //sometimes they use var _that = this;
    var _this = this;

    _this.Constructor = function Constructor() {
        //here goes the code when document is ready
    }
}

var $AccountingMovements = new AccountingMovements();
$(document).ready($AccountingMovements.Constructor);

当我问我的同事为什么要使用这种模式,他们告诉我Javascript代码必须有自己的构造函数,并且代码应该像这样编写。当我询问是否公司规定这样做,他们说没有,没有任何公司标准规定应该这样编写。看起来他们之所以这样做是因为“一直以来都是这样做的”,而不是因为这是常见的Javascript标准化实践或类似的东西。
在我方面,我学习使用IIFE和jquery文档就绪,在很多Javascript大师给出的网页示例中找到了这个方法的实际解释,甚至还有一个维基百科文章。我用这种方式编写我的代码。
(function (code) {
    code(window.jQuery, window, document);
}(function ($, window, document) {
    $(function () {
        //here goes the code when document is ready
    });
}));

我的开发负责人(他对JavaScript并不是很了解,因为他来自桌面开发背景,但在业务逻辑方面很擅长)告诉我,由于这是一个新项目,每当我看到一个新的解决方案、更好的实现或者有助于工作的东西时,我必须毫不犹豫地告诉他,他会和其他领导讨论。所以我告诉他我的方法,并且我不理解旧的构造函数代码的目的,也没有人给我一个令人信服的解释,他告诉我自己去查找支持那种方法的资源,以便在情况需要的时候告诉命令,抛弃另一种方法。
所以经过这一切,我的问题是:
JavaScript构造函数方法有一个名称吗?我的意思是,这是一个实际的标准、实践还是建议吗?因为我找不到任何关于它的信息。它可能是我错了,构造函数方法更好或有效,但因为我没有相关信息,我需要确定才能讨论我的实现。
提前致谢。

2
只是出于兴趣,你为什么不用 (function($, etc) {...})(jQuerry, etc); 这种稍微简单一点的方式呢?这不是一样的吗? - DanielM
似乎他们正在制作自己的构造函数版本,而JavaScript已经内置了构造函数。在我看来,这似乎是一种不好的做法,肯定不是最佳实践。请参阅此处有关JavaScript构造函数的解释。 - Andrew Mairose
1
@DanielM 我本来想回答你的问题,但是Jason Cust已经替我回答了。 - jecarfor
1
JavaScript非常灵活,有很多方法可以解决问题。这导致了几乎总是浪费时间的圣战。我要说你的IIFE方法看起来相当无意义。我的意思是,你只是将一个函数传递给另一个函数,以便你可以使用一堆全局变量来调用它。我知道你想要本地引用,但使用单个IIFE会更简单,就像@DanielM所说的那样。 - user1106925
@AndrewMairose:其实这只是模块模式,但他们使用外部构造函数来创建模块,而不是使用带有对象字面量的IIFE。结果相同,但可能更易于调试,因为如果记录对象本身,则控制台将显示一些构造函数数据。 - user1106925
显示剩余4条评论
4个回答

1

Constructor 这个词在 JavaScript 语言中没有特殊的含义,只有你的同事为它赋予的含义。一些 JS 框架如 Dojo 创建了类似命名的函数,但它们也有专门的、集中的方式来定义对象,并且有集中的位置调用该构造函数(而不是手动在下一行调用,留下错误的空间)。

同时,认为任何 JavaScript 对象都必须等到 DOM 准备好后才能构造自己或许是误导性的。你觉得 JQuery 是怎样创建自己的?(而且它必须很早就创建,在人们编写 $().ready 来执行 onload 函数之前)

我不知道你的同事编写对象的方式是否存在具体问题,只要他们为当前的 Constructor 函数分配了非常不同的语义含义即可。上面的代码,例如 var _this = this 就是构造函数。was-Constructor 函数的内容最好被描述为 onDOM 或类似名称。如果他们更喜欢遵循这种系统,最好将他们的类系统经过一个中央库进行处理;无论是你自己的还是别人的。


@atmd 我可能有点字面意思,但我是在指构造函数 - Katana314
@squint 我的意思就是说;那段代码,也就是那一行,是你的“构造函数”的内容(不是this_this,它只是一个自我引用)。JavaScript类最初只是它们的构造函数(function MyClass(initParam) { this.initParam = initParam; }),然后通常会将原型参数和其他函数附加到它们创建的实例上。 - Katana314
那么这是不正确的。构造函数的内容不仅仅是那一行代码,this_this 不是对构造函数本身的引用,而是对正在被创建的新对象的引用。 - user1106925
@squint 请随意自我辩论;没有人声称this是构造函数,所以听起来你只是一直在误读某些东西。但无论如何,以function AccountingMovements { 开头的代码块都是一个构造函数。如果您还有任何疑惑,请到聊天室继续讨论。 - Katana314
你刚刚称其为自我引用。如果我误解了,对不起,在这个页面上的错误信息太多了,这是一个非常简单的概念,人们似乎很难搞混事情。这种简单的模式根本不需要“中央库”。如果你不想让我回复,那么就不要发送通知给我。 - user1106925
显示剩余2条评论

0
一个很大的区别是全局作用域的污染。由于它们似乎没有在IIFE中包装它们的函数,所有的函数和变量都将可以从全局作用域访问到。如果第三方JS库(甚至是一些内部代码)重新使用了其中任何一个,这可能会导致错误和安全问题。
您提到的方法是一种非常有用的模式,它允许为您的代码指定作用域并指示哪个部分需要等待DOM。
// IIFE to create a closure to prevent global scope pollution
(function(code) {
  // execute `main` function with params
  code(window.jQuery, window, document);
}(function main($, window, document) {
  $(function readyDOM() {
    //here goes the code when document is ready
  });

  // This is the rest of your code that is not dependent on the DOM
  // Such as any object (constructers) declarations
}));

然而,这在功能上等同于:

// IIFE to create a closure to prevent global scope pollution
(function main($, window, document) {
  $(function readyDOM() {
    //here goes the code when document is ready
  });

  // This is the rest of your code that is not dependent on the DOM
  // Such as any object (constructers) declarations
})(window.jQuery, window, document);

这两种模式都可以用作对象声明的包装器。因此,将其与对象声明结合使用:

// IIFE to create a closure to prevent global scope pollution
(function main($, window, document) {
  $(function readyDOM() {
    $AccountingMovements.Constructor();
  });

  // A better pattern for object constructors
  // Assigning properties directly to `this` in the object constructor
  //  makes them enumerable by default. See http://jsbin.com/kuxuyataci/edit?js,console
  function AccountingMovements() {
    //sometimes they use var _that = this;
    var _this = this;
  }
  AccountingMovements.prototype.Constructor = function Constructor() {
    //here goes the code when document is ready
  };

  // var will be hoisted
  var $AccountingMovements = new AccountingMovements();
})(window.jQuery, window, document);

@JasonCust:只是试图打破这个页面上的误导信息。我并不是在通过说一种方法比另一种更好来参与圣战。像你这样的其他人正在提出似是而非的声明,比如 var _that = this 是一种反模式。那么如果我做 var _that = {}; 并在其上放置一些具有可能引用它的函数的属性,然后返回它,那也是一种反模式吗? - user1106925
@jecarfor 我不记得有任何东西支持他们的说法。为了更好地理解对象构造函数模式,我建议你阅读You Don't Know JS: this & Object Prototypes或至少第三章:https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/ch3.md - Jason Cust
@JasonCust,但是构造函数方法(我发布的那个)存在哪些安全问题? - jecarfor
@squint,我认为我的观点并不牵强,但正如你所说,我试图揭示一般具有误导性的信息。我恳请您至少阅读关于词法 this的部分,并查看是否仍然认为这是一个牵强的论点。 - Jason Cust
我理解JS中的this是如何工作的。新的ES6函数语法及其词法绑定的this与问题有什么关系?事实是,您正在做出关于如何使用_that变量的假设。它很可能是方法将依赖于自己动态分配的this值,使.bind()成为错误的解决方案。调用创建对象并分配属性并返回该对象的函数是一种常见模式。上面的代码正是这样做的,只是使用构造函数而不是具有IIFE的对象文字。 - user1106925
显示剩余11条评论

0

他们想要实现的是一个“应用程序引导程序”或“启动按钮”,当页面准备就绪时,他们调用Constructor,但更合适的名称是initinitialize方法。

在我看来,这是一种非常糟糕的应用程序基础/结构方式。这也可以用作模块启动器等。

使用_this作为对主构造函数/函数/对象的引用是一个好习惯,因为很快你就会进入闭包,而你需要引用主模块。

Constructor我想这只是一个占位符,在这里你永远不应该给变量命名为那样。 AccountingMovementsConstructor更好。

你贴在这里的实际上并不是一个模式,只是一个模块内的初始化方法。

搜索Javascript设计模式并了解更多相关信息。

请注意,让一个只有Java或其他不同语言经验的人架构大规模JavaScript应用程序:最糟糕的想法。

作为上述代码的替代方案:

// Define your module

(function(w) {
    w.namespace.myModule = {
        init: function() {
            console.log('hey, Im ready to rock');
        },

        // Usually you have one of these also
        destroy: function() {
            console.log('good bye world...');
        }
    }
}(window));

// Fast alternative to $(document).ready(...)
$(function() {
    window.namespace.myModule.init(); // hey, Im ready to rock
});

你可以使用一个工厂函数来分离公共和私有变量。但是我不会在这里详细介绍,这是你可以使用谷歌解决的问题 :P

我认同你的某些观点,但是我不赞成使用 _this 或类似的方式。 var XXX = this 在大多数情况下都是反模式。这源于对 JS 中 this 的工作原理缺乏了解。大多数使用此模式的解决方案可以改用 bind 来替代。如果想深入了解更多原因,我建议阅读:You Don't Know JS: Scope & Closures 。特别是关于 this 的章节:https://github.com/getify/You-Dont-Know-JS/blob/f07e52b93c688d9fdc9e579fdc2dafec5c9c5bbb/this%20%26%20object%20prototypes/ch2.md - Jason Cust
当然,您将使用bind,但是拥有this,self,that变量是另一回事。您的答案是正确的,但与此问题无关。当继承和事情变得复杂时,它会派上用场:P它们总是会变得复杂,我在这里说什么:D - Starmetal

0

你们可能有些误解。

你提供的代码片段,基本上是JS声明类的一种方式。当然,仅仅为了在ready事件上执行一堆代码,这样做有些过度设计。但是随着项目的发展,你需要对其进行结构化处理。这是其中的一种方法,没有最好的方法。这种方法对于来自像Java这样的纯面向对象语言的程序员来说很熟悉,这些语言鼓励(甚至强制)面向对象的架构。

由于JavaScript没有内置的类声明语法(至少在ES2015之前的实现中没有),因此出现了多种方法来实现。我看到的所有方法都涉及到构造函数的声明。你的代码也不例外:虽然我认为它并不理想,但构造函数中的构造代码本身就可以代表一个类。我不明白为什么你要将构造过程分成两个不同的步骤:一旦构造函数完成了它的工作,对象应该准备好使用了,单独的构造步骤只会增加不必要的复杂性。

例如,这里是CoffeeScript(粗略地说,是JS的语法层)如何实现这一点:

# CoffeeScript
class Hi
  # A typical method
  # CS is indenetation-sensitive: class contents and
  # function implementations are indented one more level
  # -> denotes a function
  method: ->
    @a += 1

  # `constructor` is a special identifier in CS
  # code from that function goes into an unusual place
  constructor: ->
    @a = 5

这是输出结果:

// Generated JavaScript
// Entire program is wrapped in IIFE, for isolation.
// Not relevant to the question, but since it's in the output
// I want to clarify why it's in there.
(function() {
  var Hi;

  Hi = (function() {
    Hi.prototype.method = function() {
      return this.a += 1;
    };

    function Hi() {
      this.a = 5;
    }

    return Hi;

  })();

}).call(this);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接