Related
Background
I want a function keeping track of its own state:
var myObject = {
myFunction: function () {
var myself = this.myFunction;
var firstTime = Boolean(!myself.lastRetry);
if (firstTime) {
myself.lastRetry = Date.now();
return true;
}
// some more code
}
}
The problem with the above code is that the value of this will depend on the site of the function call. I want the function to be able to refer to itself without using:
myObject.myFunction
.bind()
.apply()
.call()
Question
Is it possible to give a function this kind of self awareness independent of its call site and without any help from external references to it?
If you want to store that state on the function instance, give the function a name, and use that name within it:
var myObject = {
myFunction: function theFunctionName() {
// ^^^^^^^^^^^^^^^--------------------- name
var firstTime = Boolean(!theFunctionName.lastRetry);
// ^--------------------------- using it
if (firstTime) {
theFunctionName.lastRetry = Date.now();
// ^------------------------------------------------ using it
return true;
}
// some more code
}
};
You'd do that whenever you want to use a function recursively as well. When you give a name to a function that way (putting the name after function and before (), that name is in-scope within the function's own code. (It's not in-scope for the code containing the function if it's a function expression, but it is if it's a function declaration. Yours is an expression.)
That's a named function expression (where previously you had an anonymous function expression). You may hear warnings about NFEs, but the issues various JavaScript implementations had with them are essentially in the past. (IE8 still handles them incorrectly, though: More in this post on my blog.)
You might consider keeping that state somewhere private, though, via an IIFE:
var myObject = (function(){
var lastRetry = null;
return {
myFunction: function() {
var firstTime = Boolean(!lastRetry);
if (firstTime) {
lastRetry = Date.now();
return true;
}
// some more code
}
};
})();
Now, nothing outside that outer anonymous function can see lastRetry at all. (And you don't have to worry about IE8, if you're supporting stubborn XP users. :-) )
Side note: The unary ! operator always returns a boolean, so your
var firstTime = Boolean(!theFunctionName.lastRetry);
...is exactly equivalent to:
var firstTime = !theFunctionName.lastRetry;
...but with an extra unnecessary function call. (Not that it hurts anything.)
Of course you can, simply give your function an internal named representation and it can refer to itself from there. For example...
var obj = {
doThings:function doThingsInternal(arg1, arg2) {
console.log(arg1, arg2);
for (var arg in doThingsInternal.arguments) {
console.log(arg);
}
}
};
obj.doThings('John', 'Doe');
You could use a simple Closure, if you are not too bent on keeping state existence knowledge within the function. But I guess you don't want that. Another way to do this could be changing the function itself on the first call. Benefits, no/less state variables needed and no costly checks on subsequent calls! -
var myObject = {
myFunction: function () {
// Whatever you wanna do on the first call...
// ...
// And then...
this.myFunction = function(){
// Change the definition to whatever it should do
// in the subsequent calls.
}
// return the first call value.
}
};
You can extend this model to any states by changing the function definition per your state.
Here's a sample of a simple Javascript class with a public and private method (fiddle: http://jsfiddle.net/gY4mh/).
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction();
}
}
ex = new Example;
ex.publicFunction();
Calling the private function from the public one results in "this" being the window object. How should I ensure my private methods are called with the class context and not window? Would this be undesirable?
Using closure. Basically any variable declared in function, remains available to functions inside that function :
var Example = (function() {
function Example() {
var self = this; // variable in function Example
function privateFunction() {
// The variable self is available to this function even after Example returns.
console.log(self);
}
self.publicFunction = function() {
privateFunction();
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Another approach is to use "apply" to explicitly set what the methods "this" should be bound to.
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.apply(foo, null);
// => foo
Yet another approach is to use "call":
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.call(foo, null);
// => foo
both "apply" and "call" take the object that you want to bind "this" to as the first argument and an array of arguments to pass in to the method you are calling as the second arg.
It is worth understanding how the value of this in javascript is determined in addition to just having someone tell you a code fix. In javascript, this is determined the following ways:
If you call a function via an object property as in object.method(), then this will be set to the object inside the method.
If you call a function directly without any object reference such as function(), then this will be set to either the global object (window in a browser) or in strict mode, it will be set to undefined.
If you create a new object with the new operator, then the constructor function for that object will be called with the value of this set to the newly created object instance. You can think of this as the same as item 1 above, the object is created and then the constructor method on it is called.
If you call a function with .call() or .apply() as in function.call(xxx), then you can determine exactly what this is set to by what argument you pass to .call() or .apply(). You can read more about .call() here and .apply() here on MDN.
If you use function.bind(xxx) this creates a small stub function that makes sure your function is called with the desired value of this. Internally, this likely just uses .apply(), but it's a shortcut for when you want a single callback function that will have the right value of this when it's called (when you aren't the direct caller of the function).
In a callback function, the caller of the callback function is responsible for determining the desired value of this. For example, in an event handler callback function, the browser generally sets this to be the DOM object that is handling the event.
There's a nice summary of these various methods here on MDN.
So, in your case, you are making a normal function call when you call privateFunction(). So, as expected the value of this is set as in option 2 above.
If you want to explictly set it to the current value of this in your method, then you can do so like this:
var Example = (function() {
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction.call(this);
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Other methods such as using a closure and defined var that = this are best used for the case of callback functions when you are not the caller of the function and thus can't use 1-4. There is no reason to do it that way in your particular case. I would say that using .call() is a better practice. Then, your function can actually use this and can behave like a private method which appears to be the behavior you seek.
I guess most used way to get this done is by simply caching (storing) the value of this in a local context variable
function Example() {
var that = this;
// ...
function privateFunction() {
console.log(that);
}
this.publicFunction = function() {
privateFunction();
}
}
a more convenient way is to invoke Function.prototype.bind to bind a context to a function (forever). However, the only restriction here is that this requires a ES5-ready browser and bound functions are slightly slower.
var privateFunction = function() {
console.log(this);
}.bind(this);
I would say the proper way is to use prototyping since it was after all how Javascript was designed. So:
var Example = function(){
this.prop = 'whatever';
}
Example.prototype.fn_1 = function(){
console.log(this.prop);
return this
}
Example.prototype.fn_2 = function(){
this.prop = 'not whatever';
return this
}
var e = new Example();
e.fn_1() //whatever
e.fn_2().fn_1() //not whatever
Here's a fiddle http://jsfiddle.net/BFm2V/
If you're not using EcmaScript5, I'd recommend using Underscore's (or LoDash's) bind function.
In addition to the other answers given here, if you don't have an ES5-ready browser, you can create your own "permanently-bound function" quite simply with code like so:
function boundFn(thisobj, fn) {
return function() {
fn.apply(thisobj, arguments);
};
}
Then use it like this:
var Example = (function() {
function Example() {
var privateFunction = boundFn(this, function() {
// "this" inside here is the same "this" that was passed to boundFn.
console.log(this);
});
this.publicFunction = function() {
privateFunction();
}
}
return Example;
}()); // I prefer this order of parentheses
VoilĂ -- this is magically the outer context's this instead of the inner one!
You can even get ES5-like functionality if it's missing in your browser like so (this does nothing if you already have it):
if (!Function.prototype.bind) {
Function.prototype.bind = function (thisobj) {
var that = this;
return function() {
that.apply(thisobj, arguments);
};
}:
}
Then use var yourFunction = function() {}.bind(thisobj); exactly the same way.
ES5-like code that is fully compliant (as possible), checking parameter types and so on, can be found at mozilla Function.prototype.bind. There are some differences that could trip you up if you're doing a few different advanced things with functions, so read up on it at the link if you want to go that route.
I would say assigning self to this is a common technique:
function Example() {
var self = this;
function privateFunction() {
console.log(self);
}
self.publicFunction = function() {
privateFunction();
};
}
Using apply (as others have suggested) also works, though it's a bit more complex in my opinion.
It might be beyond the scope of this question, but I would also recommend considering a different approach to JavaScript where you actually don't use the this keyword at all. A former colleague of mine at ThoughtWorks, Pete Hodgson, wrote a really helpful article, Class-less JavaScript, explaining one way to do this.
My question is rather weird, it has to do with something i have seen in jQuery but so far i have been unable to recreate it.
in jQuery you can go like this
jQuery('div').append
or
jQuery.ajax
the application i am making would need a similar syntax, i notice if you use new like
var that=new function(){
}
you can call the function with just that, without the (), but in some cases i would need it.
The reason for this is some functions i have need to select a dom element just like jQuery so.
that('[data-something="this"]').setEvent('click',functin(){})
and some automatically do it so:
that.loadIt('this','[data-something="that"]')
the reason for this is that the dom elements are loaded externally and pushed, then the script waits for it to be ready before continuing. and doing it this way, to me anyway seems like the most cleanest way to get this functionality (i am coding a full javascript framework so i avoid libraries to keep the scripts fast)
Functions are objects.
Just get rid of new, and add properties directly to that.
var that = function() {
// do some work
}
that.loadit = function() {
// do other work
}
Since you're trying to achieve something like jQuery does, then have that call a constructor.
;(function(global) {
// function to be publicly exposed
var that = function(foo, bar) {
return new MyLibrary(foo, bar);
}
// publicly expose the function
global.that = that;
// use the function as a namespace for utilities
that.loadit = function() {
// do other work
}
// The actual constructor function, like the internal jQuery constructor
MyLibrary(foo, bar) {
// constructor function
}
// Prototypal inheritance of objects created from the constructor
MyLibrary.prototype.setEvent = function() {
// do some work
return this; // allows for method chaining
};
MyLibrary.prototype.otherMethod = function() {
// do something else
return this; // allows for method chaining
};
})(this);
Functions are objects and can have properties, just like other objects can. So, you can add a property to a function like this:
function myFunc(){}
myFunc.someFunc = function(){}
If you use new myFunc the resulting object won't have someFunc as it's not part of the prototype.
So, you can make something like this:
function myFunc(){
// This lets you do "myFunc()" instead of "new myFunc()"
if (!(this instanceof myFunc)) {
return new myFunc();
}
else{
this.val = 0;
this.setVal = function(x){
this.val = x;
// for function chaining
return this;
}
this.getVal = function(){
return this.val;
}
}
}
// This function is not part of the prototype
myFunc.test = function(){
alert('hi');
}
// Some tests
var obj = myFunc();
obj.setVal(12).getVal(); // 12
myFunc.test();
obj.test(); // Error: 'test' is not a function
myFunc.getVal(); // Error: 'getVal' is not a function
$.fn.loadIt=function(var1,var2) {
// $(this) is automatically passed
// do stuff
}
call it like this
$('#element').loadIt('a variable','another variable');
How is it that jQuery can do $("#foo").addClass("bar") and $.ajax()?
I'm creating a micro javascript framework and want to create a new instance of an object, such as $("#hello"). With this object there are associated methods, such as addClass, css, etc, just like with jQuery. So I could do something like
$("#foo").addClass("remove").css("color", "red");
I have been successful in creating this. However, when I want to call a method from this object, such as $.ajax, the constructor function is overwritten, and I can call $.ajax, but not $("#foo").
Basically, how can jQuery do both?
$ = function(arg) { console.log("$ function called with " + arg); }
$.ajax = function(arg) {console.log("$.ajax called with " + arg);}
$('foo');
$.ajax('bar');
http://jsfiddle.net/ac7nx/
I don't think there's any magic here. $ is just a name for the global function. Just keep in mind that in javascript, functions are first class objects that can have their own properties, including sub-functions, which is what $.ajax is.
Since you mentioned the constructor function, I should note that there are no OO objects being used here, just regular functions (no new keyword), so constructor functions don't play into this. If you are using the new keyword, that is probably where you are getting confused. If you want $('#foo') to return a new object, then inside the $ function's code you should create a new object using new and return that, which is what jQuery does, but the $ function itself is not a constructor and should not be called with new. Or in the case of something like $('#someID'), inside that function jQuery is getting an element object from the DOM and then returning that object, but still $ is just a regular function whose return value is an object, not a constructor function.
OK, the $ function is not only a function but an object, like all functions. So it can have methods. That's all that ajax is, a method of the $ function. So we can start off by doing this:
$ = function(obj) {
// some code
};
$.ajax = function (arg1, arg2) {
// some ajax-y code
};
So far so good. Now, what on earth do we put in the $ function? Well it has to return an object and that object has to have some nice methods defined on it. So we'll need a constructor function (to give us new objects) and a prototype (to provide the nifty methods for those objects).
$ = function(obj) {
var myConstructor = function (obj) {
this.wrappedObj = obj;
};
myConstructor.prototype = {
niftyMethod: function () {
// do something with this.wrappedObj
return this; // so we can chain method calls
},
anotherNiftyMethod: function (options) {
// do something with this.wrappedObj and options
return this;
}
};
return new myConstructor(obj);
};
So there we have it. We can do this:
var mySnazzObject = $("whatever");
mySnazzObject.niftyMethod().anotherNiftyMethod(true);
And we can do this:
$.ajax("overthere.html", data);
Obviously jQuery does a heck of a lot more than that, and it does it in some really impressive ways, but that's the general idea.
UPDATE: AS #Raynos was kind enough to observe without supplying a constructive answer, my original code would create the prototype ad infinitum. So we make use of an anonymous autoexecuting function to declare the constructor and prototype separately:
(function () {
var myConstructor = function (obj) {
this.wrappedObj = obj;
};
myConstructor.prototype = {
niftyMethod: function () {
// do something with this.wrappedObj
return this; // so we can chain method calls
},
anotherNiftyMethod: function (options) {
// do something with this.wrappedObj and options
return this;
}
};
var $ = function(obj) {
return new myConstructor(obj);
};
$.ajax = function (arg1, arg2) {
// some ajax-y code
};
window.$ = $;
}());
Two different things:
$.ajax is a prototyped function of jQuery called ajax.
While $('foo') is a call of the jQuery function and depending on the type of foo it reacts in different ways. At http://api.jquery.com/jQuery you can see that jQuery (almost the same as $) can react on three different types: selectors, html or callbacks.
You can imitate the magic jQuery behaviour as following:
//define
var _$ = {};
var $ = function(selector) {
_$.html = function(args) {
alert("Doing the method 'html' with arguments " + args
+ " for selector " + selector);
return _$; //needed for pipeline
};
return _$;
};
$.ajax = function(args){alert("Doing the method 'ajax' "); }
//and try
$("selector1").html("args1").html("args2");
$.ajax("args2");
In this code I created a function called someFunction. Then I modified Function.prototype.apply and call methods. So instead of my function code is working I am running my interception code (which shows an alert). But neither "call" nor "apply" intercepts direct method call. Is it possiple to intercept this?
Function.prototype.call = function(){alert("call");};
Function.prototype.apply = function(){alert("apply");};
function someFunction(){}
window.onload = function(){
someFunction.call(this); //call alert is shown
someFunction.apply(this); //apply alert is shown
someFunction(); //how can I intercept this?
}
You can only override a known function by setting another function in its place (e.g., you can't intercept ALL function calls):
(function () {
// An anonymous function wrapper helps you keep oldSomeFunction private
var oldSomeFunction = someFunction;
someFunction = function () {
alert("intercepted!");
oldSomeFunction();
}
})();
Note that, if someFunction was already aliased/referenced by another script before it was changed by this code, those references would still point to the original function not be overridden by the replacement function.
Function.prototype.callWithIntercept = function () {
alert("intercept");
return this.apply(null, arguments);
};
var num = parseInt.callWithIntercept("100px", 10);
It is worth noting that in newer versions of JS, there are Proxy objects you can use:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
There is a chance you can intercept direct function call. This requires:
Either the function is created by Function.prototype.bind and you have to overwrite Function.prototype.bind before creating the function, or
The function is created from Function() (or new Function()) and you also have to overwrite Function function before creating the target function.
If neither of the above two can be met, the only way to intercept a direct call is to wrap the target function, which is the solution provided by AndyE https://stackoverflow.com/a/3406523/1316480
For a function that is created by function literal and is hidden in private scope, there is no way to intercept a direct call to it.
I have a blog post concludes all of these: http://nealxyc.wordpress.com/2013/11/25/intercepting-javascript-function/
You could iterate over the global scope and replace any objects of function type you find which aren't "yours".
Brilliant, love it :)
const originalApply = window.Function.prototype.apply;
window.Function.prototype.apply = function(){
console.log("INTERCEPTING APPLY", arguments);
return originalApply.call(this, ...arguments);
};
You can achieve this with a Proxy.
First define a handler with an apply trap that intercepts calls to the function. Then, using that handler, set the function to be a proxy onto itself. Example:
function add(a, b){
return a + b;
}
const handler = {
apply: function(target, thisArg, argumentsList) {
console.log('add was called with ' + argumentsList.join(' and '));
return target(...argumentsList);
}
};
add = new Proxy(add, handler);
var m = add(3, 5);
console.log('m = ', m);
var n = add(12, 8);
console.log('n = ', n);