Replace object value without replacing reference - javascript

How can you update an entire object, say:
var x = {a:1}
function modify(obj) {
obj = {b:2}
}
modify(x)
console.log(x) // {a:1}
But maintain the reference? I want the object to be modified outside the function.
My specific case is using lodash.pick inside my function:
if (whitelist) {
obj = _.pick(obj, whitelist)
}
I can't seem to find a pick function that modifies the object. Is there a way to do this or do I need to start returning copies of the object?

delete everything from the old object, and then add new properties, key-by-key:
function modify(obj, newObj) {
Object.keys(obj).forEach(function(key) {
delete obj[key];
});
Object.keys(newObj).forEach(function(key) {
obj[key] = newObj[key];
});
}
var x = {a:1}
modify(x, {b:42})
document.write(JSON.stringify(x));
If you're wondering whether it's a good idea in general, the answer is no. Construct a new object, return it from the function and assign - this is a much preferred way.

You can achieve this (not exactly what you want) if you wrap your object and change the modify function like this,
var wrapper = {};
wrapper.x = {a:1};
function modify(obj, key) {
obj[key] = {b:2};
}
modify(wrapper, 'x');
console.log(wrapper.x); // {b:2}

/**
* Method to deep update the values of the source object from new object without changing source object reference
*
* #export
* #param {*} sourceObj
* #param {*} newObj
*/
export function updateObjKeepingRef(sourceObj: any, newObj: any): void {
Object.keys(newObj).forEach(key => {
// if value is object and instance is not Date
if (newObj[key] && typeof newObj[key] === 'object' && sourceObj[key] && !(newObj[key] instanceof Date)) {
updateObjKeepingRef(sourceObj[key], newObj[key]);
} else {
// updating properties
sourceObj[key] = newObj[key];
}
});
}

Why does modify not edit the object referred to by obj?
Because inside modify when you write:
obj = {b:2}
Note that obj is a local variable to the function call of modify. A new object {b:2} is created, and the local variable obj now refers to this new object. Recall that the variable x still refers to the {a:1} object.
If x is a global variable AND if there is no local variable inside the function by the name, then you can do:
var x = {a:1};
function modify() {
x = {b:2}
}
modify();
console.log(x) // {b:2}
Why does the above code work?
When you call modify(), it tries to see if it has a local variable x, since it could not find one, it looks up in the scope chain. The next scope to look for this function call is the global scope, and sure enough, there we have a x variable. Hence this global variable x's value is set now.

I was having this issue. After some research, I opted to convert objectTwo to a JSON string, and then replace objectOne with the parsed version. In other words:
var mObjectOne = { ExampleProperty: 10 };
var mObjectTwo = { ExampleProperty: 15 };
// I wanted mObjectOne to hold the same data as mObjectTwo, but keep a separate reference
var mObjectTwoAsJSON = JSON.stringify(mObjectTwo);
mObjectOne = JSON.parse(mObjectTwoAsJSON);
// Now the references are still different, as desired, but the object data has been updated.
Thanks,

obj = JSON.parse(JSON.stringify(newObj))

Related

Is it possible to change a reference that is passed into a function? [duplicate]

