【问题标题】:I'm having a lot of trouble trying to modify a "this" object from within a pseudo javascript class when it's already been initialized当它已经被初始化时,我在尝试从伪 javascript 类中修改“this”对象时遇到了很多麻烦
【发布时间】:2012-03-15 23:50:58
【问题描述】:

向下滚动到这篇文章的底部,查看解决方法/可能的解决方案。

这可能更容易在源代码中用 cmets 解释。手头的问题是我无法弄清楚伪类如何协同工作来执行我正在尝试执行的任务(在下面的代码中进行了解释)。

代码分为 3 个文件:lead.js、router.js 和 db.js。

有相当多的代码行,但大部分是 cmets。

[lead.js]

var bcrypt = require('bcrypt'),
    validators = require('../lib/validators'),
    utility = require('../lib/utility'),
    document = {};

var Lead = module.exports = function (db) {
  // Save a reference to the database.
  this.db = db;

  // Reference initial document.
  // This is totally wrong, not sure how to 'send' a variable to the constructor of a class
  // when I cannot add another param. Due to how I'm importing the db model, I won't know what
  // the document is until I fill out the form. I've also tried 'document' instead of 'Lead.document'.
  this.document = Lead.document;

  // Setup the document if it exists.
  // This also doesn't work.
  // Basically I want to be able to set up a document variable outside of this module (line #100),
  // Then pass it to this module after filling it up with values from a form.
  // Then based on what's been filled in, it would fix up (trim, convert to lower case)
  // some of the values automatically and default a few values that I'm not always going to pass.
  if (!document) {
    var salt = bcrypt.genSaltSync(10),
        hash = bcrypt.hashSync(utility.generatePassword(), salt);

    // Default values.
    if (!document.meta.createdAt) { this.document.meta.createdAt = Date.now(); }
    if (!document.login.password) { this.document.login.password = hash; }
    if (!document.login.role) { this.document.login.role = 'User'; }

    // Normalize a few values.
    this.document.login.email = document.login.email.toLowerCase().trim();
    this.document.contact.name.first = document.contact.name.first.trim();
    this.document.contact.name.last = document.contact.name.last.trim();
    this.document.contact.address.street = document.contact.address.street.trim();
    this.document.contact.address.city = document.contact.address.city.trim();
    this.document.contact.address.state = document.contact.address.state.trim();
    this.document.contact.address.zip = document.contact.address.zip.trim();
    this.document.contact.phone.home = document.contact.phone.home.trim();
  }
  // So in regards to the above code, the end result I'm looking for is...
  // I want to append some properties to the this.document reference when the document is empty (when I'm updating it, I won't set the document), 
  // and on new documents it will append a few default values/normalize all the fields.
};

Lead.prototype.validate = function(fn) {
  var errors = [];

  // Some validation rules I cut out to make this shorter.

  if (errors.length) return fn(errors);
  fn();
};

Lead.prototype.save = function(fn) {
  this.db.collection('leads', function(err, collection) {
    if (err) { fn(new Error({message: err})); }

    collection.insert(this.document, function(err, result) {
      return fn(err, result);
    });
  });
};

---

[route.js file]

  var db = require('../models/db');

  app.post('/register', function(req, res) {
    var data = req.body.lead || {};

    // Fill the document.
    var document = {
      meta: {
        host: req.headers.host,
        referer: req.headers.referer,
        createdIPAddress: req.connection.remoteAddress
      },
      login: {
        email: data.email
      },
      contact: {
        name: {
          first: data.first,
          last: data.last
        },
        address: {
          street: data.street,
          city: data.city,
          state: data.state,
          zip: data.zip
        },
        phone: {
          home: data.phone
        }
      }
    };

    // Write the document.
    db.lead.document = document;

    db.lead.validate(function(err) {
      if (err) {
        req.session.error = err;
        return res.redirect('back');
      }

      db.lead.save(function(err) {
        res.redirect('/register/success');
      });
    });
  });

---
[db.js]

var mongodb = require('mongodb'),
    server = new mongodb.Server('localhost', 27017),
    connection = new mongodb.Db('test', server);

connection.open(function(err, db) {});

module.exports =  {
  lead: new (require('./lead'))(connection)
};

当我运行它时,我的验证器总是报告密码为空,这是有道理的。我最初使用空密码将文档发送到班级(密码是随机生成的,而不是表单字段)-问题是我不知道如何处理 if (!document) ... 代码块正确设置 this.document。

我希望在 cmets 和代码之间,您可以了解我想要做什么。我已经被困了一段时间了。

编辑

