Does jQuery provides a API to call functions binding the this variable to the jQuery object? E.g.:
function log() {
console.log(this);
}
$('body').execute(log); // prints the $('body') object in the console
I know that this could be solved by making the log a plugin, but I don't want to do that because the function I need to call is generic and I don't want to tie it to jQuery.
EDIT:
jQuery has no execute method, its just a snippet I added to demonstrate what I am trying to achieve.
EDIT 2:
I am not asking how to workaround this problem (underscore.js bind already got me covered), I am only asking if jQuery already provides a similar API.
You can do this "without" having jQuery to implement anything, just "flip" things around a bit and use function-name.apply (object, arguments)
function func (arg1, arg2) {
console.log ("ARG1: " + arg1);
console.log ("ARG2: " + arg2);
console.log ("using this: " + this.html ().length + "\n");
}
func.apply ($('body'), ['abc','123']);
func.apply ($('body')); // 2nd argument is optional
output
ARG1: abc
ARG2: 123
using this: 51645
ARG1: undefined
ARG2: undefined
using this: 51645
this isn't going to be what you want it to be in your scope. You can use an anonymous function with a parameter, though:
function log($this) {
console.log($this);
}
$('body').execute(function() { log($(this)); }); // prints the $('body') object in the console
Check this out:
$.fn.prototype.execute = function() {
var f = function(){ console.log(this)};
$.proxy( f , this )()
// that means something like
// f.call(this)
}
Assume , you have function:
var f= function( arg1, arg2){
console.log(this, arg1, arg2)
}
There you call f passing context and arguments
f.call(document.body, arg1,arg2);
or arguments as array by apply method
f.apply(document.body, [arg1,arg2]);
In both cases you will get
>> <body>...</body>, valOfarg1, varOfarg2
Related
This overrides console.log without issue and it makes sense to me:
(function(c) {
console.log = function() {
c.apply(console, arguments);
}
})(console.log);
This one does not work and I don't understand why:
(function(c) {
console.log = function() {
c(arguments);
}
})(console.log);
I just get a list of properties when I call console.log.
What's the difference?
I need to build the array with arguments in the second one for it to work.
It works if you modify your function like this:
(function(c) {
console.log = function() {
// c(...arguments) also works
c(...Object.values(arguments));
}
})(console.log);
console.log('hello world')
This is because arguments is not an array, but an array-like object.
function func(...args) {
console.log(arguments);
}
func(1, 2, "hello", "world")
The second code example doesn't works as you expect it to because you are passing the arguments object as it is to the console.log function whereas in the first code example, due to the usage of apply(), the properties in the arguments object are passed as a separate argument. In other words, array or an array-like object is spread in to distinct arguments.
The console.log call in the first code example is similar to the following:
console.log(arg1, arg2, arg3, ...)
whereas in the second one, it is as:
console.log({ 0: arg1, 1: arg2, 2: arg3, ... });
(function(c) {
console.log = function() {
const a = Array.from(arguments);
c(a.join(" "))
}
})(console.log);
This works.
arguments is an object-like so we need to create the string ourselves.
I've seen many questions for that context, but I still can't figure out, what exactly my Problem is. (I'm still experimenting with JavaScript, especially with objects)
Code:
function Field(val)
{ var value = val;
this.__defineGetter__("value", function(){ return value; });
this.__defineSetter__("value", function(val){ value = val; if(this.onchange) this.onchange.call(); });
}
function LW_makePlan()
{
/* [...] */
this.Filter1=new Field("");
this.Filter2=new Field("");
this.Filter3=new Field("");
this.init = function()
{
/* [...] */
this.Filter1.onchange=this.getSomething;
}
this.getSomething = function()
{
arg="modus=doWhat";
arg=arg+"&filter1=" + this.Filter1.value;
arg=arg+"&filter2=" + this.Filter2.value;
arg=arg+"&filter3=" + this.Filter3.value;
request_fkt(null, true, arg , this.setSomething);
}
this.setSomething = function(data)
{
alert(data);
}
this.init();
};
What I'm trying:
test = new LW_makePlan();
test.Filter1.value="anything";
test.Filter1 has an "onchange"-property, that is checked in the setter of "Field". if set, the setter will also call the object given within the onchange-property.
this works so far BUT it seems, that this call creates a whole new object-instance ... no not an instance, it is, as if the function "getSomething" is copied as a stand-alone function, because the Code i called, but for example this.Filter1 within the function "getSomething" is undefined ...
Why is this happening and how can I avoid this?
PS: I don't want to use some type of event-handling-Things from 3rd Party codes, I'd like to do it myself with a little help maybe.
EDIT:
Thanks to Steffen Heil, changed to:
var scope=this;
this.Filter1.onchange=function() { scope.getSomething(); };
and it works!
Your call to this.onchange is in Field, so you are calling a function of Field. The assignment this.Filter1.onchanged=this.getSomething kind of copies the method getSomething from LW_makePlan to Field, where it will be called.
So inside of getSomething that is now called onchanged the reference this referes to the Field not the LW_makePlan.
Replace the assignment with this:
var source = this;
this.Filter1.onchange = function() { return source.getSomething(); };
And it will work. Most frameworks have a bindmethod that makes this more readable (hiding the extra variable in a scope).
In reply to the first comment:
You can explicitly call a function like this:
x.call( object, arg1, arg2, ag3 );
x.apply( object, [ arg1, arg2, arg3 ] );
These the are the same and it does not matter what x is. Inside the called function this has the value of object.
x can be:
alert
window.alert
(function(){})
(alert)
(window.alert)
Normal calls to a function are shortcuts:
object.f = g;
object.f( arg1 ) => g.call( object, arg1 );
f( arg1 ) => f.call( window, arg1 );
While window is the global object in a browser; other environments may use another global object.
While the difference between these two shortcuts seems tivial, what about the following?
(object.f)( arg1 )
This is completely valid javascript, as object.f is a function and a function can be invoked using (args1). But:
object.f = g;
(object.f)( arg1 ) => g.call( window, arg1 )
So a.f = b.f; copies a member reference from a to b, but the this context, the code is executon on depends on the way f is called.
a.f(x) == a.f.call(a,x) == (a.f).call(a,x) == b.f.call(a,x) == (b.f).call(a,x)
b.f(x) == b.f.call(b,x) == (b.f).call(b,x) == a.f.call(b,x) == (a.f).call(b,x)
By the way, you can define your own bind very easily:
function bind( object, method ) {
return function() {
return object[ method ].apply( object, arguments );
};
}
Then the original code would become:
this.Filter1.onchange = bind( this, 'getSomething' );
This would match the fix I gave above using "late binding". Most libraries prefer "early binding":
function bind( object, method ) {
return function() {
return method.apply( object, arguments );
};
}
Then the original code would become:
this.Filter1.onchange = bind( this, this.getSomething );
The advantage is better performance, but the main difference is what happens, when getSomething changes after bind was called. The first implementation calls the new value, the second the old value.
I have a lot of my code inside an object literal and there are a couple functions where I want to be able to pass the functions arguments for the parameters but I can't figure out how to do that.
Here is an example of my object..
var test = {
button: $('.button'),
init: function() {
test.button.on('click', this.doSomething);
},
doSomething: function(event, param1, param2) {
console.log(param1);
console.log(param2);
}
};
So when the button is clicked and it calls the function doSomething I want to pass in arguments for param1 and param2.
Something similar to this maybe, but this does not work.
test.button.on('click', this.doSomething('click', 'arg1', 'arg2'));
Any ideas, or am I going about this the wrong way?
The jQuery.proxy() function seems to be exactly what you need. Have a good read at the docs and see if they make sense to you. For your specific example,
var test = {
button: $('.button'),
init: function() {
test.button.on('click', $.proxy(this.doSomething, null, 'arg1', 'arg2');
},
doSomething: function(param1, param2, event) {
console.log(param1);
console.log(param2);
}
};
In this example, the parameters to $.proxy are:
this.doSomething - The the function to call
null - The context in which the function will be called. By supplying null, we are saying to use its 'normal' context.
arg1 - The value of param1 formal parameter of the called function
arg2 - The value of param2 formal parameter of the called function
Since the click callback supplied the final parameter (event), that is already provided and doesn't need to be additionally or explicitly declared. The jQuery.proxy() when passed additional parameters passes those at the front of the formal parameter list and any remaining parameters implicitly supplied are passed at the end. So if we a function that looks like:
var f = function(a, b, c) {
console.log(a, b, c);
};
and invoke it through a proxy:
var p = $.proxy(f, null, 2, 3);
p(1);
The value of a, b and c that are logged will be 2,3,1.
This question is also extremely close to this one.
How can I pass arguments to event handlers in jQuery?
I have a custom object that implements a function that'll be executed later. Here's how someone would call it:
customObject.onSomething(function(e) {
// do something with e
console.log('foobar');
});
Here's how onSomething is getting created:
var CustomObject = function() {
this.onSomething = function(callback) {
// If the user passes in parameter(s), how can I modify them before calling?
callback.apply(this);
}
}
How can I modify the argument(s) the user passed in before performing apply or call on the function?
apply takes a second parameter which is a list of arguments to pass to the function. call does the same, except it passes its own argument-list (everything after the first parameter which is used as this).
So, if you know which parameters you expect, you can just add them to the invoking function as the second parameter to apply (or as a list of parameters to call):
this.onSomething = function(arg1, arg2) {
// reverse the first and second arguments
callback.apply(this, [arg2, arg1]);
// equivalent:
callback.call(this, arg2, arg1);
};
If you don't know what kind of arguments to expect, but you still want to do something with them, you can do so with the builtin arguments pseudo-array which holds the arguments given to the current function (even when you don't declare them explicitly).
You can use this to invoke the callback with the same arguments given to the invoking function, or some transformation of them; e.g.:
this.onSomething = function() {
// call callback with the same arguments we got
callback.apply(this, arguments);
// or, make some changes
var newArgs = ["extra argument", arguments[1], arguments[0]];
callback.apply(this, newArgs);
};
Sounds like what you're asking for is fairly simple, see below:
var CustomObject = function() {
this.onSomething = function(callback, param1, param2) {
param1 += 4;
param2 = 'Something about ' + param2 + ' is different...';
callback.apply(this, [param1, param2]);
}
}
Hi all I am new to jquery and javascript.I have written a code and want to test it.But I am stuck at some point and need help from experts.
Here is my jquery class define in myfile.js
$.fn.MyClass = function(arg1 ,arg2, arg3) {
function MyFirstFunc (arg1, arg3) {
//Do some thing
return arg1 + arg3;
};
function MySecFunc (arg2, arg3) {
//Do some thing
};
MyFirstFunc(arf1 ,arg2);
}
And in my index.html file I have to call my jquery class on DOM object like this
$("#div1").MyClass(agr1_val, arg2_val, arg3_val);
Now I want to do unit testing of these functions and keep the test cases in a separate file.I have chose Qunit.
I have wrote following test
test('MyFirstFunction function test', function() {
equal(MyFirstFunc (4, 5),"9" ,"function working correctly");
});
Also in my index_test.html file I have given the path of myfile.js but still Qunit is giving me error
Died on test #1 at file:///var/www/test.js:2:1: MyFirstFunc is not defined
Can any one kindly guide me where I am doing wrong and how to test functions by keeping the test cases in a separate file
Or any other better way to do it.
Thanks
The MyFirstFunc method is not defined. MyFirstFunc and MySecFunc must be defined in the return array in order to publicly use those functions. You can define it like so
$.fn.MyClass = function (arg1, arg2, arg3) {
function MyFirstFunc(arg1, arg3) {
//Do some thing
return arg1 + arg3;
};
function MySecFunc(arg2, arg3) {
//Do some thing
};
return {
MyFirstFunc: function (arg1, arg3) {
return MyFirstFunc(arg1, arg3);
},
MySecFunc: function (arg2, arg3) {
return MySecFunc(arg2, arg3);
}
}
}
And test it with
test('MyFirstFunction function test', function() {
equal($().MyClass().MyFirstFunc(4, 5),"9" ,"function working correctly");
});
The problem isn't with your test case, it's with your function definitions. You can't simply declare a function inside another function and expect to access it in the global namespace. A fundamental property of function definitions in JavaScript is that they're the only things that provide scope, so when you use a function as an object the object's "instance variables" (declared inside the function) are only accessible inside that object.
If you're trying to create an object with methods, you need to assign those functions to instance variables of the object. Instance variables of an object in JavaScript are defined by assigning this.<variable> in the constructor function (the function you use to define the object). So to give MyClass two methods, you should do something like this:
MyClass = function(arg1, arg2, arg3) {
this.myFirstFunc = function(arg1, arg2) {
//Do something
};
this.mySecFunc = function(arg1, arg2) {
//Do something
};
};
var myInstance = new MyClass(x,y,z);
myInstance.myFirstFunc(a,b);
Note that simply giving your functions' arguments the same name as the constructor's arguments doesn't pass the arguments from the constructor to the method. Defining a function does not pass it concrete arguments, so the MyFirstFunc arg1 and arg2 are interpreted as just names of the arguments you might pass to MyFirstFunc, not instructions to pass the outer function's arguments to MyFirstFunc.
Finally, the $.fn property is an alias to jquery.prototype, so what you're doing with $.fn.MyClass = function... is assigning a new property to the JQuery object, essentially patching the library with your own add-on. This probably isn't how you want to define functions in general, though I don't know your use case.
First of all your are declaring bad methods in your jquery object.
Instead of:
function MyFirstFunc (arg1, arg3) {
You should use: this.MyFirstFunc = function(arg1, arg3) {
Also you should call your methods with a jQuery object as in this example:
equal($("#div1").MyClass(1, 2, 3).MyFirstFunc(4, 5),"9" ,"function working correctly");
Here is an example of a working Qunit test:
$.fn.MyClass = function(arg1 ,arg2, arg3) {
this.MyFirstFunc = function(arg1, arg3) {
//Do some thing
return arg1 + arg3;
};
this.MySecFunc = function(arg2, arg3) {
//Do some thing
};
return this.MyFirstFunc(arg1, arg3);
};
$(function() {
$("#div1").MyClass(1, 2, 3);
});
test('MyClass function test', function() {
equal($("#div1").MyClass (1,2,3),"4" ,"function working correctly");
});