How to change a object by function - javascript

In one task I have to change a object by deliver it to a function. it is almost like this:
function change(obj) {
return {
fn1: function() {
obj['three'] = 3;
},
fn2: function() {
obj = {'four':4};
}
};
}
var obj = {'one':1,'two':2};
console.log(obj); // output 'Object {one=1, two=2}'
var temp = change(obj);
temp.fn1();
console.log(obj); // output 'Object { one=1, two=2, three=3}'
temp.fn2();
console.log(obj); // output 'Object { one=1, two=2, three=3}'
fn1 is for changing one of the object's value. As you can see, it intends to set obj[three] to 3 and it works well.
fn2 is for replacing the object of another one. The obj should only contain 'four':4 after the execution。 however it didn't work.
Then I check the value of the obj inside fn2:
...
fn2: function() {
console.log('before fn2',obj);
obj = {'four':4};
console.log('after fn2', obj);
}
};
...
The output is:
'before fn2 Object{one=1,two=2,three=3}'
'after fn2 Object{four=4}'
So it did work inside the fn2. I get confused that fn2 fails while fn1 works. How to fix it?

In JavaScript, arguments are passed by value, though that value often has pointer-like semantics. When you run obj['three'] = 3;, you are not rebinding the variable obj; you are merely changing a property of the object that obj currently holds. On the other hand, when you reassign obj, you are changing the variable obj, not the object that the variable used to hold. You cannot achieve your desired semantics in JavaScript; the best you can do would be to delete all properties of obj and copy over whatever properties you want to be on it.

Related

Javascript object passed in a function but apparently passed by value

As javascript objects are passesd by reference then why console.log(obj1.a) is showing 1 instead of 7. As function is changing value to 7.
obj1 = {
a: 1
}
obj2 = {
a: 1
}
function c(d) {
console.log('d bef ', typeof d)
if (typeof d === 'object'){
d = obj2
obj2.a = 7
console.log ('d.a', d.a)
}
}
c(obj1)
console.log(obj1.a)
You seem confused by two different concepts.
In JS, objects ARE passed by references, but variable are not.
What does it means is that :
//WHen you declare your VARIABLE, you assign it a REFERENCE to a new object :
const something = { property : 'foo' };
//Something now is a variable whose value is a REFERENCE to an object, not the object itself.
function success(obj){
obj.property = 'bar';
}
function fail(obj){
obj = { property : 'bar' }:
}
//When you call fail :
fail(something);
console.log(something.property); //will print foo
//When you call success :
success(something);
console.log(something.property); //will print bar
In success function above, you access the object by reference through the function argument and you change its property. Because when you call success, the variable something pass its reference to the object by value. So, the argument of success (obj) has for value the reference to the object.
in fail, you replace the reference to the object by a reference to another object.
To be clear : the something variable is passed by value. But this value is a reference to an object.
Thus, you can change object properties through this reference, but you can't change the something variable value by assigning a new value to the function's parameter obj.
By passing the data as a parameter, you pass the object value but not the reference, So, you have a new object with a new reference, just scoped for the function.
This is a simpler example.
// The pet variable have the referance of the { type: 'cat' } object.
let pet = { type: 'cat' }
// p is a copy of the pet variable but not have the same referance.
const changeThePet = (p) => {
p = { type: 'dog' }
}
// here you pass the pet variable
changeThePet(pet)
console.log(pet)
p will hold the value of the pet but they aren't the same variable or same referance

Can't understand the behaviour of functions with "this" keyword

