Store a variable by reference in JavaScript - javascript

let myDivContent = document.querySelector(".myDiv").innerHTML;
myDivContent += "Hello";
myDivContent appears to be a variable that contains the content of the div. Is it possible for it to hold a reference to the innerHTML of the div in order for the code above to work as intended?

The magic of a "reference" can only be accomplished by a shared variable, property name or by a closure.
A closure is the only thing that can really do a reference the way you are imagining it. You'd need two functions. One for getting and one for setting:
get: () => thing
set: value => thing = value
This creates a "reference" to thing in the way that you are thinking.
The other issue is that += requires us to get and set the same value, so we'll need to use a property to achieve that. But properties need an object, you can't just have a bare property. So let me describe a few approaches:
Approach 1 - no closure
Here's one way you could use a property to create an object that refers indirectly to some other object (without a closure). Properties allow you to use the += syntax to modify an arbitrary value such as a string the way you demonstrated.
I'll define the reference to be an object and a property of that object.
class Ref {
constructor(obj, prop) {
this.obj = obj;
this.prop = prop;
}
get value() {
return this.obj[this.prop];
}
set value(val) {
return this.obj[this.prop] = val;
}
}
const person = { greeting: "Hello" };
const ref = new Ref(person, 'greeting');
ref.value += ' world';
console.log(person);
In the example above I've created an object { greeting: "Hello" } and stored it in a variable called person. Then I create a Ref that refers to that object and the greeting property name. You can say ref.value += 'something' because reading from ref.value calls the getter and writing to ref.value calls the setter. The += operator does both -- it reads and then writes, thus modifying the object.
This has limited flexibility and requires that you have an object and a property to refer to the thing that you want to be able to get/set.
Approach 2 - closure
The above Ref example is not flexible enough to refer to a local variable. For example, it doesn't have a way to modify the person variable, but it can modify the object that the person variable refers to. So, in general, if you need more flexibility, you could install properties on the fly and refer to the thing to be modified using a closure in the getter & setter with an arrow function like this:
var myLocalVariable = "Hello";
Object.defineProperty(this, "ref", {
get: () => myLocalVariable,
set: val => myLocalVariable = val
});
this.ref += ' world';
console.log(myLocalVariable);
You can't have a bare property, though, so you'll have to assign the getter/setter to an object. You can use this like I did above, but in cases where that is inappropriate, you'll have to create your own object to hold the property.
Approach 3 - just keep it simple
In your case, you may want to just keep a reference to the element myDiv rather than to its content myDivContent. This is flexible enough to refer to any object (but not to a variable). That may be good enough for most cases.
let myDiv = document.querySelector(".myDiv");
myDiv.innerHTML += " world";
<div class="myDiv">Hello</div>

document.querySelector(".myDiv").innerHTML doesnot holds a reference to the html content, instead it returns the a string that contains HTML serialization of the element's descendants.
So you cannot use like myDivContent += "Hello";.
The better apprach is to hold your selector in a variable and update its value as
let myDiv = document.querySelector(".myDiv");
myDiv.innerHTML += "Hello";
Here the variable myDiv holds a reference to the selector, hence you can update the innerHTML like above

Related

At what point does a function object have properties?

Functions being first class objects, it seems their local data at some point must have properties, but I'm confused on when/how this happens.
Take for example:
var output = function(x){
var s = "string";
delete s;
return s + " " + x;
};
console.log(output("bean"));
outputs string bean. I'm not sure if I expected it to delete s or not I was just messing around, because I thought var declared globally become a property of the window object.
I know delete doesn't work on local variables, and it only works on object properties. And because Functions are objects, I'm wondering at what point does local data in a function become a "property"
I'm wondering at what point does local data in a function become a "property"
It doesn't. You're confusing a function invocation with ordinary object-like interaction with the function.
Like all objects, functions can have arbitrary key-value properties associated with them eg:
var fn = function(x){
// do something
// not important
};
fn.foo = 'foo';
console.log(fn.foo);
Unless you explicitly assign a property to the function like this, the function won't have properties other than the usual ones that a function has (like .prototype).
Still, assigning such arbitrary properties to a function is pretty weird, and probably shouldn't be done in most cases.
Other properties that functions often have are .name (named function name), .length (refers to the number of arguments the function takes).
This is in contrast to non-object primitives, which cannot have key-value pairs assigned to them:
'use strict';
const someStr = 'foo';
someStr.bar = 'bar';
(though, when you try to reference a property on a primitive, the interpreter will turn it into an object by wrapping it in the appropriate prototype first, so that prototypal inheritance works with it)

