为什么这个 JS 代码在大多数地方都能工作,但在我的机器上却不行?
Why is this JS code working most places but not on my machine?
我写了一些代码,在 repl.it (https://repl.it/repls/LimpingCharmingGravity), in the code snippet here (see below), and on codepen.io (https://codepen.io/tjfwalker/pen/OERXry?editors=0012#0) 提供的 Node 环境中运行良好。但是,它无法在我的机器上使用 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))
与eval()
不同,Function()
构造函数无法访问调用范围,它只能访问全局范围。
该脚本将在节点中失败,因为每个模块都有自己的范围,并且 Entity
不在全局命名空间中。
要检查这一点,只需将您的代码包装在一个 IIFEE 中,您会发现它在浏览器上也会失败。 (见下面的片段)
要"fix"你在节点中的代码,你需要做:
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) ...'
我写了一些代码,在 repl.it (https://repl.it/repls/LimpingCharmingGravity), in the code snippet here (see below), and on codepen.io (https://codepen.io/tjfwalker/pen/OERXry?editors=0012#0) 提供的 Node 环境中运行良好。但是,它无法在我的机器上使用 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))
与eval()
不同,Function()
构造函数无法访问调用范围,它只能访问全局范围。
该脚本将在节点中失败,因为每个模块都有自己的范围,并且 Entity
不在全局命名空间中。
要检查这一点,只需将您的代码包装在一个 IIFEE 中,您会发现它在浏览器上也会失败。 (见下面的片段)
要"fix"你在节点中的代码,你需要做:
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) ...'