How do I pass variables by reference in JavaScript?
I have three variables that I want to perform several operations to, so I want to put them in a for loop and perform the operations to each one.
Pseudocode:
myArray = new Array(var1, var2, var3);
for (var x = 0; x < myArray.length; x++){
// Do stuff to the array
makePretty(myArray[x]);
}
// Now do stuff to the updated variables
What is the best way to do this?
There is no "pass by reference" available in JavaScript. You can pass an object (which is to say, you can pass-by-value a reference to an object) and then have a function modify the object contents:
function alterObject(obj) {
obj.foo = "goodbye";
}
var myObj = { foo: "hello world" };
alterObject(myObj);
alert(myObj.foo); // "goodbye" instead of "hello world"
You can iterate over the properties of an array with a numeric index and modify each cell of the array, if you want.
var arr = [1, 2, 3];
for (var i = 0; i < arr.length; i++) {
arr[i] = arr[i] + 1;
}
It's important to note that "pass-by-reference" is a very specific term. It does not mean simply that it's possible to pass a reference to a modifiable object. Instead, it means that it's possible to pass a simple variable in such a way as to allow a function to modify that value in the calling context. So:
function swap(a, b) {
var tmp = a;
a = b;
b = tmp; //assign tmp to b
}
var x = 1, y = 2;
swap(x, y);
alert("x is " + x + ", y is " + y); // "x is 1, y is 2"
In a language like C++, it's possible to do that because that language does (sort-of) have pass-by-reference.
edit — this recently (March 2015) blew up on Reddit again over a blog post similar to mine mentioned below, though in this case about Java. It occurred to me while reading the back-and-forth in the Reddit comments that a big part of the confusion stems from the unfortunate collision involving the word "reference". The terminology "pass by reference" and "pass by value" predates the concept of having "objects" to work with in programming languages. It's really not about objects at all; it's about function parameters, and specifically how function parameters are "connected" (or not) to the calling environment. In particular, note that in a true pass-by-reference language — one that does involve objects — one would still have the ability to modify object contents, and it would look pretty much exactly like it does in JavaScript. However, one would also be able to modify the object reference in the calling environment, and that's the key thing that you can't do in JavaScript. A pass-by-reference language would pass not the reference itself, but a reference to the reference.
edit — here is a blog post on the topic. (Note the comment to that post that explains that C++ doesn't really have pass-by-reference. That is true. What C++ does have, however, is the ability to create references to plain variables, either explicitly at the point of function invocation to create a pointer, or implicitly when calling functions whose argument type signature calls for that to be done. Those are the key things JavaScript doesn't support.)
Primitive type variables like strings and numbers are always passed by value.
Arrays and Objects are passed by reference or by value based on these conditions:
if you are setting the value of an object or array it is Pass by Value.
object1 = { prop: "car" };
array1 = [1,2,3];
if you are changing a property value of an object or array then it is Pass by Reference.
object1.prop = "car";
array1[0] = 9;
Code
function passVar(obj1, obj2, num) {
obj1.prop = "laptop"; // will CHANGE original
obj2 = { prop: "computer" }; //will NOT affect original
num = num + 1; // will NOT affect original
}
var object1 = {
prop: "car"
};
var object2 = {
prop: "bike"
};
var number1 = 10;
passVar(object1, object2, number1);
console.log(object1); // output: Object { prop: "laptop" }
console.log(object2); // output: Object { prop: "bike" }
console.log(number1); // ouput: 10
Workaround to pass variable like by reference:
var a = 1;
inc = function(variableName) {
window[variableName] += 1;
};
inc('a');
alert(a); // 2
And yup, actually you can do it without access a global variable:
inc = (function () {
var variableName = 0;
var init = function () {
variableName += 1;
alert(variableName);
}
return init;
})();
inc();
Simple Object
function foo(x) {
// Function with other context
// Modify `x` property, increasing the value
x.value++;
}
// Initialize `ref` as object
var ref = {
// The `value` is inside `ref` variable object
// The initial value is `1`
value: 1
};
// Call function with object value
foo(ref);
// Call function with object value again
foo(ref);
console.log(ref.value); // Prints "3"
Custom Object
Object rvar
/**
* Aux function to create by-references variables
*/
function rvar(name, value, context) {
// If `this` is a `rvar` instance
if (this instanceof rvar) {
// Inside `rvar` context...
// Internal object value
this.value = value;
// Object `name` property
Object.defineProperty(this, 'name', { value: name });
// Object `hasValue` property
Object.defineProperty(this, 'hasValue', {
get: function () {
// If the internal object value is not `undefined`
return this.value !== undefined;
}
});
// Copy value constructor for type-check
if ((value !== undefined) && (value !== null)) {
this.constructor = value.constructor;
}
// To String method
this.toString = function () {
// Convert the internal value to string
return this.value + '';
};
} else {
// Outside `rvar` context...
// Initialice `rvar` object
if (!rvar.refs) {
rvar.refs = {};
}
// Initialize context if it is not defined
if (!context) {
context = this;
}
// Store variable
rvar.refs[name] = new rvar(name, value, context);
// Define variable at context
Object.defineProperty(context, name, {
// Getter
get: function () { return rvar.refs[name]; },
// Setter
set: function (v) { rvar.refs[name].value = v; },
// Can be overrided?
configurable: true
});
// Return object reference
return context[name];
}
}
// Variable Declaration
// Declare `test_ref` variable
rvar('test_ref_1');
// Assign value `5`
test_ref_1 = 5;
// Or
test_ref_1.value = 5;
// Or declare and initialize with `5`:
rvar('test_ref_2', 5);
// ------------------------------
// Test Code
// Test Function
function Fn1(v) { v.value = 100; }
// Test
function test(fn) { console.log(fn.toString()); console.info(fn()); }
// Declare
rvar('test_ref_number');
// First assign
test_ref_number = 5;
test(() => test_ref_number.value === 5);
// Call function with reference
Fn1(test_ref_number);
test(() => test_ref_number.value === 100);
// Increase value
test_ref_number++;
test(() => test_ref_number.value === 101);
// Update value
test_ref_number = test_ref_number - 10;
test(() => test_ref_number.value === 91);
Yet another approach to pass any (local, primitive) variables by reference is by wrapping variable with closure "on the fly" by eval. This also works with "use strict". (Note: be aware that eval is not friendly to JavaScript optimizers, and also missing quotes around variable name may cause unpredictive results)
"use strict"
// Return text that will reference variable by name (by capturing that variable to closure)
function byRef(varName){
return "({get value(){return "+varName+";}, set value(v){"+varName+"=v;}})";
}
// Demo
// Assign argument by reference
function modifyArgument(argRef, multiplier){
argRef.value = argRef.value * multiplier;
}
(function(){
var x = 10;
alert("x before: " + x);
modifyArgument(eval(byRef("x")), 42);
alert("x after: " + x);
})()
Live sample: https://jsfiddle.net/t3k4403w/
There's actually a pretty sollution:
function updateArray(context, targetName, callback) {
context[targetName] = context[targetName].map(callback);
}
var myArray = ['a', 'b', 'c'];
updateArray(this, 'myArray', item => {return '_' + item});
console.log(myArray); //(3) ["_a", "_b", "_c"]
I personally dislike the "pass by reference" functionality offered by various programming languages. Perhaps that's because I am just discovering the concepts of functional programming, but I always get goosebumps when I see functions that cause side effects (like manipulating parameters passed by reference). I personally strongly embrace the "single responsibility" principle.
IMHO, a function should return just one result/value using the return keyword. Instead of modifying a parameter/argument, I would just return the modified parameter/argument value and leave any desired reassignments up to the calling code.
But sometimes (hopefully very rarely), it is necessary to return two or more result values from the same function. In that case, I would opt to include all those resulting values in a single structure or object. Again, processing any reassignments should be up to the calling code.
Example:
Suppose passing parameters would be supported by using a special keyword like 'ref' in the argument list. My code might look something like this:
//The Function
function doSomething(ref value) {
value = "Bar";
}
//The Calling Code
var value = "Foo";
doSomething(value);
console.log(value); //Bar
Instead, I would actually prefer to do something like this:
//The Function
function doSomething(value) {
value = "Bar";
return value;
}
//The Calling Code:
var value = "Foo";
value = doSomething(value); //Reassignment
console.log(value); //Bar
When I would need to write a function that returns multiple values, I would not use parameters passed by reference either. So I would avoid code like this:
//The Function
function doSomething(ref value) {
value = "Bar";
//Do other work
var otherValue = "Something else";
return otherValue;
}
//The Calling Code
var value = "Foo";
var otherValue = doSomething(value);
console.log(value); //Bar
console.log(otherValue); //Something else
Instead, I would actually prefer to return both new values inside an object, like this:
//The Function
function doSomething(value) {
value = "Bar";
//Do more work
var otherValue = "Something else";
return {
value: value,
otherValue: otherValue
};
}
//The Calling Code:
var value = "Foo";
var result = doSomething(value);
value = result.value; //Reassignment
console.log(value); //Bar
console.log(result.otherValue);
These code examples are quite simplified, but it roughly demonstrates how I personally would handle such stuff. It helps me to keep various responsibilities in the correct place.
Happy coding. :)
I've been playing around with syntax to do this sort of thing, but it requires some helpers that are a little unusual. It starts with not using 'var' at all, but a simple 'DECLARE' helper that creates a local variable and defines a scope for it via an anonymous callback. By controlling how variables are declared, we can choose to wrap them into objects so that they can always be passed by reference, essentially. This is similar to one of the Eduardo Cuomo's answer above, but the solution below does not require using strings as variable identifiers. Here's some minimal code to show the concept.
function Wrapper(val){
this.VAL = val;
}
Wrapper.prototype.toString = function(){
return this.VAL.toString();
}
function DECLARE(val, callback){
var valWrapped = new Wrapper(val);
callback(valWrapped);
}
function INC(ref){
if(ref && ref.hasOwnProperty('VAL')){
ref.VAL++;
}
else{
ref++;//or maybe throw here instead?
}
return ref;
}
DECLARE(5, function(five){ //consider this line the same as 'let five = 5'
console.log("five is now " + five);
INC(five); // increment
console.log("five is incremented to " + five);
});
Actually it is really easy. The problem is understanding that once passing classic arguments, you are scoped into another, read-only zone.
The solution is to pass the arguments using JavaScript's object-oriented design. It is the same as putting the arguments in a global/scoped variable, but better...
function action(){
/* Process this.arg, modification allowed */
}
action.arg = [["empty-array"], "some string", 0x100, "last argument"];
action();
You can also promise stuff up to enjoy the well-known chain:
Here is the whole thing, with promise-like structure
function action(){
/* Process this.arg, modification allowed */
this.arg = ["a", "b"];
}
action.setArg = function(){this.arg = arguments; return this;}
action.setArg(["empty-array"], "some string", 0x100, "last argument")()
Or better yet...
action.setArg(["empty-array"],"some string",0x100,"last argument").call()
JavaScript can modify array items inside a function (it is passed as a reference to the object/array).
function makeAllPretty(items) {
for (var x = 0; x < myArray.length; x++){
// Do stuff to the array
items[x] = makePretty(items[x]);
}
}
myArray = new Array(var1, var2, var3);
makeAllPretty(myArray);
Here's another example:
function inc(items) {
for (let i=0; i < items.length; i++) {
items[i]++;
}
}
let values = [1,2,3];
inc(values);
console.log(values);
// Prints [2,3,4]
Putting aside the pass-by-reference discussion, those still looking for a solution to the stated question could use:
const myArray = new Array(var1, var2, var3);
myArray.forEach(var => var = makePretty(var));
As we don't have javascript pass by reference functionality, the only way to do this is to make the function return the value and let the caller assign it:
So
"makePretty(myArray[x]);"
should be
"myArray[x] = makePretty(myArray[x]);"
This is in case you need assignment inside the function, if only mutation is necessary, then passing the object and mutating it should be enough
I know exactly what you mean. The same thing in Swift will be no problem. The bottom line is use let, not var.
The fact that primitives are passed by value, but the fact that the value of var i at the point of iteration is not copied into the anonymous function is quite surprising to say the least.
for (let i = 0; i < boxArray.length; i++) {
boxArray[i].onclick = function() { console.log(i) }; // Correctly prints the index
}
If you want to pass variables by reference, a better way to do that is by passing your arguments in an object and then start changing the value by using window:
window["varName"] = value;
Example:
// Variables with first values
var x = 1, b = 0, f = 15;
function asByReference (
argumentHasVars = {}, // Passing variables in object
newValues = []) // Pass new values in array
{
let VarsNames = [];
// Getting variables names one by one
for(let name in argumentHasVars)
VarsNames.push(name);
// Accessing variables by using window one by one
for(let i = 0; i < VarsNames.length; i += 1)
window[VarsNames[i]] = newValues[i]; // Set new value
}
console.log(x, b, f); // Output with first values
asByReference({x, b, f}, [5, 5, 5]); // Passing as by reference
console.log(x, b, f); // Output after changing values
I like to solve the lack of by reference in JavaScript like this example shows.
The essence of this is that you don't try to create a by reference. You instead use the return functionality and make it able to return multiple values. So there isn't any need to insert your values in arrays or objects.
var x = "First";
var y = "Second";
var z = "Third";
log('Before call:',x,y,z);
with (myFunc(x, y, z)) {x = a; y = b; z = c;} // <-- Way to call it
log('After call :',x,y,z);
function myFunc(a, b, c) {
a = "Changed first parameter";
b = "Changed second parameter";
c = "Changed third parameter";
return {a:a, b:b, c:c}; // <-- Return multiple values
}
function log(txt,p1,p2,p3) {
document.getElementById('msg').innerHTML += txt + '<br>' + p1 + '<br>' + p2 + '<br>' + p3 + '<br><br>'
}
<div id='msg'></div>
Using Destructuring here is an example where I have 3 variables, and on each I do the multiple operations:
If value is less than 0 then change to 0,
If greater than 255 then change to 1,
Otherwise dived the number by 255 to convert from a range of 0-255 to a range of 0-1.
let a = 52.4, b = -25.1, c = 534.5;
[a, b, c] = [a, b, c].map(n => n < 0 ? 0 : n > 255 ? 1 : n / 255);
console.log(a, b, c); // 0.20549019607843136 0 1