Why doesnt javascript check object's local scope first?

Why doesnt the outer scope get accessed in the inner scope ?
I am coming from C++ world where any reference to an unqualified variable inside a class's method is attempted to be resolved first within the object's scope and then in the outer scope. And this happens without having to use "this" keyword.
For ex:
#include <iostream>
using namespace std;
std::string name = "Global::name";
class MyClass {
private:
string name = "MyClass::name";
public:
void printName() {
// No need to use 'this' keyword to refer to the variables in the
// object's scope, unless there is an ambiguity to resolve
cout << "Name from inside printName is: " << name << "\n";
}
};
int main()
{
MyClass obj;
cout << "Name from inside main is: " << name << "\n";
obj.printName();
return 0;
}
prints
Name from inside main is: Global::name
Name from inside printName is: MyClass::name
But in javascript, the following code snippet
function fn() {
let name1 = "fnB";
console.log("Inside fn() name is : ", name1);
}
var obj = {
name1: "objA",
objFn: function() {
console.log("Inside objFn() name is : ", name1); // ERROR !!
// console.log("Inside objFn() name is : ", this.name1); // OK !
}
}
fn();
obj.objFn();
results in
Uncaught ReferenceError: name1 is not defined
at Object.objFn (my.js:10)
What is the reason javascript doesnt want to refer to the "name1" variable in the scope of "obj" object, without requiring "this" keyword to refer to it ? What is the problem that is being solved by forcing the use of "this" keyword in this context ?
Every language is different and makes different tradeoffs. An obvious difference between C++ and JavaScript wrt class/object methods:
In JavaScript, every function is a standalone object. It doesn't strongly belong to anything.
In C++, class methods belongs the class. They cannot be invoked without it.
In JavaScript, every function is a closure, i.e. it has access to free variables defined in a "higher" lexical scope.
In C++, methods are not closures.
Why does this matter? Consider the following example:
var name = 42;
var obj = {
name: "objA",
objFn: function() {
console.log("Inside objFn() name is : ", name);
}
}
Which name should objFn access according to your expectation?
As it is now, the function would log 42, because that's how lexical scoping + closures work. In order to access the object's name property I have to write this.name.
Now lets assume it was the other way round, that object properties would be accessed before the outer scope. Then in order to explicitly access the outer scope's variables, i.e. 42, we would need some new API, e.g. getVariableFromScope('name'). This is worse than always requiring this for a simple reason: It makes it more difficult to reason about the code. By always requiring this, the rules are very simply:
Want to access a property on the object? this.<property>
Want to access a variable in scope? <variable>
In your case it would be:
Want to access a variable in scope? <variable>, but only if the object doesn't have a property with the same name, otherwise getVariableFromScope('<variable>').
Want to access a property on the object? <property>, but only if there is not a local variable with the same name, otherwise this.<property>.
One possible tradeoff here is consistency vs convenience.
Also consider the following example:
var foo = 42;
function bar() {
console.log(foo);
}
Calling bar() will log 42. Now lets assume I pass the function to some third-party code someOtherFunction(foo) which does:
function someOtherFunction(func) {
var obj = createObject();
obj.func = func;
obj.func();
}
Do you see the problem? The result of calling bar now depends on whether obj has a name property or not. To resolve this, either someOtherFunction needs to know which free variables bar contains or bar needs to know that someOtherFunction assigns it to some object and has to account for that. Either way, the code would be tightly coupled.
Doing what C++ or Java does would basically mean to introduce dynamic scope, and I assume there is a reason why very few languages use it.
(Someone might argue that this is also like dynamic scope. Well, this is a single keyword. It's easier to reason about that than to reason about the space of all possible variable names that could be overwritten.)
There are probably more reason why the behavior you are describing is not desirable in JavaScript. But again, programming language design is all about tradeoffs.
The this keyword behaves quite differently in javascript from how it does in many other languages. The value of this is not figured out until the function is invoked, and may not have anything to do with the object you think its associated with.
For example, consider the following code:
const obj = {
name: 'bob',
sayName: function () {
console.log(this.name);
}
}
const verbalize = obj.sayName; // Make another way to reference the function
console.log(verbalize === obj.sayName); // They're literally the same function
// And yet they log very different things
obj.sayName(); // logs 'bob'
verbalize(); // for me, it logs 1d7dcb5e-0fde-4726-8875-4bdcd636c6eb
Why does verbalize produce such a weird result? Well, since i'm invoking the function without specifying what this should be equal to, this defaults to the global window object, and so i end up logging window.name, which for me happens to be "1d7dcb5e-0fde-4726-8875-4bdcd636c6eb".
So if the language was set up to check this before checking other scopes, the actual result would be (in some cases) to check for global variables before local variables, which is the exact opposite of what we'd like to happen. Thus, this has to be done explicitly.
(ps: while this can be set to the window object in some cases, it can also be set to undefined if you're in strict mode)

Can JS have getters and setters methods named same as property?

I want to achieve behaviour like down in code:
function Foo(name) {
this.name = name;
};
var myFoo = new Foo('myName');
myFoo.name('newMyName'); // sets myFoo.name = 'newMyName'
myFoo.name(); // returns 'myName'
But it is obvious that in that case I'm overridding name property with name function. Is anyhow possible to achieve that functionality?
When talking about getters and setters in javascript you may be talking about one of two concepts:
1. Getters and setters as a concept like in any other OO language.
This is the case illustrated by the code in your question. In this case, an object's property is simply that, a property which may or be either an object or a function. javascript keeps track of both within the same namespace. Indeed, functions are just objects in javascript so there is no concept of a separate namespace for functions like you'd find in languages like C.
In this case "getters" and "setters" are just regular functions so the value needs to be stored separately. There are several strategies around this.
One is to use implicit getSomething() and setSomething() style functions commonly found in Java. This allows you to disambiguate the getters and setters from the property name since the getters and setters have the word "get" and "set" added to the name.
The second strategy is the one you've written in your question. In this case you need to store the property in another name so as not to share the same name with the getter/setter.
The third strategy is to store the value in a closure:
function Foo (name) {
var name = name;
this.name = function (str) {
if (str !== undefined) name = str;
return name;
}
}
Note that in the code above the value is stored in name but the getter/setter is this.name which is a completely different variable. This allows your example code to work as you expected:
var me = new Foo('Mark');
me.name(); // returns Mark
me.name('Andy'); // sets name to Andy
2. Getters and setters as a mechanism in javascript.
This is a feature of newer versions of javascript that follows the ECMAscript 5 specification. This feature allows properties to execute code when reading or writing to it similar to how the .innerHTML property of DOM object invokes the HTML parser when you assign something to it.
The syntax of getters and setters is similar to functions but introduces the get and set keywords in place of function.
A simple example of a property with getter and setter:
var me = {
first_name : "",
last_name : "",
get name() {
return this.first_name + " " + this.last_name;
},
set name(str) {
var n = str.split(/\s+/);
this.first_name = n.shift();
this.last_name = n.join(' ');
}
}
The code above allows you to treat the functions to get and set the first_name and last_name as if it is a variable instead of a function. To use the name getter and setter you'd simply do:
me.name = "James Bond";
alert(me.first_name); // should alert James
alert(me.last_name); // should alert Bond
me.last_name = "Dean";
alert(me.name); // should alert James Dean
Using the javascript get/set mechanism, you can't store the value in the object using the same name. For example:
var foo = {
set bar(x) {this.bar=x}
}
The code above will compile but trying to set bar: foo.bar = 1 will cause a stack overflow because of the infinite loop - the this.bar= inside the setter will call the setter again.
If you want to use JavaScript getter/setter with the same name as the properties, e.g. to intercept certain setters to implement side effects, you can create a Proxy for your object.
function editableProxy (myObj) {
return new Proxy(myObj, {
toJSON: () => myObj,
get: function (target, prop) {
return Reflect.get(myObj, prop);
},
set: function (target, prop, receiver) {
if (prop === 'billTo') {
myObj.billToId = receiver?.id;
}
return Reflect.set(myObj, prop, receiver);
},
});
};
All getters work normally. Setters work normally, but if you set a billTo it also sets a billToId.
let da = {id:123}
let wrapped = editableProxy(da)
let id = wrapped.id // 123
wrapped.id=234
wrapped.id===da.id // true
wrapped.billTo={id:567,name:'Big Bad Bill'} // triggers setter side effect
wrapped.billToId // 567

Is "this" necessary in javascript apart from variable definition

My question is dead simple.
I just casually discovered that once you have defined a property with this. into an object, you don't need to prepend this. anymore when you want to call them.
So this. is really meant to be used ad definition time, like var?
I found it my self shortly after, i was referencing the window object with this. since i called my object without using new, so like it was a function.
One extra question, maybe for comments. Inside the main object, if i create a new object, and use this during the object definition, this this what will be referring to?
No, unless the context of this is a global object, such as window. Take the following example:
function Foo(bar) {
this.data = bar;
console.log(this.data); // OK
console.log(data); // ReferenceError
}
In this example, you'll get a ReferenceError: data is not defined on the first console.log(data), unless, data is a global variable. To access the instance's public member, you have to use this.data.
References:
Understanding JavaScript’s this keyword
The this keyword
There are all sorts of circumstances where you MUST use this in order to reference the right data.
These two implementations do very different things:
Array.prototype.truncate(newLen) {
// sets the length property on the current Array object
this.length = newLen;
}
Array.prototype.truncate(newLen) {
// sets a global variable named length
length = newLen;
}
var arr = [1,2,3,4,5,6,7];
arr.truncate(2);
You MUST use this in order to control what happens if you want to modify the current object. Your assumption that you can leave it off and it will still modify the current object's properties is not correct. If you leave it off, you are modifying global variables, not member properties.
So this. is really meant to be used ad definition time, like var?
No, the point of this is to be the current scope of execution. You can (and will) run into weird errors if you don't use this. For example, imagine you are an object with a property val and then on the prototype of that object you have
App.obj = function(){
this.val = 'initial';
}
obj.prototype.myMethod = function(val) {
// how would you assign argument val to object val?
}
also note that your reasoning breaks down with methods.
obj.prototype.meth2 = function(){
myMethod(); // fails where this.myMethod() would work.
}
See http://jsfiddle.net/BRsqH/:
function f(){
this.public='hello!';
var hidden='TOP SECRET!';
}
var instance=new f();
alert('Public data: '+instance.public+ /* gives "hello!" */
'\nHidden data: '+instance.hidden /* gives undefined */
);
Variables created with var are hidden and cannot be viewed nor modified outside the function which created them.
But variables created with this are public, so you can access them outside the function.
I think I got it.
I defined my object as function My_Object(){...} and then called it with MyObject(). This way the My_Object was treated as a function, not an object and therefore this == window.
So in the end I was attaching properties and methods to window instead of My_Object! That's way there were available without prepending .this.
The right way to initialize My_Object as an object is to call it like this new My_Object, isn't right?

How to get class object's name as a string in Javascript?

Let's say I instantiate an object in Javascript like this:
var myObj = new someObject();
Now, is it possible to obtain the var object's name as string 'myObj' from within one of the class methods?
Additional details (edited):
The reason why I would like to get the name of the variable holding reference to the object is that my new myObj would create a new clickable DIV on the page that would need to call a function myObj.someFunction(). As I insert the new DIV I need to know the name of the variable holding reference to the object. Is there maybe a better way of doing this?
You are right, sorry for the mixup in terminology.
The reason why I would like to get the name of the variable holding reference to the object is that my new myObj would create a new clickable DIV on the page that would need to call a function myObj.someFunction(). As I insert the new DIV I need to know the name of the variable holding reference to the object. Is there maybe a better way of doing this?
Shog9 is right that this doesn't make all that much sense to ask, since an object could be referred to by multiple variables. If you don't really care about that, and all you want is to find the name of one of the global variables that refers to that object, you could do the following hack:
function myClass() {
this.myName = function () {
// search through the global object for a name that resolves to this object
for (var name in this.global)
if (this.global[name] == this)
return name
}
}
// store the global object, which can be referred to as this at the top level, in a
// property on our prototype, so we can refer to it in our object's methods
myClass.prototype.global = this
// create a global variable referring to an object
var myVar = new myClass()
myVar.myName() // returns "myVar"
Note that this is an ugly hack, and should not be used in production code. If there is more than one variable referring to an object, you can't tell which one you'll get. It will only search the global variables, so it won't work if a variable is local to a function. In general, if you need to name something, you should pass the name in to the constructor when you create it.
edit: To respond to your clarification, if you need to be able to refer to something from an event handler, you shouldn't be referring to it by name, but instead add a function that refers to the object directly. Here's a quick example that I whipped up that shows something similar, I think, to what you're trying to do:
function myConstructor () {
this.count = 0
this.clickme = function () {
this.count += 1
alert(this.count)
}
var newDiv = document.createElement("div")
var contents = document.createTextNode("Click me!")
// This is the crucial part. We don't construct an onclick handler by creating a
// string, but instead we pass in a function that does what we want. In order to
// refer to the object, we can't use this directly (since that will refer to the
// div when running event handler), but we create an anonymous function with an
// argument and pass this in as that argument.
newDiv.onclick = (function (obj) {
return function () {
obj.clickme()
}
})(this)
newDiv.appendChild(contents)
document.getElementById("frobnozzle").appendChild(newDiv)
}
window.onload = function () {
var myVar = new myConstructor()
}
Short answer: No. myObj isn't the name of the object, it's the name of a variable holding a reference to the object - you could have any number of other variables holding a reference to the same object.
Now, if it's your program, then you make the rules: if you want to say that any given object will only be referenced by one variable, ever, and diligently enforce that in your code, then just set a property on the object with the name of the variable.
That said, i doubt what you're asking for is actually what you really want. Maybe describe your problem in a bit more detail...?
Pedantry: JavaScript doesn't have classes. someObject is a constructor function. Given a reference to an object, you can obtain a reference to the function that created it using the constructor property.
In response to the additional details you've provided:
The answer you're looking for can be found here: JavaScript Callback Scope (and in response to numerous other questions on SO - it's a common point of confusion for those new to JS). You just need to wrap the call to the object member in a closure that preserves access to the context object.
You can do it converting by the constructor to a string using .toString() :
function getObjectClass(obj){
if (typeof obj != "object" || obj === null) return false;
else return /(\w+)\(/.exec(obj.constructor.toString())[1];}
You might be able to achieve your goal by using it in a function, and then examining the function's source with toString():
var whatsMyName;
// Just do something with the whatsMyName variable, no matter what
function func() {var v = whatsMyName;}
// Now that we're using whatsMyName in a function, we could get the source code of the function as a string:
var source = func.toString();
// Then extract the variable name from the function source:
var result = /var v = (.[^;]*)/.exec(source);
alert(result[1]); // Should alert 'whatsMyName';
If you don't want to use a function constructor like in Brian's answer you can use Object.create() instead:-
var myVar = {
count: 0
}
myVar.init = function(n) {
this.count = n
this.newDiv()
}
myVar.newDiv = function() {
var newDiv = document.createElement("div")
var contents = document.createTextNode("Click me!")
var func = myVar.func(this)
newDiv.addEventListener ?
newDiv.addEventListener('click', func, false) :
newDiv.attachEvent('onclick', func)
newDiv.appendChild(contents)
document.getElementsByTagName("body")[0].appendChild(newDiv)
}
myVar.func = function (thys) {
return function() {
thys.clickme()
}
}
myVar.clickme = function () {
this.count += 1
alert(this.count)
}
myVar.init(2)
var myVar1 = Object.create(myVar)
myVar1.init(55)
var myVar2 = Object.create(myVar)
myVar2.init(150)
// etc
Strangely, I couldn't get the above to work using newDiv.onClick, but it works with newDiv.addEventListener / newDiv.attachEvent.
Since Object.create is newish, include the following code from Douglas Crockford for older browsers, including IE8.
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o
return new F()
}
}
As a more elementary situation it would be nice IF this had a property that could reference it's referring variable (heads or tails) but unfortunately it only references the instantiation of the new coinSide object.
javascript: /* it would be nice but ... a solution NOT! */
function coinSide(){this.ref=this};
/* can .ref be set so as to identify it's referring variable? (heads or tails) */
heads = new coinSide();
tails = new coinSide();
toss = Math.random()<0.5 ? heads : tails;
alert(toss.ref);
alert(["FF's Gecko engine shows:\n\ntoss.toSource() is ", toss.toSource()])
which always displays
[object Object]
and Firefox's Gecko engine shows:
toss.toSource() is ,#1={ref:#1#}
Of course, in this example, to resolve #1, and hence toss, it's simple enough to test toss==heads and toss==tails. This question, which is really asking if javascript has a call-by-name mechanism, motivates consideration of the counterpart, is there a call-by-value mechanism to determine the ACTUAL value of a variable? The example demonstrates that the "values" of both heads and tails are identical, yet alert(heads==tails) is false.
The self-reference can be coerced as follows:
(avoiding the object space hunt and possible ambiguities as noted in the How to get class object's name as a string in Javascript? solution)
javascript:
function assign(n,v){ eval( n +"="+ v ); eval( n +".ref='"+ n +"'" ) }
function coinSide(){};
assign("heads", "new coinSide()");
assign("tails", "new coinSide()");
toss = Math.random()<0.5 ? heads : tails;
alert(toss.ref);
to display heads or tails.
It is perhaps an anathema to the essence of Javascript's language design, as an interpreted prototyping functional language, to have such capabilities as primitives.
A final consideration:
javascript:
item=new Object(); refName="item"; deferAgain="refName";
alert([deferAgain,eval(deferAgain),eval(eval(deferAgain))].join('\n'));
so, as stipulated ...
javascript:
function bindDIV(objName){
return eval( objName +'=new someObject("'+objName+'")' )
};
function someObject(objName){
this.div="\n<DIV onclick='window.opener."+ /* window.opener - hiccup!! */
objName+
".someFunction()'>clickable DIV</DIV>\n";
this.someFunction=function(){alert(['my variable object name is ',objName])}
};
with(window.open('','test').document){ /* see above hiccup */
write('<html>'+
bindDIV('DIVobj1').div+
bindDIV('DIV2').div+
(alias=bindDIV('multiply')).div+
'an aliased DIV clone'+multiply.div+
'</html>');
close();
};
void (0);
Is there a better way ... ?
"better" as in easier? Easier to program? Easier to understand? Easier as in faster execution? Or is it as in "... and now for something completely different"?
Immediately after the object is instantiatd, you can attach a property, say name, to the object and assign the string value you expect to it:
var myObj = new someClass();
myObj.name="myObj";
document.write(myObj.name);
Alternatively, the assignment can be made inside the codes of the class, i.e.
var someClass = function(P)
{ this.name=P;
// rest of the class definition...
};
var myObj = new someClass("myObj");
document.write(myObj.name);
Some time ago, I used this.
Perhaps you could try:
+function(){
var my_var = function get_this_name(){
alert("I " + this.init());
};
my_var.prototype.init = function(){
return my_var.name;
}
new my_var();
}();
Pop an Alert: "I get_this_name".
This is pretty old, but I ran across this question via Google, so perhaps this solution might be useful to others.
function GetObjectName(myObject){
var objectName=JSON.stringify(myObject).match(/"(.*?)"/)[1];
return objectName;
}
It just uses the browser's JSON parser and regex without cluttering up the DOM or your object too much.

Categories

Resources