#javascript #javascript-objects #javascript-scope
#javascript #javascript-объекты #javascript-область
Вопрос:
Я пытаюсь уменьшить глобальные переменные в моем расширении Chrome, чтобы уменьшить «спагетти» или двусмысленность в моем JavaScript.
Я пытаюсь сделать это, используя функцию init, которая объявляет эти глобальные переменные в противном случае в функции обратного вызова mousedown EventListener, которая, вероятно, будет запущена более одного раза.
Это делается для того, чтобы я мог передавать эти переменные события другим обратным вызовам EventListener (а именно обратному вызову mouseup) в качестве аргументов таких обратных вызовов.
Я выделил проблему в отдельный файл:
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
</head>
<body>
<script src="test.js"></script>
</body>
</html>
test.js
isVarsInitOnce = false;
function executesMoreThanOnce() {
const initVariables = function() {
console.log('runs once');
const bools = {
boolOne: false,
boolTwo: false,
boolThree: false
};
const events = {
clickEvent:
function(event) {
//do stuff
},
keyEvent:
function(event) {
//do other stuff
}
};
return {
bools: bools,
events: events
}
};
if (!isVarsInitOnce) {
isVarsInitOnce = true;
let vars = initVariables();
var booleanObject = vars.bools;
var eventObject = vars.events;
}
console.log('objects: ', booleanObject, eventObject);
//attempting to access initialised variable after function is executed more than once will cause an error.
//this is because the booleanObject is now undefined.
booleanObject.boolOne = true;
}
//runs twice
for (let i=0; i<2; i ) {
executesMoreThanOnce();
}
Метод, который я использовал для управления выполнением initVariables()
, — это глобальная логическая переменная, isVarsInitOnce
которая эффективна при инициализации переменных и настройке объекта для использования в executesMoreThanOnce()
функции один раз.
Доступ к объектам возможен в первом экземпляре, когда функция вызывается в for
цикле, однако объекты undefined
появляются, когда к ним пытаются получить доступ во вторичном экземпляре, когда функция вызывается в for
цикле.
Это довольно четко обозначено в выводе консоли:
runs once
test.js:38 objects: {boolOne: false, boolTwo: false, boolThree: false} {clickEvent: ƒ, keyEvent: ƒ}
test.js:38 objects: undefined undefined //<--- (function called 2nd time)
test.js:42 Uncaught TypeError: Cannot set property 'boolOne' of undefined
at executesMoreThanOnce (test.js:42)
at test.js:47
Я не уверен, почему это происходит.
Кто-нибудь может помочь мне понять, почему это не работает должным образом?
У кого-нибудь есть лучшее предложение по сокращению глобальных переменных в отношении моего случая?
Большое спасибо.
Ответ №1:
Сначала оба booleanObject и EventObject должны быть объявлены вне оператора if, чтобы быть в области видимости. Во-вторых, если вам нужно запускать executesMoreThanOnce более одного раза, вам нужно установить для isVarsInitOnce значение false в цикле for
let isVarsInitOnce = false;
function executesMoreThanOnce() {
const initVariables = function() {
console.log('runs once');
const bools = {
boolOne: false,
boolTwo: false,
boolThree: false
};
const events = {
clickEvent:
function(event) {
//do stuff
},
keyEvent:
function(event) {
//do other stuff
}
};
return {
bools: bools,
events: events
}
};
let booleanObject ; // declaring booleanObject outside the if statement
let eventObject ; // declaring eventObject outside the if statement
if (!isVarsInitOnce) {
isVarsInitOnce = true;
let vars = initVariables();
booleanObject = vars.bools;
eventObject = vars.events;
}
console.log('objects: ', booleanObject, eventObject);
//attempting to access initialised variable after function is executed more than once will cause an error.
//this is because the booleanObject is now undefined.
booleanObject.boolOne = true;
}
//runs 5 times
for (let i=0; i<5; i ) {// Will execute 5 times
executesMoreThanOnce();
isVarsInitOnce = false;
}
Редактировать
Извините, я не совсем понял ваши требования. Проверьте следующее:
JavaScript
// This will be the object that holds your variables
let vars;
function executesMoreThanOnce(obj) {
const initVariables = function(obj) {
console.log('runs once and obj = ' obj);
if (obj == undefined) {
console.log('initialize once');
const bools = {
boolOne: false,
boolTwo: false,
boolThree: false
};
const events = {
clickEvent:
function(event) {
//do stuff
},
keyEvent:
function(event) {
//do other stuff
}
};
return (function(){
return {bools: bools,
events: events};
})();
}
// If vars object, "which we passed as argument", is initialized once before just return it
return vars;
};
vars = initVariables(vars);
let booleanObject = vars.bools;
let eventObject = vars.events;
console.log('objects: ', booleanObject, eventObject);
//attempting to access initialised variable after function is executed more than once will cause an error.
//this is because the booleanObject is now undefined.
// Yes you can now access the variables
booleanObject.boolOne = true;
}
//runs 5 times
for (let i=0; i<5; i ) {// Will execute 5 times
// Removed the bool flag and now passing the vars object as argument
executesMoreThanOnce(vars);
//executesMoreThanOnce();
//isVarsInitOnce = false;
}
Комментарии:
1. Затем переменные сбрасываются при каждом вызове функции executesMoreThanOnce . Я хочу инициализировать эти переменные только один раз при первом вызове функции
Ответ №2:
Я просмотрел некоторые сведения о области видимости и закрытии в JS, и я думаю, что нашел решение разложенной проблемы в моем первоначальном вопросе.
Ответ, данный Абером Абу-Рахмой, не решает мою проблему, потому что переменные сбрасываются при вызове функции executesMoreThanOnce()
.
Это не то, что я искал, потому что я хотел, чтобы переменные были изначально установлены только в первом случае, когда вызывается функция (потому что в моем реальном проекте executesMoreThanOnce()
функция по существу представляет собой обратный вызов события click, который требует сохранения данных при повторном запуске события).
В моем решении используется выражение функции, вызываемое немедленно (IIFE). для локальной инициализации переменных в области IIFE и освобождения переменных в глобальную область в возвращаемом get
методе:
test.js
const TEST = (function() {
let booleans = {
boolOne: false,
boolTwo: false,
boolThree: false
};
let events = {
clickEvent:
function(event) {
//do stuff
},
keyEvent:
function(event) {
//do other stuff
}
};
return {
executesMoreThanOnce: function(booleans, events, index) {
booleanObject = booleans;
eventsObject = events;
if (i == 2) {
booleanObject.boolTwo = true;
}
else if (i == 4) {
booleanObject.boolOne = true;
booleanObject.boolTwo = false;
}
console.log('booleanObject: ', booleanObject);
},
get variables() {
return {
booleans,
events
}
}
};
}());
for (var i=0; i<5; i ) {
TEST.executesMoreThanOnce(TEST.variables.booleans, TEST.variables.events, i);
}
Теперь вы можете видеть в консоли, что TEST.executesMoreThanOnce()
функция начинает использовать начальные переменные, определенные локально в TEST
функции IIFE:
i = 0 | booleanObject: {boolOne: false, boolTwo: false, boolThree: false}
i = 1 | booleanObject: {boolOne: false, boolTwo: false, boolThree: false}
i = 2 | booleanObject: {boolOne: false, boolTwo: true, boolThree: false}
i = 3 | booleanObject: {boolOne: false, boolTwo: true, boolThree: false}
i = 4 | booleanObject: {boolOne: true, boolTwo: false, boolThree: false}
Теперь мы также можем видеть, что как только значение i соответствует определенным условиям в TEST.executesMoreThanOnce()
функции, логические значения начинают переключаться, но, что более важно, эти изменения сохраняются между вызовами функции.
Я все еще не уверен, что полностью понимаю абстракцию под IIFE. но я могу четко видеть в коде, какие переменные относятся к области, в которой они используются (улучшая читаемость). Это было моей целью в первую очередь.
Если кто-нибудь хотел бы поправить меня в чем-то, что я неправильно понял, пожалуйста, прежде чем я начну пытаться внедрить это в свой проект.