Does a Javascript function return objects by reference or value by default?

I have an object defined outside the function, in a global scope. This object is not passed into the function as an argument, but the function does modify it and return the modified object.
What I wanted to know is, if the function returns a copy of the object, or the original global object?
Also, will passing that object to the function as an argument, make a difference, since objects are passed into functions by reference?
Whenever you're returning an object, you're returning a reference to the object. Likewise, when you're passing an object, you're passing a reference. However, passing an object in as an argument can be different than just changing an object in global scope, as these examples show. This is because the reference to the object is itself passed by value.
If you're changing the members of an object, then whether you pass it in as an argument or just update the global object makes no difference. Either way, you're working with the same object.
Example 1:
var object = {foo:'original'};
function changeObject() {
object.foo = 'changed';
return object;
}
console.log(changeObject()); // outputs {foo:'changed'}
console.log(object); // outputs {foo:'changed'}
Example 2:
var object = {foo:'original'};
function changeArgument(object) {
object.foo = 'changed';
return object;
}
console.log(changeArgument(object)); // outputs {foo:'changed'}
console.log(object); // outputs {foo:'changed'}
On the other hand, if you're overwriting the object with a new object, the change won't persist if you do it to the argument, but will persist if you do it to the global object. That's because the argument passes the reference to the object by value. Once you replace this value with a reference to a new object, you're not talking about the same object anymore.
Example 3:
var object = {foo:'original'};
function replaceObject() {
object = {foo:'changed'};
return object;
}
console.log(replaceObject()); // outputs {foo:'changed'}
console.log(object); // outputs {foo:'changed'}
Example 4:
var object = {foo:'original'};
function replaceArgument(object) {
object = {foo:'changed'};
return object;
}
console.log(replaceArgument(object)); // outputs {foo:'changed'}
console.log(object); // outputs {foo:'original'}
May be late comment, but this is typical challenge in any language.
Objects created on the heap and passed around by reference opposed to primitives(by value).
I think the root of the question is shared instance versus unique one to avoid unwelcome effects.
For example we calling a function to get a template(object) for new user to add to collection or want to clear the form
on cancel event from different modules to start over. It easy to understand and easy to overlook..test cases typically
not covering all usage permutations
The sanity checklist:
Here the shared instance:
var bigo = {
usr: { name: 'steven' },
bigi: function () {
return this.usr;
}
};
var outA = bigo.bigi();
var outB = bigo.bigi();
print(outA.name); // => steven
print(outB.name); // => steven
outA.name = 'ilan'; // change value
print(outA.name); // => ilan
print(outB.name); // => ilan
Non shared instance:
var bigo = {
bigi: function () {
var user = { name: 'steven' };
return user;
}
};
var outA = bigo.bigi();
var outB = bigo.bigi();
print(outA.name); // => steven
print(outB.name); // => steven
outA.name = 'ilan'; // change value
print(outA.name); // => ilan
print(outB.name); // => steven
What I wanted to know is, if the function returns a copy of the object, or the original global object?
Effectively, you only ever deal with references to objects in JavaScript. Even var foo = {} just assigns a reference to a new object to foo.
If the object is outside the function, you don't need to 'return' it. If you modify the object within the function it will update the object itself. Then you can reference the newly updated object in other functions as needed.
From your question this is how I think your code looks (more or less):
var o = {};
function f() {
o.prop = true;
return o;
}
In this case the global variable o references an object.
When you modify o you're modify whatever o references. Hence it modifies the original object.
When you return o you're returning a reference to the original object.
Passing the object to a function results in the reference to the original object being passed. Hence any modifications will affect the original object. For example:
var o = {};
f(o);
console.log(o.prop); // true
function f(o) {
o.prop = true;
}