I'm trying to figure out the behaviour of "this" keyword in JavaScript. In general I understand how it behaves in different contexts of function calling, but I'm having some trouble when it's part of an arrow function.
I'm using MDN as a source of information: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#Arrow_functions.
I have grasped the general concept that in the case of arrow functions "this" retains the value corresponding to the context of the function definition, but I started having trouble when playing with the last example in that section.
The example goes like this:
var obj = {
bar: function() {
var x = (() => this);
return x;
}
};
var fn = obj.bar();
console.log(fn() === obj); //true
As expected, the console returns true. However, and this is my first doubt, i don't understand why it returns false when I compare obj directly with obj.bar(), since I asume that obj.bar() and fn() invoke the same function:
var obj = {
bar: function() {
var x = (() => this);
return x;
}
};
console.log(obj.bar() === obj); //false
I started playing with the code trying to find further unexpected behaviours, and there are some more doubts. The second one: when I change the type of definition of bar, "this" in the function apparently starts referring to the global object, although I thought the function was being called as a method of obj. ¿Shouldn't the result be the opposite? ¿Is it because now "this" refers to the context of fn?
var obj = {
bar: function() {
var x = function() {
return this;
};
return x;
}
};
var fn = obj.bar();
console.log(fn() === obj); //false
console.log(fn() === this); //true
Third doubt: if I omit fn again and use directly obj.bar() in the comparison, again I find unexpected results. In this case "this" in the function refers neither to the global object nor to obj (I expected the second, since bar() is called as a method of obj).
var obj = {
bar: function() {
var x = function() {
return this;
};
return x;
}
};
console.log(obj.bar() === obj); //false
console.log(obj.bar() === this); //false
When I try to see directly the value returned by obj.bar() I can't get much help from the console:
console.log(obj.bar());
//With arrow definition: () => { length:0
// name:x }
//With the second definition: f() => { length:0
// name:"bar"
// prototype: bar}
I hope someone here can help me understand what's going on, or at least recommend me a more complete resource. Thank you in advance!
I asume that obj.bar() and fn() invoke the same function
No, obj.bar() returns the function x. It's creating a closure.
"this" in the function apparently starts referring to the global object, although I thought the function was being called as a method of obj
No, fn was not called as a method on obj, only bar was. fn() is called as a plain function.
if I omit fn again and use directly obj.bar() in the comparison, "this" in the function refers neither to the global object nor to obj
No. The this in the function is never even evaluated, the function is never called. You only called bar, and then compared the returned function against obj.
When I try to see directly the value returned by obj.bar()
…then the console shows you a function object!
What you are looking at is obj.bar() reutrns a function that is assigned to a variable fn and then fn is invoked fn()
so
var obj = {
bar: function() {
var x = (() => this);
return x;
}
};
console.log(obj.bar()() === obj); //should give you true

What object javascript function is bound to (what is its "this")?

