I want to do something like this:
var f = function(){
var args = arguments;
args.push(this._caller);
this._base(args);
};
f._caller = someObject;
f._base = function(x,y,z){ alert(x+':'+y+':'+z);};
f(y,z);
Basically - I want to give function some inner parameters, but still function should be called with its parameters and user should not know about this parameters when he calls this function.
Problem is, that in the function body 'this' will mean 'Window' and not the function in need.
So how can I do this? And can I do this at all?
this won't be the function itself, but the context the function was called with. You can access properties of the function if you name it (var f does not set the name).
var f = function fn() {
var _caller = fn._caller;
var _base = fn._base;
// ...
};
The method arguments.push() doesn't exist as arguments isn't actually an Array. Though, you can create an Array from it with .slice():
var args = Array.prototype.slice.call(argument, 0);
args.push(_caller);
To set the context (this) and pass an Array of arguments, use Function#apply.
return _base.apply(this, args);
Related
I am using history.js. In the stateObj of the pushState function, I want to add a reference to a function (Car.init(), or Boat.init(). In C++, I believe I could use a function pointer.
Then on window.onpopstate, I want to reference that function and call it. I can read the string (Car.init(), but then how can I call the function? I don't want to use eval.
You probably shouldn't, but if you do want to invoke a function based on a global dotted-path name, that could be accomplished like this:
function callFunction(name, var_args) {
// break the string into individual property/method names
var parts = name.split('.');
// start with a reference to the global object
var target = window;
var previousTarget = null;
for (var i = 0; i < parts.length; i++) {
// keep a copy of the previous reference to use as the `this` value
previousTarget = target;
// change the reference to the next named property
target = target[parts[i]];
}
// grab the remaining arguments
var args = Array.prototype.slice.call(arguments, 1);
// call the target function, with previousTarget as the subject, using args
return target.apply(previousTarget, args);
}
// This is in the top-level/global scope. This won't work for a local definition.
var MyApp = {
currentUser: {
name: 'Joe',
displayName: function(greeting) {
alert(greeting + " ," + this.name + "!");
}
},
openBar: function() {
alert("The Foo Bar is now open for business!");
}
};
var functionName = 'MyApp.currentUser.displayName';
callFunction(functionName, "Hello");
This is safer than using eval (good call on avoiding it), but is still pretty wacky and very difficult for JavaScript interpreters to optimize. Instead, the recommended approach is to use a reference (pointer) to the function. This is probably similar to what you'd do in C++. If the function doesn't use this (i.e. if it's a static function, not a method), you can just take a reference to the function directly.
var open = MyApp.openBar;
open();
If it does have a this value, you'll need to use the .bind() method to preserve its association with the object it's attached to.
var display = MyApp.currentUser.displayName.bind(MyApp.currentUser);
display("Greetings");
If you pass additional arguments to .bind(), you can also specify the leading arguments that will be used to call the function.
var displayHello = MyApp.currentUser.displayName.bind(MyApp.currentUser, "Hello");
displayHello();
Since bind is not a cross browser (old ones) function , there is a polyfill for it : ( from John Resig's book)
/*1*/ Function.prototype.bind = function ()
/*2*/ {
/*3*/ var fn = this,
/*4*/ args = Array.prototype.slice.call(arguments),
/*5*/ object = args.shift();
/*6*/ return function ()
/*7*/ {
/*8*/ return fn.apply(object,
/*9*/ args.concat(Array.prototype.slice.call(arguments)));
/*10*/ };
/*11*/ };
But I don't understand why do we need arguments at line #9.
I mean :
If I have this object :
var foo = {
x: 3
}
And I have this function :
var bar = function(p,b){
console.log(this.x+' '+p+' '+b);
}
So , if I want bar to run in the foo context , with parameters - All I need to do is :
var boundFunc = bar.bind(foo,1,2)
boundFunc ()...
So When I run var.bind(foo,1,2) the arguments is [object Object],1,2.
Those arguments are saved at line #4.
Great.
Now , the bind function returns its own closured function :
function ()
{
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
}
Question
Why do we need arguments here ? it seems that they are for something like :
var boundFunc = bar.bind(foo,1,2)
boundFunc (more1,more2....) //<----- ??
Am I missing something ?
Oonce I set the first var boundFunc = bar.bind(foo,1,2) , I already declared the parameters. why do we need them twice ?
There are two places you can pass in arguments to the bound function:
1) When you call bind (the first arguments). These are always applied to the bound function when it is called.
2) When you call the bound function (the second arguments). These are the "more1, more2" that you mention. These change depending on what is provided when the bound argument is called.
Line 9 is combining the original bound arguments with the supplied extra arguments.
I guess the concept you might be confused about is that you don't have to bind ALL arguments initially - you can bind just the context object, or you can bind the first one argument as well but have callers of the bound function supply the rest. For example:
function sum() {
var _sum = 0
for (var i = 0; i < arguments.length ; i++) {
_sum += arguments[i];
}
return _sum;
}
var sum_plus_two = sum.bind({},2);
sum_plus_two(5,7) == 14;
.bind also serves as partial application solution. Event handlers might be the best example:
var handler = function(data, event) { };
element.addEventListener('click', handler.bind(null, someData));
If the arguments from the actual function call wouldn't be passed on, you couldn't access the event object.
Ok, so I thought I understood this (no pun intended), but apparently not.
var Constructor = function () {
var internalFunction = function () {
return this === window;
};
this.myMethod = function () {
alert(internalFunction());
};
};
var myObj = new Constructor();
myObj.myMethod();
This alerts true. Why can't the internal function see this as the object? Instead I have to use alert(internalFunction.call(this)); in myMethod.
Edit: I was looking for an explanation as to why this is assigned in that way, not workarounds such as var self = this;, etc. Sorry if I didn't make that clear.
this is not bound until the function is called and is dependent on how the function is called. You could think of it as an extra parameter implicitly passed to the function.
In this case, the problem is that you're calling internalFunction using internalFunction(). The this value is set either by calling a function as a method (as in foo.bar() or foo["bar"]()) or by setting this explictly via call() or apply(). Your call is doing neither so this reverts to the global object.
The simplest way to achieve what you want in this case while keeping internalFunction private is to store a reference to this inside the constructor function:
var Constructor = function() {
var thisObj = this;
var internalFunction = function () {
return thisObj === window;
};
thisObj.myMethod = function () {
alert(internalFunction());
};
}
Because of functional scoping rules, this is reassigned inside each function... I would store a copy of your object as self and use it accordingly...
var Constructor = function () {
var self = this;
var internalFunction = function () {
return self === window;
};
this.myMethod = function () {
alert(internalFunction());
};
};
var myObj = new Constructor();
myObj.myMethod();
Should give you the output you expect.
SIDENOTE
This is a fairly precarious practice that javascript has created, mainly because if you forget the new keyword when using Constructor, you will get this referring to the window (god) object so you'll be attaching myMethod to the window without warning.
There are five ways to call a function in JavaScript. The value of this depends on which you choose:
Global function call (e.g. myFunction()). No explicit value for this is given. The value of this will be the default object (window in a browser).
Method call (e.g. obj.myFunction()). The value of this is the object on which the method was invoked (obj in this case).
Using the call method (e.g. myFunction.call(obj)). The value of this is provided explicitly (in this case obj).
Using the apply method (e.g. myFunction.apply(obj)). The value of this is provided explicitly (in this case obj).
Constructor function (e.g. new MyFunction()). The value of this is a newly-created object provided by the runtime.
Each of the five is explained in more detail here:
http://devlicio.us/blogs/sergio_pereira/archive/2009/02/09/javascript-5-ways-to-call-a-function.aspx
Its a scope issue try something like:
var Constructor = function () {
var $this = this;
var internalFunction = function () {
return $this === window;
};
this.myMethod = function () {
alert(internalFunction());
};
};
var myObj = new Constructor();
myObj.myMethod();
I'm playing around with Function.prototype as a learning exercise and was trying to make a generic curry method that could be used like this:
// Any old function
var myFn = function(arg1, arg2) {
console.log(arg1, arg2);
};
// Curry some parameters and return a new function
var myFnCurry = myFn.curry("A");
// Call the curried function, passing in the remaining parameters
myFnCurry("B"); // Outputs: A B
It's fairly straight forward to implement this feature as a function rather than a method using the following approach:
var curry = function(fn) {
var slice = Array.prototype.slice,
args = slice.call(arguments, 1);
return function() {
fn.apply(this, args.concat(slice.call(arguments)));
};
};
// Example
var myFnCurry = curry(myFn, "A");
myFnCurry("B"); // Outputs: A B
However, I really wanted to be able to utilise the Function prototype, like this:
Function.prototype.curry = function() {
var slice = Array.prototype.slice,
args = slice.call(arguments);
return function() {
// If we call curry like this: myFn.curry()
// how can we get a reference myFn here?
???.apply(this, args.concat(slice.call(arguments)));
};
};
I'm not sure how to get a reference to the myFn function (denoted by the ??? above) that the curry method is being called from.
Is there a way to access the parent function in this circumstance?
Cheers,
Ian
You're calling curry in a context of your function object (myFn.curry), and therefore inside of curry this refers to that function. But your inner function will be called in the context of global object, that's why you need to store the reference to the outer function in a closure.
Function.prototype.curry = function() {
var self = this, args = [].slice.call(arguments)
return function() {
return self.apply(this, args.concat([].slice.call(arguments)));
}
}
Code:
Sorter.prototype.init_bubblesort = function(){
console.log(this.rect_array);
this.end = this.rect_array.length;
this.bubblesort();
}
Sorter.prototype.init = function(array,sort_type){
this.rect_array = array;
this.init_bubblesort();
}
The code above works as expected, but when I change the init function to:
Sorter.prototype.init = function(array,sort_type){
var sort_types = {'bubblesort':this.init_bubblesort,
'quicksort':this.init_quicksort,
'liamsort':this.init_liamsort};
this.rect_array = array;
sort_types[sort_type]();
}
the init_bubblesort function results in an error saying this.rect_array is undefined. I'm trying to wrap my head around why init_bubblesort no longer has access to its instance's variables.
You need to use call to specify the implicit parameter (this).
Sorter.prototype.init = function(array,sort_type){
var sort_types = {'bubblesort':this.init_bubblesort,
'quicksort':this.init_quicksort,
'liamsort':this.init_liamsort};
this.rect_array = array;
sort_types[sort_type].call(this);
}
Otherwise, there is no object associated with the init_bubblesort method.
For the setTimeout, do:;
var self = this;
setTimeout(function(){sort_types[sort_type].call(self);}, 1000);
In the callback, this will refer to the window, so we create a separate variable (self), which will be closed in.
The reason why you are getting this error is that this referred to inside the functions are resolved into the object which the method is a property of.
In the first case this is the Sorter object, in the second it is the sort_types object, and of course, these are not the same.
The easiest way to do this would therefor be the following.
Sorter.prototype.init = function (array, sort_type) {
var sort_types = {
'bubblesort': "init_bubblesort",
'quicksort': "init_quicksort",
'liamsort': "init_liamsort"
};
this.rect_array = array;
this[sort_types[sort_type]]();
}
As an alternative, call could be used to 'change' the context of the executed method.
And to answer the comment on the other question, if you need to defer the execution of the sort, the you can do this
Sorter.prototype.init = function (array, sort_type) {
var sort_types = {
'bubblesort': "init_bubblesort",
'quicksort': "init_quicksort",
'liamsort': "init_liamsort"
};
this.rect_array = array;
var that = this;
window.setTimeout(function(){
that[sort_types[sort_type]]();
}, 80);
}
In this case the context is kept the same.
Matthew got your problem, but looking at your code, the sort_types object seems a bit redundant to me, if you are only using it to map a function "foo" with the prefix "init_foo", you could do something simple this:
Sorter.prototype.init = function(array,sort_type){
this.rect_array = array;
this["init_"+sort_type](); // the `this` value will work without problems
}