JavaScript - How/Can I set an object reference to null from a function?

I'm wondering if this is even possible. Basically I have a couple objects that I pass to a function, and under certain conditions I want that function to set the object to null.
Ex.
var o = {'val' : 0};
f = function(v)
{
v = null;
};
f(o); // Would like this to set 'o' to null
Unfortunately it seems I can only set the function's argument to null. After calling the function 'o' will still refer to an object.
So, is it even possible to do this? And if so, how?
If you want to change the value of o when f(o) is called, you have two options:
1) You can have f(o) return a new value for o and assign that to o like this:
var o = {'val' : 0};
o = f(o);
// o == null
Inside of f(), you return a new value for o.
function f(v) {
if (whatever) {
return(null);
}
}
2) You put o into another object and pass a reference to that container object into f().
function f(v) {
if (whatever) {
v.o = null;
}
}
var c = {};
c.o = {'val' : 0};
f(c);
// c.o == null;
The javascript language does not have true pointers like in C/C++ that let you pass a pointer to a variable and then reach back through that pointer to change the value of that variable from within the function. Instead, you have to do it one of these other two ways. Objects and arrays are passed by reference so you can reach back into the original object from the function.
JavaScript always passes function arguments "by value". Which means a function can't change what the variable outside the function points to.
However, when you pass an object to a function the "value" that is passed is a reference to the actual object, not a copy of the object. So although you can't set the outside variable to null or to another object you can modify the contents of that object. Which means you can do something like this:
var containerObj = {'o' : {'val' : 0} };
f = function(v) {
v.o = null;
};
f(containerObj.o); // This property would be set to null successfully.
Obviously creating a bunch of container objects just so you can pass them to functions isn't very pretty, but it is one way to do it.
But I'd recommend going with James Montagne's suggestion of having the function return an object or null and assigning the result back to your variable.
Not sure exactly the real use of this, but if you restructured a bit, you could do this:
var o = {'val' : 0};
var f = function(v)
{
if(something){
return null;
}else{
return v;
}
};
o = f(o);
I came here with the same problem. Although the wrapper answer works, it's ugly, and my function is asynchronous, so I can't return a null value.
The reason I wanted o to be null is that later I use if (o) //do something, which might be why OP and others want to set it as null. As such, my solution, although not an answer to the exact question, was
var o = {'val' : 0};
f = function(v) {
v.isNull = true;
}
if (!o.isNull) // do something
The easiest way of doing it would be to create a global variable called NULL and set it to null
var NULL = null
Then you can set an existing variable to NULL without modifying the original object.
var NULL = null;
var original = {};
var ptr = original;
ptr = NULL;
console.log("original is not null = " + (original!==null));
console.log("ptr is null = " + (ptr===null));