我稍微改变了它的流程以获得解决方案。

在 db.js 中,我导出了连接,而不是直接实例化线索(和未来的模型)。

在 router.js 文件中,我需要 db 和 Lead 文件,然后在 Lead 的构造函数中传递 db 连接和文档。例如。

var lead = new Lead(db, document);

在lead.js文件中,就变成了这样简单的this.document = document(和db一样)。当我提交新的潜在客户时,我未从 router.js 发送的值会附加到文档中(创建日期、随机密码等),一切都很好。

这是处理这个问题的好方法,还是有更好的重构方法?

【问题讨论】:

    标签: javascript node.js module express commonjs


    【解决方案1】:

    即使使此代码按您的意愿工作,这也是完全错误的方式。在此示例中,您有单例潜在客户。通过请求 /register url,您希望将 'document' 字段设置为此单例。 (重要)但是请求异步工作。绝对不能保证您保存刚刚验证过的文档。因为新请求可能会在引导对象中覆盖它。您需要在请求范围内执行此逻辑。一个请求的一个范围。不是一个适合所有人的。

    【讨论】:

    • 嗯,我什至没有想到这一点。是的,正在创建/需要潜在客户,但从未为每个请求设置为“新”——它只设置一次(我猜是单例?)。你认为我应该只在 db.js 文件中导出连接,然后在路由中实例化一个新的 Lead 实例吗?
    • 看看我的form 模块。我认为它应该对你有所帮助。我使用这个流程: 1. 将请求数据传递给表单(来自控制器)。 2. 表单验证数据并在需要时进行更改。 3.表单返回100%信任数据或错误。 4. 将数据传递给模型(保存、更新等)。模型文件如下所示:module.exports = function(db) { return { // public methods}; }
    • 您认为我编辑的解决方案是否合理或者可以以更好的方式完成?它解决了这两个问题(我认为)。
    【解决方案2】:

    你需要read up on object-oriented programming in Javascript

    您在代码顶部附近定义的匿名函数构造函数,因此对于您想要的当前未初始化的 document 属性,只需键入以下内容:

    this.document = null;
    

    然后一段时间后,当您使用此构造函数创建一个新对象时,如下所示:

    var myLead = new Lead(dbConnection);
    

    您将拥有myLead.document 属性。

    不过,您的代码还有许多其他问题。当它被定义为{} 时,你为什么假设有一个全局变量document 在你的库中可见相关数据?当您在下面的其他文件中设置了document 属性时,应该运行构造函数末尾的if 语句中的代码,并且应该只期望this.document 存在。

    【讨论】:

    • 如果我想在设置文档后运行该 if 语句(构造函数的结尾),但又不想让该代码块驻留在我的另一个文件中,那么最好的行动计划是什么?正确设置?
    • 简单的方法是将myLead.document = myDoc 替换为myLead.setDocument(myDoc) 作为Lead 的一种在内部设置文档并执行帮助代码的方法。更复杂的方法是使用新的getters and setters in Javascript 在访问对象的属性时隐式运行函数。
    • 我在 Lead 上玩弄 .setDocument(doc) 方法的原型,但它似乎没有生效,这是因为代码的其他部分有多残缺吗?
    • 可能是这样。我是否可以建议您尝试使用Mongoose,而不是重新发明轮子(一个自定义 ORM,它对 MongoDB 集合的文档执行验证),它完全为您处理非平凡的验证,并且相对简单延长? (它还包含 MongoDB 库,因此您也不需要包含它。)
    • 在那里做过。我在使用 Mongoose 几天后得出的结论是,您必须越过障碍才能使验证器的输出合理,而且 irc 频道和邮件组就像一座鬼城。我不想太疯狂并试图重新发明猫鼬。我只想通过验证访问模型上的 CRUD 操作。理想情况下,我希望这些操作驻留在一些通用数据库模块中,然后我可以在我的任何集合模型中执行 db.findOne('collectionName', function(err) ...) 之类的操作。
    【解决方案3】:

    您最初设置了var document = {},并且{} 不是假的。最好将document = null 设置为起始值,然后在检查!document 之后设置document = {},然后再分配您需要的任何属性。

    【讨论】:

    • 我最初将它设置为 {} 因为 null 使我的脚本抛出错误,它抱怨试图在 'document.meta.createdAt' 上设置 x 但它未定义。感谢您的提示。
    猜你喜欢
    • 2022-12-06
    • 2017-05-30
    • 2011-08-20
    • 1970-01-01
    • 1970-01-01
    • 2022-11-25
    • 2021-12-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多