1

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

4

1 回答 1

2

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

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

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

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

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

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

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

(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) ...'
于 2018-06-07T00:11:59.843 回答