Given some javascript like this:
var doSomething = function() { return 1 };
doSomething.someProperty = "a";
How can I redefine the function so that, e.g., doSomething() returns 2, but without losing any additional properties defined on it?
I've found plenty of answers about this sort of thing for OOP, but I'm not using "new" here, just calling the function directly.
You can assign the properties from the original function to a new function:
Original Answer:
function redefineFunction(oldFn, newFn) {
Object.assign(newFn, oldFn);
return newFn;
}
// Original function and properties
var doSomething = function() { return 1 };
doSomething.someProperty = "a";
// Redefine
doSomething = redefineFunction(doSomething, function() { return 2 });
console.log(doSomething);
console.log(doSomething());
console.log(doSomething.someProperty);
Simplified using Object.assign without needing a function:
var doSomething = function() {
return 1
};
doSomething.someProperty = "a";
// Redefine
doSomething = Object.assign(function() {return 2}, doSomething);
console.log(doSomething);
console.log(doSomething());
console.log(doSomething.someProperty);
You could do something like this:
var doSomething = function() {
return doSomething.returnVal;
}
doSomething.returnVal = 1; // Without this, doSomething returns undefined
console.log(doSomething()); // <= 1
doSomething.returnVal = 2;
console.log(doSomething()); // <= 2
If you want to make some more dramatic change to the function (i.e. altering the algorithm that it runs rather than just a data value within it), then probably the best approach would be to create an entirely new function and copy the properties of your old function to it:
function copyProperties(source, destination) {
for (var property in source) {
destination[property] = source[property];
}
}
var doSomething = function() {
return 1;
}
doSomething.someProperty = "a";
var doSomethingElse = function() {
return 2;
}
copyProperties(doSomething, doSomethingElse);
console.log(doSomethingElse()); // <= 2
console.log(doSomethingElse.someProperty); // <= a
Related
Is there a way to access properties in nested functions like that :
function func(){
this.new_func=function(){
console.log("something");
return 'something';
}
this.someValue=7;
return function(){
return "something_diff";
};
}
var obj=new func();
obj(); //works with returning "something diff"
obj.new_func(); // TypeError: obj.new_func is not a function
obj.someValue; // undefined
I need to delete whole "return function()..." part in order to access "someValue" and "new_func()". Why is it acting like that, and is there a way to somehow access that properties, while still returning another function ??
When you have a constructor that returns an object, that object replaces whatever you assigned to this. So indeed, the members new_func and someValue are lost.
To combine the returned function together with the other members, you can do this:
function func() {
var f = function() {
return "something_diff";
};
f.new_func = function() {
console.log("something");
return 'something';
}
f.someValue = 7;
return f;
}
var obj = new func();
console.log(obj());
obj.new_func();
console.log('someValue:', obj.someValue);
You can do it like this:
var parentFunction = function() {
var nestedFunction = function() {
var value = "nestedValue";
var moreValues = "more values";
return {
value: value,
moreValues: moreValues
}
}
var anotherNestedFunction = function() {
var anotherValue = "nestedValue";
return anotherValue;
}
return {
nested: nestedFunction,
another: anotherNestedFunction
}
}
Then:
var newFunction = new parentFunction();
var nested = newFunction.nested();
console.log("nested value: ", nested.value);
console.log("another nested value: ", newFunction.another);
Here is a working example:
Why is it acting like that, and is there a way to somehow access that properties, while still returning another function ??
Because of the pharenteis:
var obj=new func();
Basically you're firing your function and what is stored the variable obj is what the "func" returns.
In order to access to private properties, you should look at the Closures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
I have a list of objects I am looping over; each of these objects has a property on it which is a function referring to this. If I create a list of callbacks based on my objects, it seems that I have to "double-wrap" the this-dependent function in order to maintain the correct references. I don't understand why this is - can anyone explain?
function Pokemon(name) {
this.name = name;
this.sayName = function() {
console.log(this.name);
};
}
function runFunctions(arrayOfFunctions) {
for (var i = 0, fn; fn = arrayOfFunctions[i]; i++) {
fn();
}
}
var monsters = [
new Pokemon('Charmander'),
new Pokemon('Squirtle'),
new Pokemon('Bulbasaur')
];
var unclosedCalls = [];
var closedCalls = [];
for (var i = 0, monster; monster = monsters[i]; i++) {
var unclosedCall = (function(m) {
return m.sayName
})(monster);
var closedCall = (function(m) {
return function() {
m.sayName();
}
})(monster);
unclosedCalls.push(unclosedCall);
closedCalls.push(closedCall);
}
console.log('--start')
runFunctions(unclosedCalls); // doesn't work
console.log('----')
runFunctions(closedCalls); // works
console.log('--end')
closedCalls is the list of double-wrapped callbacks.
I don't get why m in each creation of unclosedCall is not actually closed over.
Here is a jsbin with my code: http://jsbin.com/qivilusuje/1/edit?js,console,output
The problem with the "unclosed" calls is that the function reference that you return (m.sayName) is immediately disassociated from the variable m from which the function property was retrieved.
A function reference doesn't know anything about which object it was retrieved from so therefore when the function is eventually invoked it has no "context" - this will be set to the global object instead of the object that originally had this function as a property:
var obj = {
func : function() { console.log(this) }
}
obj.func() // outputs "obj" because a.b() calls "b" with "this" === "a"
var ref = obj.func;
ref(); // outputs "window"
To fix it you can have the unclosed call do return m.sayName.bind(m), although having got that far there's no need for the IIFE either, and it would work just as well to say:
var unclosedCall = monster.sayName.bind(monster);
I think I understand why variables exist outside of the function they were declared in, because you're returning another function:
myFunction = function() {
var closure = 'closure scope'
return function() {
return closure;
}
}
A = myFunction(); // myFunction returns a function, not a value
B = A(); // A is a function, which when run, returns:
console.log(B); // 'closure scope'
The way that it's written now, calling A() is like a getter.
Q: How can I write myFunction so that calling A(123) is a setter?
Try the following:
myFunction = function() {
var closure = 'closure scope'
// value is optional
return function(value) {
// if it will be omitted
if(arguments.length == 0) {
// the method is a getter
return closure;
} else {
// otherwise a setter
closure = value;
// with fluid interface ;)
return this;
}
}
}
A = myFunction(); // myFunction returns a function, not a value
A(123); // set value
B = A(); // A is a function, which when run, returns:
console.log(B); // '123'
You could do something like this if you want both getter and setter for example:
var func = function() {
var closure = 'foo';
return {
get: function() { return closure; },
set: function(value) { closure = value; }
}
};
var A = func();
A.set('foobar');
console.log(A.get()); //=> "foobar"
Should be as simple as:
myFunction = function() {
var closure = 'closure scope'
return function(setTo) {
if (typeof setTo !== "undefined") {
closure = setTo;
return this; //support call chaining, good idea hek2mgl
} else {
return closure;
}
}
}
Since the closure variable is within the closure of the function's scope, you should be able to assign to it the same way you can read from it.
See jsFiddle: http://jsfiddle.net/WF4VT/1/
Another alternative would be to use a class and define getters and setters:
function MyClass(p){
this._prop = p;
}
MyClass.prototype = {
constructor: MyClass,
get prop(){
return this._prop;
},
set prop(p){
this._prop = p;
}
}
var myObject = new MyClass("TEST");
console.log(myObject.prop);
myObject.prop = "test";
console.log(myObject.prop);
Demo: http://jsfiddle.net/louisbros/bMkbE/
jsFiddle Demo
Have your returned function accept an argument. Use it as a setter:
myFunction = function() {
var closure = 'closure scope';
return function(val) {
closure = val;
return closure;
}
}
A = myFunction(); // myFunction returns a function, not a value
B = A(123); // A is a function, which when run, returns:
console.log(B); // 'closure scope'
Revisiting this question, I see that I could do it this way:
function outside() {
var result = 'initialized'
return inside
function inside(argVariable) {
if(arguments.length) {
result = argVariable
return this
} else {
return result
}
}
}
myFunction = outside() // outside returns a function
X = myFunction() // returns: 'initialized'
$('body').append(X + '<br>')
myFunction(123) // setter
X = myFunction() // returns: 123
$('body').append(X)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I want to build a javascript function that maintains state. Here's a pattern that I've come up with, but something in the back of my mind tells me this is an anti-pattern.
function f() {
var state = 1;
f = function() {
return state++;
};
return f();
};
Is there anything wrong with this? If so, what's a better approach?
Well it's a matter of opinion what the best way is, but (although I know it works) I'm a little uncomfortable with having the function overwrite itself. A similar pattern that doesn't do that but still uses practically the same closure idea is this:
var f = function() {
var state = 1;
return function() {
return state++;
};
}();
Or here is another way:
function f() {
return f.state++;
}
f.state = 1;
Of course with the f.state method the advantage and disadvantage (depending on your needs) is that the .state property can be read and modified by other code.
Normally, you set a closure scope and return a function that has access to that scope. Every time that function is now called, the state will remain as long as that function exists. Example:
var statefulFunction = function() {
// set up closure scope
var state = 1;
// return function with access to the closure scope
return function() {
return state++;
};
}(); // immediately execute to return function with access to closure scope
var first = statefulFunction(); // first === 1
var second = statefulFunction(); // second === 2
Another pattern is to create a closure scope and return an object with methods that have access to that closure scope. Example:
var myStatefulObj = function() {
// set up closure scope
var state = 1;
// return object with methods to manipulate closure scope
return {
incr: function() {
state++;
},
decr: function() {
state--;
},
get: function() {
return state;
}
};
}();
myStatefulObj.incr();
var currState = myStatefulObj.get(); // currState === 2
myStatefulObj.decr();
currState = myStatefulObj.get(); // currState === 1
A better way to achieve this might be to use an Immediately-Invoked Function Expression (IIFE) to encapsulate your state.
var f = (function () {
var state = 1;
return function() {
return state++;
};
}());
console.log(f()); // 1
console.log(f()); // 2
When we are creating a method inside a closure it becomes private to that closure and can't be accessed until we expose it in some way.
How can it be exposed?
You can return a reference to it...
var a = function() {
var b = function() {
// I'm private!
alert('go away!');
};
return {
b: b // Not anymore!
};
};
See it on jsFiddle.
You could also bind it to the window object. But I prefer the method above, otherwise you are exposing it via a global variable (being a property of the window object).
You need to pass it to the outside in some manner.
Example: http://jsfiddle.net/patrick_dw/T9vnn/1/
function someFunc() {
var privateFunc = function() {
alert('expose me!');
}
// Method 1: Directly assign it to an outer scope
window.exposed = privateFunc;
// Method 2: pass it out as a function argument
someOuterFunction( privateFunc );
// Method 3: return it
return privateFunc;
}
someFunc()(); // alerts "expose me!"
function someOuterFunction( fn ) {
fn(); // alerts "expose me!"
}
window.exposed(); // alerts "expose me!"
You expose functions or properties of a closure by internally declaring them in this scope (which can change depending on invocation).
function example(val) {
var value = val;
this.getVal = function() {
return value;
}
this.setVal = function(v) {
value = v;
}
}
var ex = new example(2);
ex.getVal(); // == 2
ex.setVal(4); // == null
ex.getVal(); // == 4
Methods declared in this can access variables declared using var, but not the other way 'round.
function example(val) {
var value = val;
var double = function(v) {
return 2 * v;
}
this.getDouble = function() {
return double(value);
}
}
var ex = new example(2);
ex.getDouble(); // == 4
The function closes over the scope. What you want to do is to return a reference to a function that has access to the scope you require so you can invoke it at a later point.
If you need to create a function that calls a specific method at some later point,
var ex = new example(2);
var delayed_call = function() {
return(ex.getDouble()); // == 4, when called
}
setTimeout(delayed_call, 1000);
If scoping is an issue,
var ex = new example(2);
var delayed_call = (function(ex_ref) {
return function() {
return(ex_ref.getDouble()); // == 4, when called
}
})(ex); // create a new scope and capture a reference to ex as ex_ref
setTimeout(delayed_call, 1000);
You can inline most of this with the less readable example of,
setTimeout((function(ex_ref) {
return function() {
return(ex_ref.getDouble()); // == 4, when called
})(new example(2)))
, 1000
);
setTimeout is just a convenient way of demonstrating execution in new scope.
var ex = new example(2);
var delayed_call = function() {
return(ex.getDouble());
}
delayed_call(); // == 4
For performance purposes you can invoke it this way:
var a = (function(){
function _a(){}
_a.prototype = (function(){
var _test = function(){ console.log("test"); };
return {
test: _test
}
}());
return new _a();
}());
// usage
var x = a;
x.test(); // "test"