How to pass arguments when extending a jQuery method? - javascript

I'm trying to modify .offset() so that it never returns undefined, i.e. for invisible/hidden elements.
The problem that I'm having with that is that I don't know how to properly pass this to the original method, to be honest. Breakpoint-debugging reveals that I keep getting Window when it should be the element in question. Here's what I got:
(function( $ ){
var original = $.fn.offset;
$.fn.offset = function( elem, coordinates, pass ) {
console.log('step 1'); // successful.
console.log(this); // element confirmation.
var o = original(elem, coordinates, pass); // triggers an exception.
console.log('step 2'); // is never reached.
if (o === undefined ) {
console.log('step 3a');
return {};
}
else {
console.log('step 3b');
return o;
}
}
}( jQuery ));
[…]
$('#element').offset(); // method call.
And here's the exception:
Error in event handler for (unknown): TypeError: undefined is not a function
at jQuery.fn.extend.offset (chrome-extension://…/js/jquery-1.11.0.js:10109:10)
at $.fn.offset (chrome-extension://…/js/jquery-modifications.js:35:11)
I've tried different variations – original(arguments), this.original(), original(this, …) – but none worked. In this question an argument called elem is used next to three more arguments – but I'm not sure why. Is it because the API mentions attributeName, value and a callback function? If so then my attempt should work, analogous to the .offset() API. Looking at how jQuery defines these functions didn't help either because .fn.attr doesn't even show up like .fn.offset does.

As you said:
The problem that I'm having with that is that I don't know how to properly pass this to the original method, to be honest.
In order to change function scope (this) there are two Function Prototype methods, called call() and apply()
References to documentation of these two methods:
Function.prototype.call()
Function.prototype.apply()
So, your code should now looks like this:
var original = $.fn.offset;
$.fn.offset = function() {
var o = original.apply(this, arguments);
return typeof o === 'undefined' ? {} : o;
};
and
return typeof o === 'undefined' ? {} : o;
It's just a better (IMO) shorter version of:
if (typeof o === 'undefined') {
return {};
} else {
return o;
}

Related

Can a javascript Function object be called?

This is weird! I am struggling hard with the problem that javascript Proxy handler's apply trap does not get the Proxy itself passed as an argument, only the naked function. But I need to pass along some metadata to use for re-wrapping function results.
So I thought I could just use Object.create to make a specialization of the function to which I could stick additional information. But that doesn't work! And that's surprising!
function test() { return "hi!"; }
test() // 'hi!'
test1 = test // ƒ test() { return "hi!"; }
test1() // 'hi!'
test2 = Object.create(test) // Function {}
test2() // Uncaught TypeError: test2 is not a function
test3 = new Function([], "return 'lo!';") // ƒ anonymous() { return 'lo!'; }
test3() // 'lo!'
test2.prototype.constructor() // 'hi!'
test3.prototype.constructor() // 'lo!'
Object.getPrototypeOd(test2)() // 'hi!'
So I guess I can could help myself evaluate a function if I unwrapped it somehow:
while(fn instanceof Function && typeof fn != 'function')
fn = Object.getPrototypeOf(fn);
But that doesn't work if I just want to call it, i.e., make my special function transparent to any downstream user.
OK, here is a workaround, instead of using Object.create(fn) I can just wrap it:
fn = function() { return fn.apply(this, arguments); }
now I can stick my special metadata to this fn and I can also wrap it in a Proxy.
But my question is: what is the meaning of Object.create(fn) if you don't get an actual callable function?
Short answer: Function can be called; Object cannot be called.
See Function like inheritance of Object. Functions are object but objects are not function.
See this:
function test() { return "hi!"; }
test() // 'hi!'
typeof test // 'function'
test instanceof Object // true
test2 = Object.create(test)
typeof test2 // 'object'
test2 instanceof Object // true
typeof test2.prototype.constructor // 'function'
// something else that works
sample1 = Object.create({test})
typeof sample1 // 'object'
typeof sample1.test // 'function'
sample1.test() // 'hi!'
If you insist to make an Object callable, maybe your closest option is to create your own class that have function behaviour. There're some suggestions here: Can you make an object 'callable'?

Underscore chaining inner workings

I'm, curios how the _.chaining function is implemented and how (or better, why) it works the way it does.
Especially my question is where does the wrapping for each function happen. Let's assume I'm using _.chain(someArray).filter(...); When I step into the function, I can see that the filter function got transformed into something like
function () {
var args = [this._wrapped]; //the data from chain(...)
push.apply(args, arguments); //push to the (?) array
return result.call(this, func.apply(_, args)); //?? where are these coming from?
}
I can see that the function has 3 Closures in it's scope (compare this to the un-chained function that show the definition of the function without all the Closure to it's original function)
The first one is the find function itself, the second "the safe reference to the object itself" and the third on the underscore class itself.
When calling _.chain(), how and where (code-wise) does the transformation (the creating of the scopes etc). I can see that
//http://underscorejs.org/docs/underscore.html#section-139
_.chain = function(obj) {
return _(obj).chain();
};
gets called and this goes to
//http://underscorejs.org/docs/underscore.html#section-145
//...
chain: function() {
this._chain = true;
return this;
},
//...
Then I'm stuck. I can't figure out what happens from there. I assume that the magic happens inside the constructor, but I can't seem to figure out where the additional creation of the Closures comes in. All the functions themselves don't show any sign of being wrapped, the chain call doesn't look like it wraps something. result seems to be there but I don't know where it came from. So, where and how does this happen?
_.chain(obj) returns new instance of _ with attribute _chain = true, that instance of _ has a _wrapped attribute set to current object (great work here). _.mixin(_) in line #1210 add all of underscore methods to underscore (constructor). _.mixin method replace and extend _ methods (still has the parent functions! Accessible via _.prototype). _.mixin change functions of that _ instance (this is the place you see that new function).
New function is:
function () {
var args = [this._wrapped];
push.apply(args, arguments);
return result.call(this, func.apply(_, args));
}
(doesn't matter what method it is, same for all, func is referenced to the original method)
result method function is :
var result = function(obj) {
return this._chain ? _(obj).chain() : obj;
};
so if the object returned by func.apply(_, args) has _chain (_.chain set that attribute) returns _(obj).chain() then you can use it again :)
This is process of chaining but what about prototypes!
In constructor function :
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj); // This line do the magic
this._wrapped = obj;
};
Consider this :
func = function(a){this.a = a;}
b = func(2);
b.a // TypeError: Cannot read property 'a' of undefined
c = new func(2);
c.a // returns 2, whooa this the magical javascript!
Read this (Underscore docs about OOP) if you want to learn more about underscore mixin function
Am I missing something?

