Nested JavaScript functions - javascript

I would like to make nested JavaScript functions as a prove of concept. I found an example and modified it a little to fit my prove of concent:
var t = {
nestedOne: {
nest: function() {
alert('nest');
this.nestedTwo.nest2();
},
nest3: function() {
alert('nest3');
},
nestedTwo: {
nest2: function() {
alert('nest2');
t.nestedOne.nest3();
}
}
}
};
t.nestedOne.nest();
// *** Output is nest, nest2 and nest3 ***
This works, but I wonder why in nest2, I have to call by t.nestedOne.nest3, and not this.nestedOne.nest3, similar to how I call from nest2.

Its all about the context of this
The easiest way to explain, is to make a slight change to your code:
var t = {
nestedOne: {
nest: function() {
console.log('nest',this);
this.nestedTwo.nest2();
},
nest3: function() {
console.log('nest3',this);
},
nestedTwo: {
nest2: function() {
console.log('nest2',this);
t.nestedOne.nest3();
}
}
}
};
t.nestedOne.nest();
The output from the above is
nest Object { nestedTwo={...}, nest=function(), nest3=function()}
nest2 Object { nest2=function()}
nest3 Object { nestedTwo={...}, nest=function(), nest3=function()}
Note that in the second call, this refers to the function, no longer the object.
Now, you can make the following 2 changes
call next2 passing in the context of this:
this.nestedTwo.nest2.call(this);
use this in nest2:
this.nest3();
And all works as expected:
var t = {
nestedOne: {
nest: function() {
console.log('nest',this);
this.nestedTwo.nest2.call(this);
},
nest3: function() {
console.log('nest3',this);
},
nestedTwo: {
nest2: function() {
console.log('nest2',this);
this.nest3();
}
}
}
};
t.nestedOne.nest();

The context of a function call is determined by the object on which the function is called, not the left-most object in the path used to get to it.
this:
this.nestedTwo.nest2();
^^^^^^^^^
not this:
this.nestedTwo.nest2();
^^^^

Related

Access correct "this" from callback function in array of functions inside an object

I’m trying to access “helperFunction” from inside a function in “steps” array. Obviously using “this” doesn’t refer to the correct object but I can’t seem to work out the proper solution.
const bannerAnimation = {
property: 0,
steps: [
function one() {
this.property = this.helperFunction();
},
function two() {
console.log(this);
}
],
helperFunction() {
// do some calculations and return the result
return 1;
},
doSteps(steps = this.steps) {
steps.forEach(step => {
setTimeout(step, 100);
});
}
};
bannerAnimation.doSteps();
All help much appreciated!
You can achiever this by using bind inside the callback to setTimeout to correctly bind the this to the correct context.
const bannerAnimation = {
property: 0,
steps: [
function one() {
this.property = this.helperFunction();
},
function two() {
console.log(this);
}
],
helperFunction() {
// do some calculations and return the result
return 1;
},
doSteps(steps = this.steps) {
var self = this;
steps.forEach(step => {
setTimeout(step.bind(self), 100);
});
}
};
bannerAnimation.doSteps();

javascript: extending methods between objects, like mixins

I want to share or reuse some logic between differents objects, that they will be pretty similar, just changing the "scope".
var Mixin = {
show: function () {
this.container.show();
},
hide: function () {
this.container.hide();
},
play: function (data) {
data.map().append();
}
};
var ObjectA = {
container: $('#container_a');
foo: function () {
this.play(otherData); // Mixin common method?
}
};
var ObjectB = {
container: $('#container_b'),
foo: function () {
this.play(data); // Mixin common method?
}
};
ObjectA.show() // show $('#container_a');
ObjectB.show() // show $('#container_b');
I was trying using underscore
_.extend(ObjectA, Mixin);
but it seems like I have issues with the reference of the Mixin (this reference to the last extended object), like if i need to clone the object and extend it?
Is there any approach to do something similar?
Thanks!!
EDIT: I having issue with the scope of 'this', that is referencing to window, when a pass as a callback a function inherits from the mixin, like this.
PersonMixin = {
mixinFoo: function () {
this.handleResponse();
}
};
Person = {
personMethod: function () {
OtherLibrary.libMehtod(this.mixinFoo);
}
};
Object.assign(Person, PersonMixin);
and then, something like this will fail, this an example stack trace
Person.personMethod();
OtherLibrary.libMethod(callbackMixin);
Ajax.post(callbackMixin);
callbackMixin(response); // this.handleResponse() is not defined, because this reference to window object.
EDIT 2: I can solve this issue using bind()
You can do this in a number of ways, my preference is adjusting the objects __proto__ property on creation which will cause it to inherit your mixin via its prototype chain. This does not require the use of underscore.
I adjusted your example for ES6 and made it a bit simpler but should get the point across.
const PlayerType = (
{ show() {
console.info(`show ${this.name}`)
}
, hide() {
console.info(`hide ${this.name}`)
}
, play: function (data) {
data.map().append();
}
}
)
const objA = { __proto__: PlayerType
, name: 'objA'
, foo(...args) {
this.play(...args)
}
}
const objB = { __proto__: PlayerType
, name: 'objB'
, foo(...args) {
this.play(...args)
}
}
objA.show()
objB.show()
Simpler and no ES6:
var Mixin = (
{ show() {
console.info('show ' + this.name)
}
, hide() {
console.info('hide ' + this.name)
}
}
)
var a = { __proto__: Mixin, name: 'a' }
var b = { __proto__: Mixin, name: 'b' }
a.show()
b.show()
Alternate - Does the same thing with Object.create().
var Mixin = (
{ show() {
console.info('show ' + this.name)
}
, hide() {
console.info('hide ' + this.name)
}
}
)
var a = Object.create(Mixin, { name: { value: 'a', enumerable: true } })
var b = Object.create(Mixin, { name: { value: 'b', enumerable: true } })
a.show()
b.show()
It works, just check your syntax also.
var Mixin = {
show: function() {
console.log(this.tmp);
}
}
var oA = {
tmp: 'tmpA'
}
var oB = {
tmp: 'tmpB'
}
var mA = Object.assign(oA, Mixin);
var mB = Object.assign(oB, Mixin)
mA.show();
mB.show()

