const person = {
name: "JONY SINS",
age: 25,
walk() {
let anotherPerson = {
right() {
console.log("right");
},
left() {
console.log("left");
},
};
console.log(this);
},
};
is it possible to call right() and left() function in this object
anotherPerson is a local variable inside the walk() method. This variable can only be accessed inside the method. You could call anotherPerson.left() from inside walk(), but you can't call it from outside the method.
If the method returned the variable, you could call walk() and then call left() or right() from the returned value.
const person = {
name: "JONY SINS",
age: 25,
walk() {
let anotherPerson = {
right() {
console.log("right");
},
left() {
console.log("left");
},
};
console.log("walk");
return anotherPerson;
},
};
let other = person.walk();
other.left();
other.right();
Related
Just wanted to share a little trick I learned to pass variables into the scope of your JS Array.forEach() method.
I had a situation where I needed to use a forEach loop to build a dataset. But I needed to access variables in the current scope as well (I needed to be able to reference this in the loop).
This is the situation I was in:
var dataset = {
data: [],
backgroundColor:[],
};
items.forEach(function (item) {
dataset.data.push(item.age);
if (item.age < 2) {
dataset.bgColor.push(this.green);
} else if (item.age < 5) {
dataset.bgColor.push(this.yellow);
} else {
dataset.bgColor.push(this.red);
}
}, this);
this.refreshGraph(dataset);
Dataset isn't accessible from within the loop. So how do we access it while iterating?
I haven't seen this solution on stack overflow and it didn't fit any question I could find.
Answer below:
With the abilities of es6
If you'll use an Arrow Function the this will be taken from
items.forEach(item => {
// You can use this as out of the forEach scope
});
From MDN Web Docs:
An arrow function does not have its own this. The this value of the
enclosing lexical scope is used; arrow functions follow the normal
variable lookup rules. So while searching for this which is not
present in current scope, an arrow function ends up finding the this
from its enclosing scope.
Another nice explanation:
https://hackernoon.com/javascript-es6-arrow-functions-and-lexical-this-f2a3e2a5e8c4
If you have a function out of scope of some data yet need to access it, you can use a curried function that takes that dataset as the first parameter and can still use this normally throughout:
//curried function that uses `dataset` and `this` but it is not
//in the context where the iteration happens
function makeLoopCallback(dataset) {
return function(item) {
dataset.data.push(item.age);
if (item.age < 2) {
dataset.bgColor.push(this.green);
} else if (item.age < 5) {
dataset.bgColor.push(this.yellow);
} else {
dataset.bgColor.push(this.red);
}
}
}
//object to serve as `this` context for a function
var obj = {
green: "Green",
yellow: "Yellow",
red: "Red",
doSomething: function(items) {
var data = {
data: [],
bgColor:[],
};
items.forEach(makeLoopCallback(data), this);
return data;
}
}
//set up some dummy data
var input = [ { age: 1 }, { age: 2 }, { age: 3 }, { age: 4 }, { age: 5 }, { age: 6 } ];
//call the function
console.log(obj.doSomething(input))
An alternative is to use Array#reduce instead of Array#forEach with a function that takes two parameters directly. Since .reduce cannot set the this context, you can just use Function#bind to do it:
//external function that uses `dataset` and `this` but it is not
//in the context where the iteration happens
function external(dataset, item) {
dataset.data.push(item.age);
if (item.age < 2) {
dataset.bgColor.push(this.green);
} else if (item.age < 5) {
dataset.bgColor.push(this.yellow);
} else {
dataset.bgColor.push(this.red);
}
return dataset;
}
//object to serve as `this` context for a function
var obj = {
green: "Green",
yellow: "Yellow",
red: "Red",
doSomething: function(items) {
var data = {
data: [],
bgColor:[],
};
return items.reduce(external.bind(this), data);
}
}
//set up some dummy data
var input = [ { age: 1 }, { age: 2 }, { age: 3 }, { age: 4 }, { age: 5 }, { age: 6 } ];
//call the function
console.log(obj.doSomething(input))
The solution is to pass a JSON object as the this argument.
so before we had:
Array.forEach(function(){}, this)
// "this" is just an object ^^^^ just like anything else in JavaScript
Now we have:
Array.forEach(function(){}, {_self: this, dataset: dataset})
// you can access _self and dataset just as if they were in scope
And now you can make data changes while iterating with an anonymous function :)
Full example:
var dataset = {
data: [],
backgroundColor:[],
};
items.forEach(function (item) {
dataset.data.push(item.age);
if (item.age < 2) {
dataset.bgColor.push(_self.green);
} else if (item.age < 5) {
dataset.bgColor.push(_self.yellow);
} else {
dataset.bgColor.push(_self.red);
}
}, { _self: this , dataset: dataset});
Array.prototype.forEach(callbackFun, ?this)
You can pass dataset as this argument to forEach
var dataset = {
data: [],
backgroundColor:[],
};
items.forEach(function (item) {
this.dataset.data.push(item.age);
if (item.age < 2) {
this.dataset.bgColor.push(this.tempThis.green);
} else if (item.age < 5) {
this.dataset.bgColor.push(this.tempThis.yellow);
} else {
this.dataset.bgColor.push(this.tempThis.red);
}
}, {tempThis:this,dataset:dataset});
this.refreshGraph(dataset);
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);
My issue is I have 2 inner objects in my js class and I'm trying to use the methods from one of those objects in my other object (examples of what I'm trying to do below). I understand why this doesn't work because of a the scope. I'm just wondering if there is a way to get it to work.
var Class1 = {
self : this,
Obj1 : {
Obj1Method : function () {
alert("Do something");
},
Obj1Method2 : function () {
alert("Do something else");
},
InnerObj1 : {
InnerNestObj1Method : function (val) {
alert(val + 2);
}
}
},
Class1Method2 : function () {
this.Obj1.Obj1Method2();
},
Obj2 : {
Obj2Method : function (val2) {
self.Obj1.InnerObj1.InnerNestObj1Method(val2);
},
Obj2Method2 : function () {
self.Class1Method2();
}
}
};
Class1.Obj1.InnerObj1.InnerNestObj1Method(3); //works
Class1.Class1Method2(); //works
Class1.Obj2.Obj2Method2(); //No bueno
Class1.Obj2.Obj2Method(5); //No bueno
You can fix your example by replacing self with Class1.
The line self : this, is setting Class1.self to point to the global object (this when that line is evaluated).
var Class1 = {
self : this,
Obj1 : {
Obj1Method : function () {
alert("Do something");
},
Obj1Method2 : function () {
alert("Do something else");
},
InnerObj1 : {
InnerNestObj1Method : function (val) {
alert(val + 2);
}
}
},
Class1Method2 : function () {
this.Obj1.Obj1Method2();
},
Obj2 : {
Obj2Method : function (val2) {
Class1.Obj1.InnerObj1.InnerNestObj1Method(val2);
},
Obj2Method2 : function () {
Class1.Class1Method2();
}
}
};
Class1.Obj1.InnerObj1.InnerNestObj1Method(3); //works
Class1.Class1Method2(); //works
Class1.Obj2.Obj2Method2(); //bueno
Class1.Obj2.Obj2Method(5); //bueno
What happens when you do self: this
// If this is running in non strict mode, from the global scope, `this` points
// To the global object because there was no function call setting `this`
var Class1 = {
self : this,
};
What you need to understand is that this is set by whoever called the function using this. In the example above, there is no caller, so the runtime sets this to point to the global object.
Here's how you could you could make your object a bit more reusable and give yourself a reference to the outer object:
function createClass() {
var self = {
Obj1: {
Obj1Method: function() {
alert("Do something");
},
Obj1Method2: function() {
alert("Do something else");
},
InnerObj1: {
InnerNestObj1Method: function(val) {
alert(val + 2);
}
}
},
Class1Method2: function() {
self.Obj1.Obj1Method2();
},
Obj2: {
Obj2Method: function(val2) {
self.Obj1.InnerObj1.InnerNestObj1Method(val2);
},
Obj2Method2: function() {
self.Class1Method2();
}
}
};
return self;
}
var Class1 = createClass();
Class1.Obj1.InnerObj1.InnerNestObj1Method(3); //works
Class1.Class1Method2(); //works
Class1.Obj2.Obj2Method2(); //works
Class1.Obj2.Obj2Method(5); //works
You can do it with Classes:
"use strict"
class Class1 {
constructor() {
this.Obj1 = {
Obj1Method: function() {
alert("Do something");
},
Obj1Method2: function() {
alert("Do something else");
},
InnerObj1: {
InnerNestObj1Method: function(val) {
alert(val + 2);
}
}
};
var self = this;
this.Obj2 = {
Obj2Method: function(val2) {
self.Obj1.InnerObj1.InnerNestObj1Method(val2);
},
Obj2Method2: function() {
self.Class1Method2();
}
};
}
Class1Method2() {
this.Obj1.Obj1Method2();
}
};
var c1 = new Class1();
c1.Obj1.InnerObj1.InnerNestObj1Method(3); //works
c1.Class1Method2(); //works
c1.Obj2.Obj2Method(3); //works
c1.Obj2.Obj2Method2(); //works
I have the following object:
var party =
{
food:
{
serve: function () {
// I want to call turnOff method from here
}
cleanUp: function () {
}
}
music:
{
turnOff: function () {
}
}
}
So as the comment points out, I want to call the turnOff method from the music object, how can I do this? this refers to the food object but I need to access the music object...
var party =
{
food:
{
serve: function () {
party.music.turnOff();
},
cleanUp: function () {
}
},
music:
{
turnOff: function () {
}
}
}
Use a constructor instead of a literal with a variable referencing the parent object
var party = new (function()
{
var self = this;
this.food =
{
serve: function () {
self.music.turnoff();
},
cleanUp: function () {
}
}
this.music =
{
turnOff: function () {
}
}
})();
Call it as party.music.turnOff().
FYI, your above code block isn't valid. You're missing some commas - after the serve and food closing braces.