Scenario: I am developing a chrome extension and I have a bunch of listeners I need to add in the foreground.
What I would like to do: create an object named 'listeners' containing only functions (functions that will run addListeners), and a function called 'init' that would iterate my 'listeners' object and dynamically execute every function.
Why: I would like to add new listeners to the object without worrying about having to call them directly one by one in my init function. I know it would not be too much of a hassle doing so but it would be interesting if I could make the thing more dynamic.
Is this possible?
Something like:
const listeners = {
func1: function (){...},
func2: function (){...},
func3: function (){...}
}
function init(){
for (let func in listeners){
//somehow execute func
//func() appearently does not work
//()=>func appearently does not work
}
}
init();
The for...in loop iterates the keys of the object, so the func variable is a string, and not a function. To run the function use listeners[func]();.
You can use Object.values() to get an of functions, and then iterate it with for...of:
const listeners = {
func1(){ console.log(1); },
func2(){ console.log(2); },
func3(){ console.log(3); }
}
function init(){
for (const func of Object.values(listeners)){
func()
}
}
init();
Or do the same with Array.forEach():
const listeners = {
func1(){ console.log(1); },
func2(){ console.log(2); },
func3(){ console.log(3); }
}
const init = () => Object.values(listeners).forEach(func => func())
init();
Try this:
const listeners = {
func1: function () {
console.log(1);
},
func2: function () {
console.log(2);
},
func3: function () {
console.log(3);
}
}
function init() {
for (let func in listeners) {
listeners[func]();
}
}
init();
On top of the other answers. I think Array structure is more appropriate in your case.
const listeners = [
function () {
console.log(1);
},
function () {
console.log(2);
},
function () {
console.log(3);
},
];
function init() {
listeners.forEach(func => func());
}
init();
you will just need to do it like that listeners[func]() in the loop , in for in loop, keys are the one iterated, so you call items like that obj/array[key] func in your case
and while you have functions you need to add ().
check inspect in this fiddler link
Related
I'm using an animation library that uses "scenes". Scenes are structured like this:
const animations = animationLibrary({
scenes: {
sceneOne: {
appear() {
// do something when scene appears
},
disappear() {
// do something when scene disappears
}
},
sceneTwo: {
appear() {
// do something when scene appears
},
disappear() {
// do something when scene disappears
}
},
}
})
I have a lot of scenes, 40 or so, for a large project. In the past for this project, scope has not been an issue, as I generally do not need to be accessing the same variable in multiple functions, so it works for me to just define a variable inside a function and use it there. However, there are a growing number of cases where I would like to execute a setInterval in one method and a clearInterval in a different function. I am currently doing that like this:
var setIntervalId;
const animations = animationLibrary({
scenes: {
sceneOne: {
appear() {
setIntervalId = setInterval(myFunc, 1000)
},
disappear() {
clearIntervalId(setIntervalId)
}
},
}
})
The problem I am having is as the main scenes object grows, with many scenes inside of it, it seems badly organized to have a number of variables, unrelated to each other, stored outside of the animations const for scope reasons. I have tried writing my own functions inside of a scene, or adding objects inside scenes to store values, but whenever I try to return them inside a function I'm getting undefined errors in the console.
Is there a way for me to store variables or an object of values that is nested inside a single scene and still be able to access and update those variables/values across functions within that same single scene?
You can use Immediately Invoked Function Expression to create a separate variable scope for each of your scene objects.
const animations = animationLibrary({
scenes: {
sceneOne: (function () {
var setIntervalId;
return {
appear() {
setIntervalId = setInterval(myFunc, 1000)
},
disappear() {
clearInterval(setIntervalId)
}
};
})()
}
})
You could probably use factory method createScene() for creating scene objects with less code. But in this case, your animation library must always call appear() and disappear() methods on the scene object (e.g. scenes.sceneOne.appear()), otherwise it will not work due to different meaning of the this keyword.
const animations = animationLibrary({
scenes: {
sceneOne: createScene(
function () {
this.setIntervalId = setInterval(myFunc, 1000)
},
function () {
clearInterval(this.setIntervalId)
}
)
}
})
function createScene(appearFunc, disappearFunc) {
return {
appear: appearFunc,
disappear: disappearFunc,
setIntervalId: undefined
}
}
Just add a property for interval as well.
let animation = {
scenes: {
sceneOne: {
intervalRef:null,
count:0,
appear() {
console.log("Start");
this.intervalRef = setInterval(()=> console.log(this.count++), 1000);
},
disappear() {
console.log("Finish");
clearInterval(this.intervalRef);
},
},
},
};
animation.scenes.sceneOne.appear();
setTimeout(()=>{
animation.scenes.sceneOne.disappear();
},5000)
You could approach the topic with JS Classes
Scene Class
class Scene {
constructor(intervalId, data, callBack) {
this.intervalId = intervalId;
this.data = data;
this.callBack = callBack;
}
appear() {
// do something when scene appears
console.log('appear');
}
disappear() {
// do something when scene disappears
console.log('disappear');
}
getData(key) {
return this.data[key];
}
foo() {
console.log('foo(): bar');
}
bar() {
console.log('class callBack function');
this.callBack();
}
}
Instantiation
const callBack = () => { console.log('passed callBack function'); };
let scene = new Scene(1, {foo: 'bar'}, callBack);
let scene2 = new Scene(2, { foo: scene.getData('foo') }, callBack);
let scene3 = new Scene(3, scene.data, callBack);
In your case
const animations = animationLibrary({
scenes: {
sceneOne: scene,
sceneTwo: scene2,
sceneThree: scene3,
sceneFour: new Scene(4, {lorem: 'ipsum'}, function() {console.log('pass another callback');})
},
});
Output
scene.appear();
// appear
scene.getData('foo');
// "bar"
scene.foo();
// foo(): bar
scene.bar();
// class callBack function
// passed callBack function
I have a js file with many functions
function one(){
//do stuff
}
function two(){
//do stuff
}
function execute_top_one(){
//do stuff
}
function execute_top_two(){
//do stuff
}
and I need to create a function that executes, for example, all functions that start with (or contain) "execute_top" in the function name, instead of having to call all the functions manually like this
execute_top_one();
execute_top_two();
Any suggestion?
I would suggest that you do it in a little other way:
const functionStore = {
one: function() {
console.log('one')
},
two: function() {
console.log('two')
},
execute_top_one: function() {
console.log('execute_top_one')
},
execute_top_two: function() {
console.log('execute_top_two')
},
}
const execute_these = "execute_top"
const executeFunctions = (functionStore, filterString) => {
Object.entries(functionStore).forEach(([fnName, fn]) => {
if (fnName.indexOf(filterString) !== -1) {
fn()
}
})
}
executeFunctions(functionStore, execute_these)
The difference is that you gather your functions into one object (I called it functionStore, and then create the filtering string & function. As you see from the snippet, filtering an object's keys and values (called fnName & fn in my snippet) is quite easy - and inside the filtering you can call the functions stored.
I want to do something like this:
var build= (function(){
//my function body
})();
function test(){
//somthing then call build
build() //i want to call build function again in my code
}
How can I do this?
I tried this in angular:
var buildRoot = (() => {
$SubNode.get({
TypeID: vendorAdminService.NodeType.Category
}, function(data: vendorAdminService.IGetNodeParameters) {
$scope.ProductTree = data.TreeNodeModelItem;
$scope.AjaxLoading = false;
}, function(err) {
// alert(err)
})
})();
$mdDialog.show(confirm).then(function() {
$Category.Remove(node.ID)
buildRoot
}, function() {
});
but it does not work.
Anybody can guide me??
You need to return a function in your IIFE.
If you IIF is not trivial and has many functionalities you could also consider using Reveal Module Pattern.
var build = (function() {
var f = function() {
console.log('hello');
};
f();
return f;
})();
function test() {
build();
}
test();
Just use a named function.
Your IIFE needs to return a function, for later calling. But then is no need for an anonymous function.
function build() {
//my function body
}
or
var build = function () {
//my function body
};
var build = (function() {
var init = function() {
// magic code
};
return {
init: init
}
}());
function test() {
build.init()
}
test();
You include all your functionalities inside your build object, and you'll be able to call them as soon as you return them from inside that object. This effectively is called the revealing module pattern
For more information, read this
I see that there are missing semi-colons ";"
$mdDialog.show(confirm).then(function() {
$Category.Remove(node.ID);
buildRoot();
}, function() {
});
Now I have a prototype like:
function A() {}
A.prototype.run = function () {
console.log('run 1');
};
Given that I cannot change anything where A is at (no control over the source). I would like to extend the method run. Not only log run 1, also log run 2. I tried several different approaches, it does not work.
A.prototype.run = function () {
this.run.call(this);
console.log('run 2');
}
Or
A.prototype.run = function () {
arguments.callee.call(this);
console.log('run 2');
}
Anyone having a solution for this? I would rather not to copy what's inside the method run. Thanks!
A.prototype._run = A.prototype.run;
A.prototype.run = function () {
this._run.call(this);
console.log('run 2');
}
You can override the run method, saving a reference to it as such;
(function (orig) {
A.prototype.run = function () {
orig.apply(this, arguments);
console.log('run 2');
}
}(A.prototype.run));
This is similar to your first attempt, but preserves the first value of run, so you can effectively do this.run.call(this) as you attempted.
I have assigned 5000 ms to Settimeout but it is executing before assigned time interval.Can any body explain why it is happening.
<script type="text/javascript">
var getcallback = {
closure: function (callback, functionparam) {
return callback.call(functionparam);
}
}
var cleartimeout;
var startSlideShow = {
timerid: 5000,
startAnimation: function () {
cleartimeout = setTimeout(getcallback.closure(function () {
alert("this is a basic example of chaining methods");
this.startAnimation();
},this), this.timerid);
},
stopAnimation:function(){
}
}
startSlideShow.startAnimation();
</script>
Because getcallback.closure() is executing the function right away, you are not storing a reference to a function to call at a later time.
As soon as you call startAnimation, you're calling getcallback.closure, which immediately calls the callback function. To use setTimeout correctly, you need to either have closure return a function, or not use such a strange thing, and instead just use an anonymous function.
Something along the lines of:
var getcallback = {
closure: function (callback, functionparam) {
return function() {
callback.call(functionparam);
};
}
}
...
Or, to be cleaner, just:
var cleartimeout;
var startSlideShow = {
timerid: 5000,
startAnimation: function () {
cleartimeout = setTimeout(function () {
alert("this is a basic example of chaining methods");
this.startAnimation();
}, this.timerid);
},
stopAnimation:function(){
}
}
startSlideShow.startAnimation();