An example can be seen on this JSFiddle
I'm creating an instance, changing one of the functions of that instance, and then calling that function within a different function in that instance. I'm having difficulty accessing local and instance variables inside the updated function.
Code
var MyObj = function (name) {
var that = this;
MyObj.myObjs = (MyObj.myObjs || []),
this.objName = name,
this.objIdx = MyObj.myObjs.length;
MyObj.myObjs.push(this.objName);
this.doOnSetName = function () {};
this.setName = function (name) {
that.doOnNameSet();
that.objName = name;
MyObj.myObjs[that.objIdx] = name;
}
}
var obj1 = new MyObj("obj1");
//obj1.doOnNameSet = function() { alert("objName: "+this.objName) };
//obj1.setName("obj1");
var obj2 = new MyObj("obj2");
obj2.doOnNameSet = function () {
$("#console").append("Old objName: " + this.name
+ "<br />New objName: " + name + "<br />")
};
obj2.setName("obj2 - changed");
$("#console ").append("Objects: <br />*" + MyObj.myObjs.join(", <br />*"));
Actual Outcome
Old objName: undefined
New objName: result
Objects:
*obj1,
*obj2 - changed
Desired Outcome
Old objName: obj2
New objName: obj2 - changed
Objects:
*obj1,
*obj2 - changed
that.objName = name;
MyObj.myObjs[that.objIdx] = name;
that.doOnNameSet();
means that the old name is already forgotten when the listener is called. I'm not sure how you did intend to get it. Maybe call the listener before you change it, with the new name as an argument.
obj2.doOnNameSet = function () {
$("#console").append("Old objName: " + this.name
+ "<br />New objName: " + name + "<br />");
};
The property is named objName, and that name variable is obviously undefined. Did you want to have it as a parameter?
You could pass, the old and the new name to doOnSetName (Fiddle):
var MyObj = function (name) {
var that = this;
MyObj.myObjs = (MyObj.myObjs || []),
this.objName = name,
this.objIdx = MyObj.myObjs.length;
MyObj.myObjs.push(this.objName);
this.doOnSetName = function (oldName, newName) {};
this.setName = function (name) {
that.doOnNameSet(that.objName, name);
that.objName = name;
MyObj.myObjs[that.objIdx] = name;
}
}
var obj1 = new MyObj("obj1");
//obj1.doOnNameSet = function() { alert("objName: "+this.objName) };
//obj1.setName("obj1");
var obj2 = new MyObj("obj2");
obj2.doOnNameSet = function (oldName, newName) {
$("#console").append("Old objName: " + oldName
+ "<br />New objName: " + newName + "<br />")
};
obj2.setName("obj2 - changed");
$("#console ").append("Objects: <br />*" + MyObj.myObjs.join(", <br />*"));
The errors are in your obj2.doOnNameSet method. this.name should be this.objName. name is undefined in your code snippet but presumably it is defined as 'result' somewhere else in your code. You can fix this with the following change
obj2.doOnNameSet = function (name) {
Finally you need to call doOnNameSet before you make the change:
this.setName = function (name) {
that.doOnNameSet(name);
that.objName = name;
MyObj.myObjs[that.objIdx] = name;
}
Not sure what it is you're trying to do but the following will set "this" name on creation and save it in a shared names list (not a global variable like your code but a prototype property shared by all instances). When setName is used than the name in the array is set to the changed name:
var MyObj = function (name) {
this.namesIndex=this.names.length;
this.names.push(name);
this.objName = name;
}
MyObj.prototype.names=[];
MyObj.prototype.setName = function (name){
this.objName=name;
this.names[this.namesIndex]=name;
}
var o1=new MyObj(1);
var o2=new MyObj(2);
o2.setName("2 changed");
console.log(o2.names);
Related
var Car = function(name, year) {
this.name = name;
this.year = year;
this.print = function() {
console.log(name+" "+year);
}
}
var tesla = new Car("tesla", 2018);
tesla.print();
tesla = JSON.parse(JSON.stringify(tesla));
console.log(tesla);
tesla.print(); // Uncaught TypeError: tesla.print is not a function
How can i add the print function to the object after the parse? Is there an elegant solution for this?
You could create a prototype for printing and call the method with an object for binding.
function Car(name, year) {
this.name = name;
this.year = year;
}
Car.prototype.print = function() { // add prototype
console.log(this.name + " " + this.year); // take this as reference to the instance
};
var tesla = new Car("tesla", 2018);
tesla.print();
tesla = JSON.parse(JSON.stringify(tesla));
console.log(tesla);
Car.prototype.print.call(tesla); // borrow method from class, take own object
A clean approach is to add a function deserialize as prototype which takes an object and assign all properties to the instance.
function Car(name, year) {
this.name = name;
this.year = year;
}
Car.prototype.print = function() {
console.log(this.name + " " + this.year);
};
Car.prototype.deserialize = function(object) {
Object.entries(object).forEach(([k, v]) => this[k] = v);
};
var tesla = new Car("tesla", 2018);
tesla.print();
tesla = JSON.parse(JSON.stringify(tesla));
console.log(tesla);
var tesla2 = new Car;
tesla2.deserialize(tesla);
tesla2.print();
The JSON data format does not support functions (which would be very unhelpful if you generated some JSON from a JavaScript object and then tried to parse it with C#!).
A sensible approach to this would be to change the Car constructor function so that it can accept an object as the first argument.
var Car = function(name, year) {
if (typeof name === "object") {
// use properties of `name` to set everything in this object
} else {
// Treat name and year as a string and number just like you are now
}
...
Then you can:
tesla = new Car( JSON.parse(JSON.stringify(tesla)) );
… which will also generate an object with the correct prototype.
My html page is not responding to this code I wrote in JS, i'm a total beginner, and just started learning JS, can somebody tell me why this doesn't work?
/* this is a practice file that'll play with js
nothing strange to look at here folks! */
var firstName = 'Steven';
var lastName = 'Curry';
var fullName = firstName + ' ' + lastName;
function Hotel(HotelName){
this.HotelName = HotelName;
this.numRooms = 20;
this.numGuests;
this.checkAvailability {
if(numRooms != 20 ){
return true;
}
else{
return false;
}
}
this.getHotelName = function(){
//can it work with this dot operator?
return this.HotelName;
}
}
var HiltonHotel = new Hotel('Hilton');
var hName = document.getElementById('hotelName');
hName.textContent = getHotelName();
var el = document.getElementById('name');
el.textContent = fullName;
<!DOCTYPE html>
<html>
<body>
<div id = 'greeting'> Hello
<span id="name">friend</span>!
<h1>Welcome To the <span id = 'hotelName'>Hyatt</span>
</div>
<script
src = "https://stacksnippets.net/js">
</script>
</body>
</html
I'm pretty sure it's ordering and my syntax i need to work on, any advice is greatly appreciated thank you!
Few misunderstandings:
checkAvailability is a function, you are missing parens.
while accessing the getHotelName function, you have to refer to the HiltonHotel variable, to be able to access and call that function.
few minor errors in your html code, while operating in code snippet, you don't have to add a separate script, it's connected together by default.
var firstName = 'Steven';
var lastName = 'Curry';
var fullName = firstName + ' ' + lastName;
function Hotel(HotelName) {
this.HotelName = HotelName;
this.numRooms = 20;
this.numGuests;
this.checkAvailability = function() { // it's a function (missing parens)
if (numRooms != 20) {
return true;
} else {
return false;
}
}
this.getHotelName = function() {
return this.HotelName;
}
}
var WeiHotel = new Hotel('Hilton');
var hName = document.getElementById('hotelName');
hName.textContent = WeiHotel.getHotelName(); // refer to the `WeiHotel` variable
var el = document.getElementById('name');
el.textContent = fullName;
<div id='greeting'> Hello
<span id="name">friend</span>!
<h1>Welcome To the <span id='hotelName'>Hyatt</span></h1>
</div>
An extension to the answer of #KindUser:
You're not using closures anywhere in this class to store some private state. Therefore you should attach the methods to the prototype and not to the instance itself. It's more economic, because now all instances share one function, not one per instance. And the JS engine can optimize that better.
Then, you have another error in checkAvailability: numRooms needs to be addressed as this.numRooms because it is a property of this instance, and there is no variable with this name.
And one about style. If you have something like
if(condition){
return true;
}else{
return false;
}
you can simplify this to:
return condition;
//or if you want to enforce a Boolean value,
//but your condition may return only a truthy/falsy value:
return Boolean(condition);
//sometimes also written as:
return !!(condition);
Next. Stick to the coding standards. In JS a variable/property starting with an uppercase letter would indicate a class/constructor, therefore HotelName, HiltonHotel, WeiHotel are misleading.
And I find the property name hotelName redundant and counter-intuitive. Imo you have a Hotel, it has a name, but that's just an opinion.
var firstName = 'Steven';
var lastName = 'Curry';
var fullName = firstName + ' ' + lastName;
function Hotel(name) {
this.name = name;
this.numRooms = 20;
this.numGuests;
}
Hotel.prototype.checkAvailability = function() {
return this.numRooms !== 20;
}
Hotel.prototype.getHotelName = function() {
return this.name;
}
var hotel = new Hotel('Hilton');
var hName = document.getElementById('hotelName');
hName.textContent = hotel.getHotelName(); // refer to the `weiHotel` variable
var el = document.getElementById('name');
el.textContent = fullName;
<div id='greeting'> Hello
<span id="name">friend</span>!
<h1>Welcome To the <span id='hotelName'>Hyatt</span></h1>
</div>
or as an ES6 class (and some playin around):
class Person{
constructor(firstName, lastName){
this.firstName = firstName;
this.lastName = lastName;
}
//this is a getter, you can read it like a property
get fullName(){
return this.firstName + " " + this.lastName;
}
//this function is implicitely called whenever you try to convert
//an instance of `Person` into a string.
toString(){
return this.fullName;
}
}
class Hotel{
constructor(name) {
this.name = name;
this.numRooms = 20;
this.numGuests;
}
checkAvailability() {
return this.numRooms !== 20;
}
getHotelName() {
return this.name;
}
}
var steve = new Person('Steven', 'Curry');
var hotel = new Hotel('Hilton');
var hName = document.getElementById('hotelName');
hName.textContent = hotel.getHotelName(); // refer to the `weiHotel` variable
var el = document.getElementById('name');
el.textContent = steve.fullName;
//this uses the `toString()` method to convert the `Person` steve into a string
//for people, this makes sense, for the Hotel you'd want to think:
// - where do I want to use this?
// - and what should this string contain?
console.log("Hello, I'm " + steve + " and I'm at the "+ hotel.name);
<div id='greeting'> Hello
<span id="name">friend</span>!
<h1>Welcome To the <span id='hotelName'>Hyatt</span></h1>
</div>
I’ve made a little sandbox using the p5.js library : http://gosuness.free.fr/balls/
I’m trying to implement a way to deal with the options on the side, which are toggled using keyboard shortcuts.
This is what I tried to do :
var options =
{
Option: function(name, value, shortcut)
{
this.name = name;
this.shortcut = shortcut;
this.value = value;
this.show = function ()
{
var texte = createElement("span",this.name + " : " + this.shortcut + "<br />");
texte.parent("options");
texte.id(this.name);
}
},
toggle: function(shortcut)
{
for (var o in this)
{
console.log(o);
if (o.shortcut == shortcut)
{
o.value = !o.value;
changeSideText("#gravity",gravity);
addText("Toggled gravity");
}
}
}
};
I instantiate each option inside the object options thus :
var gravity = new options.Option("gravity", false,"G");
var paintBackground = new options.Option("paintBackground",false,"P");
When I call the function options.toggle, console.log(o) gives me "Option" "toggle". but what I want is to get for (var o in this) to give me the list of properties of the object options, which are in this case gravity and paintBackground
How do I do that ?
Thanks !
When You create a instance of Option, its not kept within the variable options, but in the provided variable.
var gravity = new options.Option("gravity", false,"G");
Creates an instance of Option located under gravity variable.
Your iterator for (var o in this) iterates over options properties, with the correct output of the object's methods.
If You want your code to store the new instances of Option within options variable, you can modify code like
var options =
{
instances: [],
Option: function(name, value, shortcut)
{
this.name = name;
this.shortcut = shortcut;
this.value = value;
this.show = function ()
{
var texte = createElement("span",this.name + " : " + this.shortcut + "<br />");
texte.parent("options");
texte.id(this.name);
}
options.instances.push(this);
},
toggle: function(shortcut)
{
for (var i in this.instances)
{
console.log(this.instances[i]);
if (this.instances[i].shortcut == shortcut)
{
this.instances[i].value = !this.instances[i].value;
changeSideText("#gravity",gravity);
addText("Toggled gravity");
}
}
}
};
this is your example working as You intend it to, but i wouldnt consider this as a reliable design pattern.
I would like to compare the names, if they match, then say we have similar names, else that we have different names. But this code gives output undefined while comparing names.
Could someone please help me out to understand/fix this problem?
I want to get following, for example:
We have different names, Bob and I.
function addPersonMethods(obj){
this.obj = obj;
}
addPersonMethods.prototype.greet = function(str){
return str + ", my name is "+ this.obj.name;
}
addPersonMethods.prototype.nameshake = function(othername){
this.othername = othername;
if (this.obj.name == this.othername){
console.log("We have the same name ," + this.obj.name + " and I! ");
}
else
console.log("We have different names ," + this.obj.name + " and I.");
}
var bob_def = { name: 'Bob', age: 21 };
var eve_def = { name: 'Eve', age: 21 };
var bob = new addPersonMethods(bob_def);
var eve = new addPersonMethods(eve_def);
var another_bob = new addPersonMethods({name:'Bob', age: 40});
console.log(bob.greet("Hi all"));
console.log(eve.greet("Hello"));
console.log(another_bob.greet("Hey"));
console.log(bob.nameshake(eve));
Your nameshake() method expects a string (the name), but you're passing an object, so the comparison will never be true. You want to compare to that object's .obj.name.
Second, you're logging the result of bob.nameshake(), when the function doesn't actually return anything.
And you're printing your own name in the "We have..." statements, when you want to print the other person's.
function addPersonMethods(obj) {
this.obj = obj;
}
addPersonMethods.prototype.greet = function(str) {
return str + ", my name is " + this.obj.name;
}
addPersonMethods.prototype.nameshake = function(otherperson) {
var othername = otherperson.obj.name;
if (this.obj.name == othername) {
console.log("We have the same name, " + othername + " and I! ");
}
else
console.log("We have different names, " + othername + " and I.");
}
var bob_def = {
name: 'Bob',
age: 21
};
var eve_def = {
name: 'Eve',
age: 21
};
var bob = new addPersonMethods(bob_def);
var eve = new addPersonMethods(eve_def);
var another_bob = new addPersonMethods({
name: 'Bob',
age: 40
});
console.log(bob.greet("Hi all"));
console.log(eve.greet("Hello"));
console.log(another_bob.greet("Hey"));
bob.nameshake(eve);
bob.nameshake(another_bob);
You are comparing a string (this.obj.name) to an object (othername). They won't be equal so you will always output that they have different names. The return value of any function by default is undefined unless you specify otherwise, so that's why your output is tailed by undefined.
Either pass in eve.obj.name to the function, or extract that value inside the function so you can compare properly.
I want to create a local variable dynamically. JavaScript: Dynamically Creating Variables for Loops is not exactly what I am looking for. I dont want an array. I want to access it like a local variable.
Something like:
<script type="text/javascript">
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
createVariables(properties);
function createVariables(properties)
{
// This function should somehow create variables in the calling function. Is there a way to do that?
}
document.write("Outside the function : " + var1 + "<br>");
document.write("Outside the function : " + var2 + "<br>");
</script>
I tried the following code.
<script type="text/javascript">
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
createVariables(properties);
function createVariables(properties)
{
for( var variable in properties)
{
try
{
eval(variable);
eval(variable + " = " + properties[variable] + ";");
}
catch(e)
{
eval("var " + variable + " = '" + properties[variable] + "';");
}
}
document.write("Inside the function : " + var1 + "<br>");
document.write("Inside the function : " + var2 + "<br>");
}
document.write("Outside the function : " + var1 + "<br>");
document.write("Outside the function : " + var2 + "<br>");
</script>
But the generated variables are not accessible outside the createVariables().
Now, I have this solution.
<script type="text/javascript">
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
function createVariables(properties)
{
var str = "";
for( var variable in properties)
{
str += "try{";
str += "eval('" + variable + "');";
str += "eval(\"" + variable + " = properties['" + variable + "'];\");";
str += "}";
str += "catch(e){";
str += "eval(\"var " + variable + " = properties['" + variable + "'];\");";
str += "}";
}
return str;
}
eval(createVariables(properties));
document.write("Outside the function : " + var1 + "<br>");
document.write("Outside the function : " + var2 + "<br>");
</script>
This works. But I am looking for an alternative/better solution. Is it possible to do it without eval?
EDIT: 04-July
Hi,
I tried a solution similar to what #Jonathan suggested.
<script type="text/javascript">
var startFunc = function(){
var self = this;
self.innerFunc = function innerFunc(){
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
properties["var3"] = "value3";
function createVariables(caller, props) {
for(i in props) {
caller[i] = props[i];
}
caller.func1();
}
createVariables(self, properties);
console.log( var1 );
}
self.func1 = function func1(){
console.log( "In func 1" );
console.log( var2 );
}
innerFunc();
console.log( var3 );
}
startFunc();
</script>
This all works fine. But it is actually creating global variables instead of creating the variables in the function.
The "self" passed to the createVariables() function is window. I am not sure why it is happening. I am assigning the function scope to the self. I am not sure what is happening here. It is anyway creating global variables in this case.
If my question is not clear,
What I am after is creating local variables in the caller. The scenario is like
1) I am inside a function.
2) I invoke another function which returns me a map[This map contains name and value of a variable].
3) I want to dynamically create all the variables, if they are not already defined. If they are already defined [global/local], I want to update them.
4) Once these variables are created, I should be able to access them without any context.[Just the variable name]
<script type="text/javascript">
function mainFunc()
{
var varibalesToBeCreated = getVariables();
createVariables(varibalesToBeCreated);
alert(var1);
alert(var2);
}
function createVariables(varibalesToBeCreated)
{
// How can I implement this function,
// such that the variables are created in the caller?
// I don't want these variables this function.
}
function getVariables()
{
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
}
mainFunc();
</script>
Depending on the scope you'd like the variables to have, this could be accomplished in a few different ways.
Global scope
To place the variables in the global scope, you could use window[varName]:
function createVariables(variables) {
for (var varName in variables) {
window[varName ] = variables[varName ];
}
}
createVariables({
'foo':'bar'
});
console.log(foo); // output: bar
Try it: http://jsfiddle.net/nLt5r/
Be advised, the global scope is a dirty, public place. Any script may read, write, or delete variables in this scope. Because of this fact, you run the risk of breaking a different script that uses the same variable names as yours, or another script breaking yours.
Function scope (using this)
To create variables in a function's scope (this.varName), you can use bind:
var variables = {
'foo':'bar'
};
var func = function () {
console.log(this.foo);
};
var boundFunc = func.bind(variables);
boundFunc(); // output: bar
Try it: http://jsfiddle.net/L4LbK/
Depending on what you do with the bound function reference, this method is slightly vulnerable to outside modification of the variables. Anything that can access boundFunc can change or refer to the value of the values by using boundFunc.varName = 'new value'; This may be to your advantage, depending on use case.
Function scope (using arguments)
You can use apply to pass an array of values as arguments:
var variables = [
'bar'
];
var func = function (foo) {
console.log('foo=', foo);
};
func.apply(null, variables);
Try it: http://jsfiddle.net/LKNqd/
As arguments are ephemeral in nature, nothing "outside" could interfere with or refer back to the values, except by modifying the variable array and re-calling the function.
Global scope as temporary
And here's a small utility function that will make temporary use of the global scope. This function is dangerous to code that also uses the global scope -- this could blast over variables that other scripts have created, use at your own risk:
var withVariables = function(func, vars) {
for (var v in vars){
this[v] = vars[v];
}
func();
for (var v in vars){
delete this[v];
}
};
// using an anonymous function
withVariables(
function () {
console.log('anonymous: ', foo);
},
{
'foo':'bar'
}
); // output: bar
// using a function reference
var myFunction =function () {
console.log('myFunction: ', foo);
};
withVariables(myFunction, {
'foo':'bar'
}); // output: bar
console.log(foo); // output: undefined
Try it: http://jsfiddle.net/X3p6k/3/
Documentation
bind on MDN - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
apply on MDN - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
window on MDN - https://developer.mozilla.org/en-US/docs/Web/API/Window
Here is working sample based on Chris Baker answer: Function scope (using arguments)
function myFn() {
function keyList(params) {
var str = '';
for (var key in params) {
if (params.hasOwnProperty(key)) {
str += ',' + key;
}
}
return str.length ? str.substring(1) : str;
}
function valueList(params) {
var list = [];
for (var key in params) {
if (params.hasOwnProperty(key)) {
list.push(params[key]);
}
}
return list;
}
var params = {
'var1': 'value1',
'var2': 'value2'
};
var expr = 'document.write("Inside the function : " + var1 + "<br>")'
var fn;
eval('var fn = function(' + keyList(params) + '){' + expr + '};');
fn(valueList(params));
}
myFn();
I have written short code snippet which will create both local and global variable dynamically
function createVar(name,dft){
this[name] = (typeof dft !== 'undefined')?dft:"";
}
createVar("name1","gaurav"); // it will create global variable
createVar("id");// it will create global variable
alert(name1);
alert(id);
function outer(){
var self = this;
alert(self.name1 + " inside");
}
createVar.call(outer,"name1","saurav"); // it will create local variable
outer.call(outer); // to point to local variable.
outer(); // to point to global variable
alert(name1);
hope this helps
Regards
Gaurav Khurana
The example below demonstrates how with gets a value from the object.
var obj = { a : "Hello" }
with(obj) {
alert(a) // Hello
}
But I want to notice: with is deprecated!
This answer is more or less the same as several answers above but here with a simplified sample, with and without using eval. First using eval (not recommended):
var varname = 'foo'; // pretend a user input that
var value = 42;
eval('var ' + varname + '=' + value);
And alternatively, without using eval:
var varname = prompt('Variable name:');
var value = 42;
this[varname] = value;
I hope this helps.
Source: https://www.rosettacode.org/wiki/Dynamic_variable_names#JavaScript
since you are wanting the scope of where the function is being called pass this to the function
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
function createVariables(context) {
for(i in properties) {
context[i] = properties[i];
}
}
createVariables(this);
console.log( var1 );
Do you need something like this?
function createVariables(properties, context){
for( var variable in properties){
context[variable] = properties[variable ];
}
}
So, calling as createVariables(properties, this) will fill the current scope with values from properties.
<script type="text/javascript">
var properties = new Object();
properties["var1"] = "value1";
properties["var2"] = "value2";
createVariables(properties, this);
document.write("Outside the function : " + var1 + "<br>");
document.write("Outside the function : " + var2 + "<br>");
</script>