I have an array of objects containing references (bound by the .bind() method) to my class functions. When I access them directly, like array[3].myFunction, everything works fine. But the strange behavior occurs when I try to access these function iterating over the array. I've tried by Array.forEach(), for-in, for-of and Array.map() function, but the result is always the same - I get the first function four times. What am I doing wrong here? Thanks in advance.
var Container = function() {
this.function1 = function() {
console.log('function 1 invoked');
};
this.function2 = function() {
console.log('function 2 invoked');
};
this.function3 = function() {
console.log('function 3 invoked');
};
this.function4 = function() {
console.log('function 4 invoked');
};
this.array = [
{ key: '1', myFunction: this.function1.bind(this) },
{ key: '2', myFunction: this.function2.bind(this) },
{ key: '3', myFunction: this.function3.bind(this) },
{ key: '4', myFunction: this.function4.bind(this) },
];
};
var container = new Container();
// Just printing the results below
console.log('direct access:');
console.log(container.array[3].myFunction);
console.log('forEach:');
container.array.forEach(el => {
console.log(el.myFunction);
});
console.log('for in:');
for (let i in container.array) {
console.log(container.array[i].myFunction);
}
console.log('map:')
container.array.map(el => {
console.log(el.myFunction);
});
PLNKR: http://plnkr.co/edit/mn8iGh4F3GcJXTNWXMiJ?p=preview
Have a look below. All seems to be working.
When you do console.log(el.myFunction), it will actually print the handle and not executing it where all handle looks same as function () { [native code] }
When you invoke the function instead el.myFunction(), you can see all they are calling the right functions and print the results respectively.
You can check the function invocation below.
var Container = function() {
this.function1 = function() {
console.log('function 1 invoked');
};
this.function2 = function() {
console.log('function 2 invoked');
};
this.function3 = function() {
console.log('function 3 invoked');
};
this.function4 = function() {
console.log('function 4 invoked');
};
this.array = [
{ key: '1', myFunction: this.function1.bind(this) },
{ key: '2', myFunction: this.function2.bind(this) },
{ key: '3', myFunction: this.function3.bind(this) },
{ key: '4', myFunction: this.function4.bind(this) },
];
};
var container = new Container();
// Just printing the results below
console.log('direct access:');
container.array[3].myFunction();
console.log('forEach:');
container.array.forEach(el => {
el.myFunction();
});
console.log('for in:');
for (let i in container.array) {
container.array[i].myFunction();
}
console.log('map:')
container.array.map(el => {
el.myFunction();
});
Related
var Person = {
name: "jana",
getName: function(callBack) {
callBack();
console.log("** "+this.name);
}
}
var anotherPerson = { name: "prabu"}
I have 2 objects. I need "anotherPerson" to be bound with the Person object. Also, I want to send parameter as a function.
I have tried below methods, but its not working
Person.getName.apply(anotherPerson, function(){})
Person.getName.apply(anotherPerson)(function(){})
Use call to pass an arbitrary number of arguments to your function, or apply to pass an array of arguments:
var Person = {
name: "jana",
getName: function(callBack) {
callBack();
console.log("** " + this.name);
}
}
var anotherPerson = {
name: "prabu"
}
Person.getName.call(anotherPerson, function () {})
Person.getName.apply(anotherPerson, [function () {}])
Have you tried Object.assign ? Like so
var Person = {
name: "jana",
getName: function(callBack) {
callBack();
console.log("** " + this.name);
}
}
var anotherPerson = {
name: "prabu"
}
Object.assign(Person, anotherPerson).getName(alert)
You can use arrow function and return the name parameter to assign value to getName.
var Person = {
name: "jana",
getName: (obj) =>obj.name
}
var anotherPerson = {
name: "prabu"
}
Person.getName(anotherPerson);
console.log(Person);
I have a question about .bind() function.
I have this code and it outputs Window object and I don't understand why. Could you explain to me why bind(this) had no effect on the function?
let vakho = {
name: "salome",
a: function () {
let something = function () {
return this;
}
something.bind(this)
return {
f: function () {
return something();
}
}
},
}
console.log(vakho.a().f())
.bind returns a new function with the attached context. You need to assign the result to the something again.
let vakho = {
name: "salome",
a: function () {
let something = function () {
return this;
};
something = something.bind(this); // Assign to the something
return {
f: function () {
return something();
}
}
},
}
console.log(vakho.a().f());
console.log(vakho.a().f().name);
I have my javascript code like this . Inside that I have an init() function and in that function I have an options JSON object and in that object I have a function defined as objectselected(). How I call that function in a button click event
I have tried like this WorkFlow.init().options.Objectselected() but it is not working,
var WorkFlow = {
connectionData: [],
selectedTouchpoints: [],
init: function () {
var options = {
palleteId: "myPaletteElement",
elementId: "playAreaContainer",
TextStoreList: ['One', 'Two', 'Three'],
LinkTextStoreList: $('#drpLinkType option').map(function () {
return this.text;
}).get(),
shapeList: ['RoundedRectangle', 'Circle', 'Rectangle', 'Ellipse', 'Square', 'Diamond', 'Card', 'Database'],
diagramUpdate: function (e) {
},
objectSelected: function (e) {
},
linkUpdate: function (e) {
},
initialize: function () {
}
myGraph = new Graph(options);
options.initialize();
},
}
How to call that function.
One way around is you can return options and than call it.
init: function () {
var options = {
...your code..}
return options;
},
and call it than
var options = WorkFlow.init();
options.Objectselected();
As it stands, you have no access to options because it's a local variable - that is, local to its scope.
To access its contents, you'll need to return it from init().
Think about it:
WorkFlow.init()
Currently this returns undefined, because your init() returns nothing. You're trying to chain like in jQuery, but that relies on the API always returning the instance. Your path finds a dead-end at init().
To fix this, have init() return options - or at least the part of it you want to access from outside - an "export".
So (basic example)
init: function() {
var options {
my_func: function() { }, //<-- we want outside access to this
private: 'blah' //<-- this can stay private - leave it out of the export
}
//return an export, exposing only what we need to
return {
my_func: options.my_func
}
}
You need to return options as it is inside init function's scope
var WorkFlow = {
connectionData: [],
selectedTouchpoints: [],
init: function () {
var options = {
palleteId: "myPaletteElement",
elementId: "playAreaContainer",
TextStoreList: ['One', 'Two', 'Three'],
LinkTextStoreList: $('#drpLinkType option').map(function () {
return this.text;
}).get(),
shapeList: ['RoundedRectangle', 'Circle', 'Rectangle', 'Ellipse', 'Square', 'Diamond', 'Card', 'Database'],
diagramUpdate: function (e) {
},
objectSelected: function (e) {
},
linkUpdate: function (e) {
},
initialize: function () {
}
myGraph = new Graph(options);
options.initialize();
return options;
},
}
And call it as WorkFlow.init().objectSelected();
Building on Patrick's comment, you'd need to return options from the init function:
var WorkFlow = {
connectionData: [],
selectedTouchpoints: [],
init: function () {
var options = {
palleteId: "myPaletteElement",
...
options.initialize();
return options;
},
}
How to call getStartPoint() in startPoint, in the below code?
var obj = (function () {
return {
defaults: {
prop: {
getStartPoint: function () {
// more login ...
},
getEndPoint: function () {
// more login ...
},
startPoint: this.getStartPoint(),
endPoint: this.getEndPoint(),
}
}
};
})();
I am getting error
Uncaught TypeError: Object ... has no method 'getStartPoint'
in all these: getStartPoint(), this.getStartPoint(), obj.defaults.prop.getStartPoint()
One possible solution:
var obj = (function () {
function getStartPoint() {
console.log("getStartPoint");
}
function getEndPoint() {
console.log("getEndPoint");
}
return {
defaults: {
prop: {
getStartPoint: getStartPoint,
getEndPoint: getEndPoint,
startPoint: getStartPoint(),
endPoint: getEndPoint()
}
}
};
})();
Another:
function Obj() {
if (!(this instanceof Obj)) return new Obj();
this.defaults = {
prop: {
startPoint: this.defaults.prop.getStartPoint(),
endPoint: this.defaults.prop.getEndPoint()
}
};
}
Obj.prototype = {
defaults: {
prop: {
getStartPoint: function () {
console.log("getStartPoint");
},
getEndPoint: function () {
console.log("getEndPoint");
}
}
}
};
var obj = new Obj();
This is a kind of weird problem. Given the javascript below, I'm expecting the newFunctions object to contain functions that wrap an original object, however it's only running the last action that occurs in the loop. Shouldn't var actionToCall copy a reference to what currentAction is currently looking at, and not change when the loops iterate? I'm stumped here.
var typeArray = {
type1: {
function1: function() {
console.log("this is function 1");
},
function2: function() {
console.log("this is function 2");
}
},
type2: {
somefunction: function() {
console.log("this is some function")
}
},
type3: {
blah: function() {
return;
},
moreBlah: function(something) {
console.log(something);
},
evenMore: function() {
console.log("I'm last!");
}
}
},
index,
typeIndex,
currentType,
actionIndex,
currentAction,
newFunctions = {};
for(typeIndex in typeArray) {
currentType = typeArray[typeIndex];
for(actionIndex in currentType) {
currentAction = currentType[actionIndex];
console.log(currentAction.toString());
newFunctions[actionIndex] = function() {
var actionToCall = currentAction;
console.log("about to run action");
actionToCall.call(this);
console.log("action has been called");
}
}
}
console.log(newFunctions);
for(index in newFunctions) {
(newFunctions[index])();
}
It's because actionToCall is being assigned to currentAction.
Since currentAction is global it's value keeps changing as the loop iterates.
When the loop ends, currentAction is assigned to evenMore.
Here's the fix using a self execution function to induce scope.
var typeArray = {
type1: {
function1: function() {
console.log("this is function 1");
},
function2: function() {
console.log("this is function 2");
}
},
type2: {
somefunction: function() {
console.log("this is some function")
}
},
type3: {
blah: function() {
return;
},
moreBlah: function(something) {
console.log(something);
},
evenMore: function() {
console.log("I'm last!");
}
}
},
index,
typeIndex,
currentType,
actionIndex,
currentAction,
newFunctions = {};
for(typeIndex in typeArray) {
currentType = typeArray[typeIndex];
for(actionIndex in currentType) {
currentAction = currentType[actionIndex];
console.log(currentAction.toString());
//induce scope here so actionToCall keeps the current value of currentAction.
(function(){
var actionToCall = currentAction;
newFunctions[actionIndex] = function() {
console.log("about to run action");
actionToCall.call(this);
console.log("action has been called");
}
})();
}
}
console.log(newFunctions);
for(index in newFunctions) {
(newFunctions[index])();
}