Making primitive data types readOnly/nonConfig in JavaScript

Does anyone have any example implementation of making individual object props readOnly/non-configurable? I mean primitive data types. Have tried using ES5 Object API, but hitting a brick wall.
I can't show code, because it's still at that "messy" phase, but basically I'm iterating through an outside object which, itself, holds numeruos objects. Those objects each hold various primitive data types. I have made the outer objects readOnly, non-config, etc, but can't figure out how to do likewise for individual props, the innermost props.
So, if outer.inner.prop === "Hello", I want to make that value readOnly.
Thanks!
UPDATE
I just figured this out, it was all in the for loop I was using to iterate over props. Now I've actually get data descriptors for the props, even the primitive ones. :) Thanks all!
You have to iterate through the inner object, since there is no way to deep-freeze an object using standard ES5 methods.
function deepFreeze(obj) {
Object.keys(obj).forEach(function (key) {
if (typeof obj[key] == 'object')
deepFreeze(obj[key]);
});
Object.freeze(obj);
}
Edit:
Also works for defineProperty if you don't want to freeze:
function deepWriteProtect(obj) {
Object.keys(obj).forEach(function (key) {
if (typeof obj[key] == 'object')
deepWriteProtect(obj[key]);
Object.defineProperty(obj, key, { writable: false });
});
}
I'm not 100% sure I understand your question correctly, but from what I gather you are asking for private variables. If so, that can be easily achieved using closures.
function myClass(){
var mySecretProperty = 10;
this.getMySecretProperty = function(){
return mySecretProperty;
}
this.changeMySecretProperty = function(s){
// whatever logic you need for a setter method
mySecretProperty = s;
}
}
var myObj = new MyClass();
myObj.changeMySecretProperty(120);
myObj.getMySecretProperty(); // will return 120
myObj.mySecretProperty // will return undefined
Would the following (ES5) example help? It creates an empty constructor, with a getter for property a (and no setter, so de facto a is read only):
var Obj = function(){};
Obj.prototype = {
get a() {return 5;}
}
var x = new Obj;
alert(x.a); //=> 5
x.a = 6; //=> TypeError: setting a property that has only a getter
Not using ES5 you can do
var Obj = function(){
var a = 5;
if (!Obj.prototype.getA) {
Obj.prototype.getA = {
toString: function() {
return a;
}
};
}
}
var y = new Obj;
alert(y.getA); //=> 5
But that is not 100% failsafe: Obj.prototype.getA can be overwritten.
Here is a jsfiddle showing how you can use ES5 getter/setter definitions to make a property of an object something that can only be fetched. The code looks like this:
var object = {
get x() {
return 17;
}, set x() {
alert("You cannot set x!");
}
};
Of course the getter could obtain the value of the property ("x") from anywhere, like a closure from a constructor or something. The point is that the setter simply does not change the value, so attempts to change it:
object.x = 100;
will not have any effect.

