【问题标题】:Why is this JS code working most places but not on my machine?为什么这个 JS 代码在大多数地方都可以工作,但在我的机器上却不行?
【发布时间】:2018-06-06 23:47:12
【问题描述】:

我编写了一些代码,在 repl.it (https://repl.it/repls/LimpingCharmingGravity) 提供的 Node 环境中运行良好,在此处的代码 sn-p (见下文) 和 codepen.io (https://codepen.io/tjfwalker/pen/OERXry?editors=0012#0) 上运行良好。但是,在我的机器上使用 Node 时它不起作用。尝试运行会产生以下错误消息:

ReferenceError: Entity is not defined
    at Entity.eval [as addSub] (eval at <anonymous> (/Users/…pathtofile…/app.js:18:69), <anonymous>:3:11)
    at Object.<anonymous> (/Users/…pathtofile…/app.js:60:20)
    at Module._compile (internal/modules/cjs/loader.js:702:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:713:10)
    at Module.load (internal/modules/cjs/loader.js:612:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:551:12)
    at Function.Module._load (internal/modules/cjs/loader.js:543:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:744:10)
    at startup (internal/bootstrap/node.js:238:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:572:3)

我的本​​地节点是 10.4.0,通过 macOS 上的 nvm...不过,我想这不是那个。

什么给了?

let activeUserMock = 'someUserName'

const Entity = (function() {
    let lastID     = 0
      , entityList = []
    
    function Entity(userFields, userMethods) {
        this.meta = {id: ++lastID, dob: new Date, creator: activeUserMock}
        
        userFields.forEach(
            function(field) {
                this[field.key] = field.value
            }
        ,this)
        
        userMethods.forEach(
            function(method) {
                this[method.name] = Function(...method.args, method.body)
            }
        ,this)
        
        entityList.push(this)
    }
    
    Entity.findByID = function(id) {
        return entityList.find(function(entity) {
            return entity.meta.id === id
        })
    }
    
    return Entity
})();

// ======= SIMULATE AN END USER —FROM SOME CLIENT UI— MODELING THEIR OWN DOMAIN ==========

new Entity([ //stuff from the user ↴
    {key: 'type', value: 'feature'}
   ,{key: 'name', value: 'LMS'}
   ,{key: 'subs', value: []}
   ,{key: 'sups', value: []}
   ,{key: 'desc', value: 'a module to facilitate learning.'}
],[
    {name: 'addSub', args: ['subID'], body: 'let sub = Entity.findByID(subID); this.subs.push(sub); sub.sups.push(this); return this'}
   ,{name: 'addSup', args: ['supID'], body: 'let sup = Entity.findByID(supID); this.sups.push(sup); sup.subs.push(this); return this'}
])

new Entity([ //stuff from the user ↴
    {key: 'type', value: 'feature'}
   ,{key: 'name', value: 'SRS'}
   ,{key: 'subs', value: []}
   ,{key: 'sups', value: []}
   ,{key: 'desc', value: 'a module that implements the spaced repetition learning technique.'}
],[
    {name: 'addSub', args: ['subID'], body: 'let sub = Entity.findByID(subID); this.subs.push(sub); sub.sups.push(this); return this'}
   ,{name: 'addSup', args: ['supID'], body: 'let sup = Entity.findByID(supID); this.sups.push(sup); sup.subs.push(this); return this'}
])

Entity.findByID(1).addSub(2)

// ==========================================================

console.log(Entity.findByID(1))

【问题讨论】:

  • 你是如何运行代码的?
  • @J.Pichardo node app.js 来自与该文件相同的目录
  • 从表面上看,在我看来这个问题需要更多的细节,比如你的环境(你的 js 运行的是什么?它是什么版本?)
  • 是的,我同意@CollinD
  • @J.Pichardo & @CollingD 这与节点版本无关,只是Function 只能访问全局范围,并且在任何节点版本中,模块都不在全局范围内,除非你使用global.someVar

标签: javascript node.js iife function-constructor


【解决方案1】:

eval() 不同,Function() 构造函数无法访问调用范围,它只能访问全局范围。

该脚本将在节点中失败,因为每个模块都有自己的范围,而 Entity 不在全局命名空间中。

要检查这一点,只需将您的代码包装在 IIFEE 中,您就会看到它在浏览器上也会失败。 (见下面的sn-p)

要在节点中“修复”您的代码,您需要执行以下操作:

global.Entity = (function() {
 /* ... */
})():

但您可能应该重新考虑您的方法,改用bind,并使用this 访问Entity

以下内容也会在浏览器中失败:

(function() {
  let activeUserMock = 'someUserName'

  const Entity = (function() {
      let lastID     = 0
        , entityList = []

      function Entity(userFields, userMethods) {
          this.meta = {id: ++lastID, dob: new Date, creator: activeUserMock}

          userFields.forEach(
              function(field) {
                  this[field.key] = field.value
              }
          ,this)

          userMethods.forEach(
              function(method) {
                  this[method.name] = Function(...method.args, method.body)
              }
          ,this)

          entityList.push(this)
      }

      Entity.findByID = function(id) {
          return entityList.find(function(entity) {
              return entity.meta.id === id
          })
      }

      return Entity
  })();

  // ======= SIMULATE AN END USER —FROM SOME CLIENT UI— MODELING THEIR OWN DOMAIN ==========

  new Entity([ //stuff from the user ↴
      {key: 'type', value: 'feature'}
     ,{key: 'name', value: 'LMS'}
     ,{key: 'subs', value: []}
     ,{key: 'sups', value: []}
     ,{key: 'desc', value: 'a module to facilitate learning.'}
  ],[
      {name: 'addSub', args: ['subID'], body: 'let sub = Entity.findByID(subID); this.subs.push(sub); sub.sups.push(this); return this'}
     ,{name: 'addSup', args: ['supID'], body: 'let sup = Entity.findByID(supID); this.sups.push(sup); sup.subs.push(this); return this'}
  ])

  new Entity([ //stuff from the user ↴
      {key: 'type', value: 'feature'}
     ,{key: 'name', value: 'SRS'}
     ,{key: 'subs', value: []}
     ,{key: 'sups', value: []}
     ,{key: 'desc', value: 'a module that implements the spaced repetition learning technique.'}
  ],[
      {name: 'addSub', args: ['subID'], body: 'let sub = Entity.findByID(subID); this.subs.push(sub); sub.sups.push(this); return this'}
     ,{name: 'addSup', args: ['supID'], body: 'let sup = Entity.findByID(supID); this.sups.push(sup); sup.subs.push(this); return this'}
  ])

  Entity.findByID(1).addSub(2)

  // ==========================================================

  console.log(Entity.findByID(1))
})();

更新

按照您的建议,具体的代码是什么样的 改为“使用绑定,并使用此访问实体

this[method.name] = Function(...method.args, method.body).bind(this)

然后使用:

body: 'let sub = this.constructor.findByID(subID) ...'

代替:

body: 'let sub = Entity.findByID(subID) ...'

【讨论】:

  • 鉴于您的回答是准确的,您能帮我理解为什么代码似乎在其他环境中按原样工作,例如我链接到的 repl.it 的节点环境?
  • 他已经解释了原因,这与范围有关。Repl 不是模块,因此您可以再次访问全局范围。你的app.js 是一个模块..
  • 那么,Marcos,按照您的建议“改用bind,并使用this 访问Entity”的代码具体是什么样的?
猜你喜欢
  • 1970-01-01
  • 2023-03-10
  • 2011-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多