unable to access function from another function using this within same object

I have the following:
$scope.option = {
generateID:function(){
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);
},
values : [
{id:this.generateId()},
{id:this.generateId()},
{id:this.generateId()},
{id:this.generateId()}
],
markCorrect : function(option){
},
remove:function(option)
{
this.values = this.values.filter(function(value){return value.id!=option.id})
}
}
I always get a this.generateId is not a function error. I am pretty sure that i am missing something fundamental here!
It may be better to store the id generator function in a separate function so it is easier to reference:
function generateId = function() {
return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 5);
}
$scope.option = {
generateID: generateId,
values : [
{id: generateId()},
{id: generateId()},
{id: generateId()},
{id: generateId()}
],
markCorrect : function(option){
},
remove:function(option)
{
this.values = this.values.filter(function(value){return value.id!=option.id})
}
}
The primary issue is that you're trying to access properties of $scope.option in the middle of declaring it. Try doing something like this instead:
$scope.option = (function () {
function generateId () {
/* logic */
}
return {
values: [
{id: generateId()}
// ...
],
markCorrect: function () {},
remove: function () {}
};
}) ();
This is the 'revealing module pattern', i.e. a function that returns an object forming a closure on some other data or functionality.
There is a typo; rename generateID to generateId.

Referencing root parent of variable in singleton pattern

If i am inside hello.world.planet and i reference this.two, that will get siblings, how can i reference hello.planet without referring to the name of the variable, something similar to 'this'
var hello = {
world: {
one: {
},
two: {
},
three: {
}
},
planet: {
}
}
I'm not completely following where you want to make a reference, but you could try using .bind(). I hope this is what you're after to:
var hello = {
world: {
one: {
},
two: function() {
alert(this.planet);
}.bind(hello),
three: {
}
},
planet: {
}
}

Calling function defined in an object

I have an object that defines the name and parameters of a function to be called in the click event of a page element.
object_array = [
{
id: "id1",
onclick: "load_file(filename.php);",
},
{
id: "id2",
onclick: "open_url('http://url.com');",
}
];
That information must be retrieved dynamically. I'd like in the click event to call the onclick function for a given object in the array.
$('element').click(function() {
// call object.onclick
});
Seems that eval() is not a good choice. Is there a different way of calling that function?
You should refactor the object_array to:
[{
id: "id1",
action: "load_file",
url: "filename.php"
}, {
id: "id2",
action: "open_url",
url: 'http://url.com'
}];
Then you can call it with:
var actions = {
load_file: function(url) {
...
},
open_url: function(url) {
...
},
...
};
object_array.forEach(function(ob) {
$("#"+ob.id).click(function() {
actions[ob.action](ob.url);
});
});
If you have more complex arguments, you could also deliver an arguments array instead of url and use apply() on the function.
Or, if you just want a lookup-table of functions, use:
var object = {
"id1": function() {
load_file('filename.php');
},
"id2": function() {
open_url('http://url.com');
}
};
$('element').click(function() {
object[this.id]();
});
Here's a working jsfiddle:
http://jsfiddle.net/Zh6Fv/1/
Like so:
object_array = [
{
id: "id1",
// Encapsulate in an anonymous function
onclick: function(){ load_file('filename.php'); },
},
{
id: "id2",
// Encapsulate in an anonymous function
onclick: function(){ open_url('http://url.com'); },
}
];
Then actually bind it it like this ::
$('element').click(obj.onclick);
Presumably you would be using it like this::
object_array.forEach(function(obj){
// Note there is no need to wrap the obj.onclick
// into another anonymous function anymore!
$("#" + obj.id).click(obj.onclick);
});
Here's a working jsfiddle:
http://jsfiddle.net/Zh6Fv/1/
you can declare a function like
onclick : function()
{
action(arguments);
}

Categories

Resources