Почему этот код JS работает в большинстве мест, но не на моей машине?
Я написал некоторый код, который отлично работает в env-узле, предоставленном repl.it ( https://repl.it/repls/LimpingCharmingGravity), во фрагменте кода здесь (см. Ниже) и в codepen.io ( https://codepen.io/tjfwalker/pen/OERXry?editors=0012). Это не работает, однако, с 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 через nvm в macOS... хотя, я думаю, дело не в этом.
Что дает?
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))
1 ответ
В отличие от 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))
})();
ОБНОВИТЬ
как конкретно выглядит код, соответствующий вашей рекомендации "использовать вместо этого связывание и получить доступ к Entity, используя это
this[method.name] = Function(...method.args, method.body).bind(this)
А затем используйте:
body: 'let sub = this.constructor.findByID(subID) ...'
Вместо:
body: 'let sub = Entity.findByID(subID) ...'