How do you find the arity of an anonymous function in strict mode?

Following the jQuery plugin pattern, how can one find the arity of a function, say the methods.myfunc function, in the case where we've used apply() to define the scope of this and applying arguments to this?
(function($, window, document ){
"use strict";
//...
methods = {
myfunc: function(){
// myfunc.length? tried, didnt work
// arguments.length? tried, didnt work
// methods.myfunc.length? tried, didnt work
// arguments.callee tried, doesnt work in strict mode
}
//...
}
$.MyPluginThing = function( method ){
if( methods[method] ){
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
}else if( typeof method === "object" || ! method ){
return methods.init.apply( this, arguments, window );
}else{
$.error( "Method " + method + " does not exist on jQuery.MyPluginThing" );
}
}...
This may expose some of my ignorance with function scope, but I'm pretty stumped here, and have not found an example that explains this well enough.
Part of my inspiration for this question comes from NodeJS/ExpressJS, where they have a variable number of arguments for some functions. If 3 arguments are passed, for example, it's assumed there is an error object, but you could just as easily pass two and there's no problem!
update: changed function in code from init to myfunc
You'll have to use a named function expression (with all its idiosyncrasies):
var methods = {
init : function init () {
var arity = init.length;
}
};
Here's the fiddle: http://jsfiddle.net/tqJSK/
To be honest though, I have no idea why you'd ever need this. You can hard code that number within the function, since the amount of named arguments will never change...
Update: as pointed out by #T.J.Crowder, you can use regular function declarations instead:
(function($, window, document) {
function init () {
var arity = init.length;
}
var methods = {
init : init
};
}(jQuery, window, document));
Update 2: if all you're looking for is the number of arguments supplied in this specific call, just use arguments.length:
var methods = {
init : function () {
var count = arguments.length;
}
};
Here's the fiddle: http://jsfiddle.net/tqJSK/1/

using call and apply for properties?

Let's say I have a variable like this:
var a = this.property || this.parent.anotherProperty;
It's possible to set the context (by context i mean 'this', maybe the 'scope' is a better word...) for a like when using .call() or .apply() for functions?
EDIT:
I have an helper function that given a value return:
if the value is a function -> value()
if it isn't a function -> value
This is the code:
function unwrapValue(value){
return typeof value === 'function' ? value() : value;
}
unwrapValue is inside a plain object (Utils) and it's called from outside this object:
Utils.unwrapValue(value);
Now, I have a property url in a function (that may be either a function or something else):
this.url = this.baseUrl || this.collection.baseUrl;
I don't know if this.url is a function or something else so I use unwrapValue to get the value of url:
var params = {};
params.url = Utils.unwrapValue(this.url);
And the problem is here, unwrapValue return this.url but setting 'this' to something else (i tought it was the Utils object but for some reason it's the window object) so params.url is window.baseUrl || window.collection.baseUrl which is not what i want.
If value is a function I solved this way:
function unwrapValue(value, context){
if(typeof value === 'function'){
return typeof context === 'undefined' ? value() : value.call(context);
}else{
return value;
}
}
so that if a second parameter context is passed to unwrapValue, value's this will be set to context.
with this question I was searching a way to use context aslo in the case value wasn't a function like:
this.url = this.baseUrl || this.collection.url;
And just to clarify a little more: this.baseUrl and this.collection.url are simple strings
There's a way to solve that?
You possibly want to pass an object literal as the thisArg into that function:
function fn() {
var a = this.a || this.parent.a;
console.log(a);
}
fn.call({
a: false,
parent: {
a: "foobar"
}
});
As you don't pass in further arguments, you could've used apply as well instead of call.
Assuming that a is a function (it's unclear from your question) then you can use Function.bind to set the context for all calls made to it:
var a = (this.a || this.parent.a).bind(this);
See MDN for more information and a shim for older browsers.

Is it possible to call function.apply without changing the context?

In some Javascript code (node.js specifically), I need to call a function with an unknown set of arguments without changing the context. For example:
function fn() {
var args = Array.prototype.slice.call(arguments);
otherFn.apply(this, args);
}
The problem in the above is that when I call apply, I'm change the context by passing this as the first argument. I'd like to pass args to the function being called without changing the context of the function being called. I essentially want to do this:
function fn() {
var args = Array.prototype.slice.call(arguments);
otherFn.apply(<otherFn's original context>, args);
}
Edit: Adding more detail regarding my specific question. I am creating a Client class that contains a socket (socket.io) object among other info pertaining to a connection. I am exposing the socket's event listeners via the client object itself.
class Client
constructor: (socket) ->
#socket = socket
#avatar = socket.handshake.avatar
#listeners = {}
addListener: (name, handler) ->
#listeners[name] ||= {}
#listeners[name][handler.clientListenerId] = wrapper = =>
# append client object as the first argument before passing to handler
args = Array.prototype.slice.call(arguments)
args.unshift(this)
handler.apply(this, args) # <---- HANDLER'S CONTEXT IS CHANGING HERE :(
#socket.addListener(name, wrapper)
removeListener: (name, handler) ->
try
obj = #listeners[name]
#socket.removeListener(obj[handler.clientListenerId])
delete obj[handler.clientListenerId]
Note that clientListenerId is a custom unique identifier property that is essentially the same as the answer found here.
If I understand you correctly:
changes context
| n | y |
accepts array n | func() | func.call() |
of arguments y | ???????? | func.apply() |
PHP has a function for this, call_user_func_array. Unfortunately, JavaScript is lacking in this regard. It looks like you simulate this behavior using eval().
Function.prototype.invoke = function(args) {
var i, code = 'this(';
for (i=0; i<args.length; i++) {
if (i) { code += ',' }
code += 'args[' + i + ']';
}
eval(code + ');');
}
Yes, I know. Nobody likes eval(). It's slow and dangerous. However, in this situation you probably don't have to worry about cross-site scripting, at least, as all variables are contained within the function. Really, it's too bad that JavaScript doesn't have a native function for this, but I suppose that it's for situations like this that we have eval.
Proof that it works:
function showArgs() {
for (x in arguments) {console.log(arguments[x]);}
}
showArgs.invoke(['foo',/bar/g]);
showArgs.invoke([window,[1,2,3]]);
Firefox console output:
--
[12:31:05.778] "foo"
[12:31:05.778] [object RegExp]
[12:31:05.778] [object Window]
[12:31:05.778] [object Array]
Simply put, just assign the this to what you want it to be, which is otherFn:
function fn() {
var args = Array.prototype.slice.call(arguments);
otherFn.apply(otherFn, args);
}
'this' is a reference to your function's context. That's really the point.
If you mean to call it in the context of a different object like this:
otherObj.otherFn(args)
then simply substitute that object in for the context:
otherObj.otherFn.apply(otherObj, args);
That should be it.
If you bind the function to an object and you use everywhere the bound function, you can call apply with null, but still get the correct context
var Person = function(name){
this.name = name;
}
Person.prototype.printName = function(){
console.log("Name: " + this.name);
}
var bob = new Person("Bob");
bob.printName.apply(null); //window.name
bob.printName.bind(bob).apply(null); //"Bob"
One way that you can work around the change of context that can happen in JavaScript when functions are called, is to use methods that are part of the object's constructor if you need them to be able to operate in a context where this is not going to mean the parent object, by effectively creating a local private variable to store the original this identifier.
I concede that - like most discussions of scope in JavaScript - this is not entirely clear, so here is an example of how I have done this:
function CounterType()
{
var counter=1;
var self=this; // 'self' will now be visible to all
var incrementCount = function()
{
// it doesn't matter that 'this' has changed because 'self' now points to CounterType()
self.counter++;
};
}
function SecondaryType()
{
var myCounter = new CounterType();
console.log("First Counter : "+myCounter.counter); // 0
myCounter.incrementCount.apply(this);
console.log("Second Counter: "+myCounter.counter); // 1
}
These days you can use rest parameters:
function fn(...args) {
otherFn(...args);
}
The only downside is, if you want to use some specific params in fn, you have to extract it from args:
function fn(...args) {
let importantParam = args[2]; //third param
// ...
otherFn(...args);
}
Here's an example to try (ES next version to keep it short):
// a one-line "sum any number of arguments" function
const sum = (...args) => args.reduce((sum, value) => sum + value);
// a "proxy" function to test:
var pass = (...args) => sum(...args);
console.log(pass(1, 2, 15));
I'm not going to accept this as an answer, as I'm still hoping for something more suitable. But here's the approach I'm using right now based upon the feedback on this question so far.
For any class that will be calling Client.prototype.addListener or Client.prototype.removeListener, I did added the following code to their constructor:
class ExampleClass
constructor: ->
# ...
for name, fn of this
this[name] = fn.bind(this) if typeof(fn) == 'function'
message: (recipient, body) ->
# ...
broadcast: (body) ->
# ...
In the above example, message and broadcast will always be bound to the new ExampleClass prototype object when it's instantiated, allowing the addListener code in my original question to work.
I'm sure some of you are wondering why I didn't just do something like the following:
example = new ExampleClass
client.addListener('message', example.bind(example))
# ...
client.removeListener('message', example.bind(example))
The problem is that every time .bind( ) is called, it's a new object. So that means that the following is true:
example.bind(example) != example.bind(example)
As such, the removeListener would never work successfully, thus my binding the method once when the object is instantiated.
Since you seem to want to be using the bind function as it is defined in Javascript 1.8.5, and be able to retrieve the original this object you pass the bind function, I recommend redefining the Function.prototype.bind function:
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
/** here's the additional code **/
fBound.getContext = function() {
return oThis;
};
/**/
return fBound;
};
Now you can retrieve the original context that you called the bind function with:
function A() {
return this.foo+' '+this.bar;
}
var HelloWorld = A.bind({
foo: 'hello',
bar: 'world',
});
HelloWorld(); // returns "hello world";
HelloWorld.getContext(); // returns {foo:"hello", bar:"world"};
I was just reminded of this question after a long time. Looking back now, I think what I was really trying to accomplish here was something similar to how the React library works with its automatic binding.
Essentially, each function is a wrapped bound function being called:
function SomeClass() {
};
SomeClass.prototype.whoami = function () {
return this;
};
SomeClass.createInstance = function () {
var obj = new SomeClass();
for (var fn in obj) {
if (typeof obj[fn] == 'function') {
var original = obj[fn];
obj[fn] = function () {
return original.apply(obj, arguments);
};
}
}
return obj;
};
var instance = SomeClass.createInstance();
instance.whoami() == instance; // true
instance.whoami.apply(null) == instance; // true
Just push properties directly to the function's object and call it with it's own "context".
function otherFn() {
console.log(this.foo+' '+this.bar); // prints: "hello world" when called from rootFn()
}
otherFn.foo = 'hello';
otherFn.bar = 'world';
function rootFn() {
// by the way, unless you are removing or adding elements to 'arguments',
// just pass the arguments object directly instead of casting it to Array
otherFn.apply(otherFn, arguments);
}

Categories

Resources