I know that inside the function it is this.
var func = function {
return this.f === arguments.callee;
// => true, if bound to some object
// => false, if is bound to null, because this.f === undefined
}
var f = func; // not bound to anything;
var obj = {};
obj1.f = func; // bound to obj1 if called as obj1.f(), but not bound if called as func()
var bound = f.bind(obj2) // bound to obj2 if called as obj2.f() or as bound()
Edited:
You can't actually call obj2.f() as f doesn't become a property of obj2
edit end.
The question is: how to find the object, that the function is bound to, outside of this function?
I want to achieve this:
function g(f) {
if (typeof(f) !== 'function') throw 'error: f should be function';
if (f.boundto() === obj)
// this code will run if g(obj1.f) was called
doSomething(f);
// ....
if (f.boundto() === obj2)
// this code will run if g(obj2.f) or g(bound) was called
doSomethingElse(f);
}
and partial application without changing the object that the function is bound to:
function partial(f) {
return f.bind(f.boundto(), arguments.slice(1));
}
Consensus:
You can't do it. Takeaway: use bind and this with great care :)
Partial Application
You can do partial application:
// This lets us call the slice method as a function
// on an array-like object.
var slice = Function.prototype.call.bind(Array.prototype.slice);
function partial(f/*, ...args */) {
if (typeof f != 'function')
throw new TypeError('Function expected');
var args = slice(arguments, 1);
return function(/* ...moreArgs */) {
return f.apply(this, args.concat(slice(arguments)));
};
}
What Object is this Function Bound To?
Additionally, there's a pretty straight-forward solution to the first part of your question. Not sure if this is an option for you, but you can pretty easily monkey-patch things in JS. Monkey-patching bind is totally possible.
var _bind = Function.prototype.apply.bind(Function.prototype.bind);
Object.defineProperty(Function.prototype, 'bind', {
value: function(obj) {
var boundFunction = _bind(this, arguments);
boundFunction.boundObject = obj;
return boundFunction;
}
});
Just run that before any other scripts get run, and any script which uses bind, it will automatically add a boundObject property to the function:
function f() { }
var o = { };
var g = f.bind(o);
g.boundObject === o; // true
(Note: I'm assuming you're in an ES5 environment above due to the fact that you're using bind.)
A function in javascript is not technically bound to anything. It may be declared as a property of an object as in:
var obj = {
fn: function() {}
}
obj.fn();
But, if you get the function into it's own variable by itself, it is not bound to any particular object.
For example, if you did this:
var obj = {
fn: function() {}
}
var func = obj.fn;
func();
Then, when you call func() which in turn executes the fn() function, it will have no association with obj at all when it is called. Associating an object with a function is done by the caller of the function. It is not a property of the function.
If one were to use fn.bind(obj), that creates a new function that just internally executes a call to obj.fn(). It doesn't magically add any new capabilities to javascript. In fact, you can see a polyfill for .bind() to see how it works here on MDN.
If you are expecting this to always be a particular object no matter how a function is called, that is not how javascript works. Unless a function is actually a shim that forces an association with a hard-wird object when it's called (what .bind() does), then a function doesn't have a hard-wired association. The association is done by the caller based on how it calls the function. This is different than some other languages. For example, in C++, you can't call a function without having the right object to associate with it at call time. The language simply won't resolve the function call and you get a compile error.
If you are branching on types in javascript, then you are probably not using the object-oriented capabilities of the language properly or to your best advantage.
Instead of binding the function func to the objects, why not try treating func as an object, that can hold obj1 and obj2 as its properties?
For example:
var func = function {
this.object; // Could be obj1 or obj2
return this.f === arguments.callee;
// => true, if this.object is not null
}
var f = func;
f.object = obj1; // or func.object = obj2;
You can also write a function that handles whether or not the object is obj1 or obj2:
function g(f) {
if (typeof(f) !== 'function') throw 'error: f should be function';
if (f.object === obj)
// this code will run if g(f) was called
doSomething(f);
if (f.object === obj2)
// this code will run if g(f) or g(bound) was called
doSomethingElse(f);
}
The reason is that you want to treat obj1 and obj2 as a property of the function f. However, when you bind, you are adding the function as a property of either obj1 or obj2. It's possible to bind the function to multiple objects, so it doesn't make sense to look for THE one object to which you bound the function; because you're adding the function as a subset of the object.
In this case, since you want to treat the object as a subset of the function, it might make sense to add a property into the function that can hold obj1 or obj2.
If you are the one doing the bind, you can add a field to the function to record the this for later testing.
var that = "hello";
var xyz = function () { console.log(this); }.bind(that);
xyz.that = that;
// xyz is callable as in xyz(), plus you can test xyz.that without calling

How come JavaScript {this} doesn't reference the parent object?

When making objects I assumed that this would return the object's instance - instead it seems it's something else. Why is this?
var Obj = {
foo : 'value',
bar : function() { return this.foo; } // Error
bar : function() { return Obj.foo; } // Works
}
Update: I must be missing something because in some cases using this inside objects doesn't work. How come this only references the object instance sometimes?
It does. You have a syntax issue.
Use commas to delimit object members
var Obj = {
foo : 'value',
bar : function() { return this.foo; },
}
within member functions this refers to a reference to the object that the function is called on.
jsFiddle Example
Within a JavaScript function this is set depending on how the function was called.
With your example Obj, assuming you correct the syntax error and use commas between properties:
var Obj = {
foo : 'value', // needs comma, not semicolon
bar : function() { return this.foo; }
}
If you use the "dot" syntax on Obj to call the bar function then this will be automatically set to Obj:
Obj.bar(); // this will be Obj
That's an easy way to ensure this ends up set the way you want. But if you call the function in some other way this could be set to something else:
var nonObjBar = Obj.bar; // get a reference to the function
nonObjBar(); // this will (probably) be `window`, but depends if
// in strict mode or inside some other function, etc
var Obj2 = { foo: "other foo" };
Obj.bar.call(Obj2); // the .call() method sets this to Obj2
// so bar will return Obj2's foo, "other foo"
That last example uses the .call() method on Obj.bar to invoke the function in a way that allows you to set this to anything you like (strict versus non-strict mode affects the way this works for some cases).
It might seem strange if you're coming from languages like Java, but JavaScript functions don't belong to any given object. This behaviour is pretty well defined and on purpose.
After fixing the semi-colon after 'value', this seems to work:
var Obj = {
foo : 'value',
bar : function() { return this.foo; } // Error
};
alert(Obj.bar()); // alerts 'value'
See working jsFiddle here: http://jsfiddle.net/jfriend00/UFPFf/.
When you call a method on an object, the javascript engine sets the this pointer to point to the object for the duration of the method call.
You shouldn't have a semicolon after foo.
Actually when you call Obj.bar you will have this referring to Obj.
See http://jsfiddle.net/AAkbR/
var Obj = {
foo : 'value',
bar : function () { return this.foo; }
};
alert(Obj.bar() === Obj.foo);
Alerts true.

How to procedurally set Javascript getter/setters? (or actually closure scopes)

I'm trying to procedurally add getters/setters to objects in Javascript and although I think the code below should simply work, it doesn't act as I expected.
here is my code:
var data = {a:1, b:2, c:3};
function Abc(data) {
var data = data || {};
for ( var key in data ) {
console.log(key, data[key]);
this.__defineGetter__(key, function() {
console.log('using getter');
return data[key];
})
}
return this;
}
abc = Abc(data);
console.log('this should be 1', abc.a);
console.log('this should be 2', abc.b);
console.log('this should be 3', abc.c);
and this is my unexpected output
a 1
b 2
c 3
using getter
this should be 1 3
using getter
this should be 2 3
using getter
this should be 3 3
the output makes absolutely no sense to me, but I get the same output on Chrome and Webkit, so I'm guessing I'm just stupid and this is not a bug of the Javascript engines :)
as the comments mention my triple use of "data" isn't really good!
By the time the closure that was passed to __defineGetter__ is executed, the loop has finished and key remains at the last value. Try this:
function Abc(data) {
if(!(this instanceof Abc)) {
return new Abc(data);
}
data = data || {};
for(var key in data) {
(function(key) {
console.log(key, data[key]);
this.__defineGetter__(key, function() {
console.log('using getter');
return data[key];
});
}).call(this, key);
}
return this;
}
Some other things:
You weren't using var on key, so key was global. That wasn't causing your issue, but it's a good idea to add it anyway.
You were declaring a new variable named data when there was already a data in the scope of the function. It's not needed to use var there since there's already a data in the function; I removed it.
You weren't using new, which was also causing odd behavior; the new three lines at the top of the function cause it to act as if it was called with new.
#Robert Gould. main problem in your code sample is not in "defining getters and setters", but in "understanding closures".
See more about closures in MDN
Additionaly your code is invalid on using this keyword, because your Abc() function this object points to global window object. You must use new Abc() to properly use this keyword or must create new empty object inside Abc() and return it.
1)
function Abc(data) {
data=data||{};
for(key in data) {
console.log(key, data[key]);
(function(data,key) { // defining closure for our getter function
this.__defineGetter__(key, function() {
console.log('using getter');
return data[key];
});
}).call(this,data,key);
}
// dont need to return: when used *new* operator *this* is returned by default
}
var abc = new Abc(data);
or
2)
function Abc(data) {
data=data||{};
var obj={};
for(key in data) {
console.log(key, data[key]);
(function(data,key) { // defining closure for our getter function
obj.__defineGetter__(key, function() {
console.log('using getter');
return data[key];
});
})(data,key);
}
return obj; // return object
}
var abc = Abc(data);
If you realy need to known about "defining getters|setterns" read about:
Object.defineProperty() MDN and get operator MDN
and for back compatibility __defineGetter__ MDN
you must also understand what not all top used browsers currently supports getters and setters for properties
key is a local variable in the scope of Abc, the anonymous function you wrote has no variable key, so it uses the one from the outer scope. That variable has changed to the last value in the loop by the time the anonymous function is used, so you see the last value. You can fix this with a closure:
this.__defineGetter__(key, (function(l_key){
return function() {
console.log('using getter');
return data[l_key];
}
})(key));
In that code l_key is a local copy of key to the anonymous function returned be another anonymous function.

Categories