javascript - 为什么这个 JS 代码在大多数地方都能工作,但在我的机器上却不行?

标签 javascript node.js iife function-constructor

我写了一些代码,这些代码在 repl.it ( https://repl.it/repls/LimpingCharmingGravity ) 提供的 Node env 中、此处的代码片段(见下文)和 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)

我的本​​地 Node 是 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))

最佳答案

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

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

要检查这一点,只需将您的代码包装在一个 IIFEE 中,您会发现它在浏览器上也会失败。 (见下面的片段)

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

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))
})();

更新

what concretely does code look like that follows your recommendation to "use bind instead, and access Entity using this

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

然后使用:

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

代替:

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

关于javascript - 为什么这个 JS 代码在大多数地方都能工作,但在我的机器上却不行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50731154/

相关文章:

javascript - Node JS 中以字符串形式获取另一个程序的连续输出

javascript - 使用 Api Gateway 创建 session 并重定向

javascript - 这个 IIFE switch 语句是否会导致不必要的开销?

javascript - 将对象与模块模式组合时我做错了什么

javascript - 错误连接 MongoDB : Error: Route. get() 需要一个回调函数,但得到一个 [对象未定义]

javascript - 当数组中的任何项目发生变化时,如何使 Ember 计算属性更新?

node.js - 索引.ejs VS 索引.html

javascript - 计数器在循环中神秘地递增

javascript - 在 Javascript 中向字符串添加格式方法。

javascript - 在 IIFE 中使用实例属性