Invoking a function Object in JavaScript

I have a small question in JavaScript.
Here is a declaration:
function answerToLifeUniverseAndEverything() {
return 42;
}
var myLife = answerToLifeUniverseAndEverything();
If I do console.log(myLife), it will print 42, as I am just invoking the same instance of function resulting in 42 as the answer. (Basic rule on JavaScript that only references of objects are passed and not the object.)
Now, on the other, hand if I do:
var myLife = new answerToLifeUniverseAndEverything();
then I can't invoke the function; instead, myLife becomes just an object? I understand that this is a new copy of the same function object and not a reference, but why can't I invoke the method?
Can you please clarify the basic fundamental I am missing here?
By prefixing the call to answerToLifeUniverseAndEverything() with new you are telling JavaScript to invoke the function as a constructor function, similar (internally) to this:
var newInstance = {};
var obj = answerToLifeUniverseAndEverything.call(newInstance); // returs 42
if (typeof obj === 'object') {
return obj
} else {
return newInstance;
}
JavaScript proceeds to initialize the this variable inside the constructor function to point to a new instance of answerToLifeUniverseAndEverything. Unless you return a different Object yourself, this new instance will get returned, whether you like it or not.
When you do var myLife = answerToLifeUniverseAndEverything();, myLife is simply holding the return value from the function call - in this case, 42. myLife knows nothing about your function in that case, because the function was already called, returned, and then it assigned the resulting value (42) to the new variable myLife.
A completely different thing happens when you do var myLife = new answerToLifeUniverseAndEverything(); - instead, a new object is created, passed to the function as this, and then (assuming the function doesn't return an object itself), stored in the newly created variable. Since your function returns a number, not an object, the newly generated object is stored.
Try:
function answerToLifeUniverseAndEverything() {
return 42;
}
var myLife = answerToLifeUniverseAndEverything;
alert(myLife());
When you do:
var myLife = answerToLifeUniverseAndEverything();
you're assigning the function result to myLife ie 42.
I think i've described the behaviour of new elsewhere. Basically when you do new f() the JS engine creates an object and passes that as this, then uses that object if the return value of f() is not an object.
eg.
o = new f();
is equivalent (approximately) to
temp = {};
temp2 = f.call(temp);
o = typeof temp2 === "object" ? temp2 : temp;
If I do console.log(myLife) It'll print 42, as I am just invoking the same instance of function resulting in 42 as the answer. (Basic rule on javascripts that only references of objects are passed and not the object)
Not quite. This is because you're assigning the return value of answerToLifeUniverseAndEverything() to myLife. If you wanted to make a copy of the function, drop the brackets:
var myLife = answerToLifeUniverseAndEverything;
console.log(myLife());

Categories

Resources