Eventmanager that calls other objects' functions - javascript

I'm using javascript and jQuery. I have a "problem" that my web app has different views, and one action (like mouse click) should do different things in the different views.
I've created an ActionManager that get's notified when an click event is fired. This manager object knows about the current view. Now I wan't the actionmanager to be able to trigger a function in different objects.
What I've done is that the manager gets an instance of the different objects. This is done via a registration when they are initialized, where they register what kind of event they wan't to handle and what kind of view they are responsible for.
the problem is that I want to make this as generic as possible, so that the actionmanager doesn't need to know about what the name of the function that is gonna be called is. Then the different instances can have different function names without letting the actionmanager to know about it. I could let the registration part store the function name for that event in that instance by sending a reference to the function, but then I can't do this:
myfunc : function (myinstance, myfunction)
{
myinstance.myfunction();
}
that will assume that myfunction() exists as a function in myinstance, and not use the real function name which could be "onMouseClick".
Edit:
Explanation for others seeing this tread and wondering why: The reason for my eventmanager is that I want to only add one click event on the elements that need it, and not changing the click event code. I also want to only call one method and run the code in that method. I don't want to call several methods and let the methods decide based on the view if they should run or not.

If myfunction is a string:
You can do this:
myfunc : function (myinstance, myfunction)
{
myinstance[myfunction]();
}
A function on an object is simply a function object assigned to a property on that object. In JavaScript, you can access properties in one of two ways:
Using dotted notation and a literal for the property name, e.g. x = obj.foo;, or
Using bracketed notation and a string for the property name, e.g. x = obj["foo"];
They do exactly the same thing, but of course the second form is much more flexible — designed, in fact, for exactly the situation you're describing.
If myfunction is an actual function object:
You can do this:
myfunc : function (myinstance, myfunction)
{
myfunction.call(myinstance);
}
That calls myfunction and ensures that this = myinstance during the call. All JavaScript function objects have a call function and an apply function. They both do the same thing (call the function with a specific this value), but they differ in how you pass arguments to the function:
With call, you pass them as discrete arguments in the call call:
func.call(inst, arg1, arg2, arg3);
With apply, you pass in an array of arguments:
func.apply(inst, [arg1, arg2, arg3]);
// ^----------------^---- note that this is an array
E.g.:
var a = [arg1, arg2, arg3];
func.apply(inst, a);
Example of all of the above
Live copy - Since you said you were using jQuery, I went ahead and used it for convenience, but none of the above is related to jQuery, it's how vanilla JavaScript works.
var myinstance = {
foo: "bar",
myfunction: function(arg1, arg2) {
display("<code>myfunction</code> called, <code>this.foo</code> is: " + this.foo);
if (arg1) {
display("<code>arg1</code> is: " + arg1);
}
if (arg2) {
display("<code>arg1</code> is: " + arg2);
}
}
};
// Without arguments
callUsingString(myinstance, "myfunction");
sep();
callUsingFunction(myinstance, myinstance.myfunction);
sep();
// With arguments
callUsingStringWithArgs(myinstance, "myfunction", ["one", "two"]);
sep();
callUsingFunctionWithArgs(myinstance, myinstance.myfunction, ["one", "two"]);
sep();
function callUsingString(inst, func) {
inst[func]();
}
function callUsingFunction(inst, func) {
func.call(inst);
}
function callUsingStringWithArgs(inst, func, args) {
// Easier using the function reference:
callUsingFunctionWithArgs(inst, inst[func], args);
}
function callUsingFunctionWithArgs(inst, func, args) {
func.apply(inst, args);
}
(display just appends a paragraph element to the page with the given text; sep just appends an hr element.)
More reading:
Mythical methods
You must remember this

If myFunction is a string you can do it this way.
myfunc : function (myinstance, myfunction)
{
myinstance[myfunction]();
}
But if myFunnction is a function object, you have to call that function. Where the this object within the myFunction references to myinstance.
myfunc : function (myinstance, myfunction)
{
myfunction.call(myinstance);
}
You can do it this way, but why don't you use the .bind() and the .trigger() to handle events? DEMO http://jsfiddle.net/DnjRs/

Related

Confused about when to use `bind` in an Event Handler

The following successfully prints 'foo'.
var obj = {
name: 'foo',
printName: function printName() {
console.log(this.name);
}
};
var printButton= document.getElementById('printIt');
printButton.addEventListener('click', function(){
obj.printName();
});
The following doesn't, however:
printButton.addEventListener('click', obj.printName() );
I know the solution... simply use bind so that we're referencing the obj object. i.e:
printButton.addEventListener('click', obj.printName.bind(obj) );
Why then don't we need to use bind in the first example. I don't know why wrapping obj.printName() function call in the anonymous function results in the console.log correctly referencing and printing this properly, but when called directly after click, you needs to use bind
Alright, I commented with some good information on this question so I might as well answer!
Functions are first class
Okay, let's starts with some fundamentals of javascript that is very dissimilar to some other programming languages: in javascript functions are first class citizens--which is just a fancy way of saying that you can save functions into variables and you can pass functions into other functions.
const myFunction = function () { return 'whoa a function'; }
array.map(function () { return x + 1; });
And because of this wonderful feature, there is a big difference between the expressions:
Expression 1
obj.printName
and
Expression 2
obj.printName();
In expression 1: the function isn't being invoked so the value of the expression is of type function
In expression 2: the function is being invoked so the value of the expression is what the function returns. In your case, that's undefined
addEventListener
The method addEventListener takes in two arguments:
a string of the type of event
a function that will be run when the event fires.
Alight, so what does that mean?
When you call
// doesn't work
printButton.addEventListener('click', obj.printName() );
you're not passing a value of type function to the addEventListener method, you're actually passing undefined.
// works
printButton.addEventListener('click', obj.printName.bind(obj) );
then works (for one reason) because the second argument is actually of type function.
What does bind do? Why does it return a function?
Now we need to discuss what bind actually does. It related to the pointer* this.
*by pointer, I mean a reference identifier to some object
bind is a method that exists on every function object that simply binds the this pointer of a desired object to the function
This is best shown by an example:
Say you have a class Fruit that has a method printName. Now that we know that you can save a method into a variable, let's try that. In the example below we're assigning two things:
boundMethod which used bind
unboundMethod that didn't use bind
class Fruit {
constructor() {
this.name = 'apple';
}
printName() {
console.log(this.name);
}
}
const myFruit = new Fruit();
// take the method `printName`
const boundMethod = myFruit.printName.bind(myFruit);
const unboundMethod = myFruit.printName;
boundMethod(); // works
unboundMethod(); // doesn't work
So what happens when you don't call bind? Why doesn't that work?
If you don't call bind in this case, the value of the function that gets stored into the identifier unboundMethod can be thought to be:
// doens't work
const unboundMethod = function() {
console.log(this.name);
}
where the contents of the function is the same contents of the method printName from the Fruit class. Do you see why this is an issue?
Because the this pointer is still there but the object it was intended to refer to is no longer in scope. When you try to invoke the unboundMethod, you'll get an error because it couldn't find name in this.
So what happens when you do use bind?
Loosely bind can be thought of as replacing the this value of function with the object you're passing into bind.
So if I assign: myFruit.printName.bind(myFruit) to boundMethod then you can think of the assignment like this:
// works
const boundMethod = function() {
console.log(myFruit.name);
}
where this is replaced with myFruit
The bottom-line/TL;DR
when to use bind in an Event Handler
You need to use Function.prototype.bind when you want to replace the thises inside the function with another object/pointer. If your function doesn't ever use this, then you don't need to use bind.
Why then don't we need to use bind in the first example?
If you don't need to "take the method" (i.e. taking the value of type of function), then you don't need to use bind either Another way to word that is: if you invoke the method directly from the object, you don't need bind that same object.
In the wrapper function, you're directly invoking the method of the object (as in expression 2). Because you're invoking the method without "taking the method" (we "took" the methods into variables in the Fruit example), you don't need to use bind.
printButton.addEventListener('click', function(){
// directly invoke the function
// no method "taking" here
obj.printName();
});
Hope this helps :D
Note: You need to call printButton.addEventListener('click', obj.printName() ); without parenthesis in obj.printName() since you want to pass the function.
The answer lies in the way this is bound in Javascript. In JS, the way a function is called decides how this is bound. So when you provide the callback function like below:
printButton.addEventListener('click', function(){
obj.printName();
});
Notice, printName is being called via dot notation. This is called implicit binding rule when this is bound to an object before dot, in this case obj. Clearly in this case, you get the expected output.
However, when you call it like this:
printButton.addEventListener('click', obj.printName );
Notice that, all you are passing is the address of the function that is inside obj. So in this case info about obj is lost. In other words, the code that calls back the function doesn't have the info about obj that could have been used to set this. All it has is the address of the function to call.
Hope this helps!
EDIT:
Look at this crude implementation I call bind2 that mimics native bind. This is just to illustrate how native bind function returns a new function.
Function.prototype.bind2 = function (context) {
var callBackFunction = this;//Store the function to call later
return function () { //return a new function
callBackFunction.call(context);//Later when called, apply
//context, this is `obj` passed
//in bind2()
}
};
function hello() {
alert(this.name);
}
obj = {
name:'ABC'
};
var f = hello.bind2(obj);
f();
Notice: How function f() is hard bound here. f() has hard bound this with obj. You cannot change this to other than obj now. This is another thing with bind that probably will help you knowing.

Are function-arguments not necessarily objects?

I'm learning functional programming and node.js, and I came across this odd problem when using Function.prototype.apply and .bind.
function Spy(target, method) {
var obj = {count: 0};
var original = target[method]
target[method] = function (){//no specified arguments
obj.count++
original.apply(this, arguments)//only arguments property passed
}
return obj;
}
module.exports = Spy
This code works, it successfully spies on target.method.
//same code here
target[method] = function (args){//args specified
obj.count++
original.apply(this, args)//and passed here
}
//same code here
This code, however, does not. It gives an error message: TypeError: CreateListFromArrayLike called on non-object.
And then the biggest surprise is, this method works perfectly fine.
//same code here
target[method] = function (args){
obj.count++
original.bind(this, args)
}
//same code here
So why exactly do I get this error? Is it because function arguments are not necessarily objects? Or is it because apply has a stricter description than bind?
In this version:
target[method] = function (args){//args specified
obj.count++
original.apply(this, args)//and passed here
}
Here you are not taking all the arguments but just one, named args. Since apply expects an array like object you cannot use args since it is only the first argument passed to the original target.
You can change it to:
target[method] = function (arg){ //only one argument specified
obj.count++
original.apply(this,[arg]) //one argument passed here
}
Now it works, but you can only spy on one argument functions. Using call would be better since you only have one extra argument:
target[method] = function (arg){ //only one argument specified
obj.count++
original.call(this,arg) //one argument passed here
}
Now bind is a totally different animal. It partial applies functions, thus return functions. Imagine you need to send a callback that takes no arguments but calls a function with some arguments you have when making it. You see code like:
var self = this;
return function() {
self.method(a, b);
}
Well. bind does this for you:
return this.method.bind(this, a, b);
When calling either of these returned functions the same happens. The method method is called with the arguments a and b. So calling bind on a function returns a partial applied version of that function and does not call it like call or apply does.
bind is called the same way as call is, even though they do very different things.
If you really wanted to use bind in this way. You could use the spread operator (ES2015) to expand the arguments 'array' to individual arguments:
original.bind(null, ...args);
That will bind the original function with the array values as individual arguments.

How to get all arguments of a callback function

Very often, I find myself using a callback function and I don't have its documentation handy, and it would be nice to see all of the arguments that are meant to be passed to that callback function.
// callback is a function that I don't know the args for...
// and lets say it was defined to be used like: callback(name, number, value)
something.doSomething( callback );
How can I determine what args its passing into that?
Note: looking at the source code can be unhelpful when the code itself is obfuscated and minified (as many js frameworks are)
To get the list of arguments without breaking functionality, overwrite the callback function in this way:
var original = callback;
callback = function() {
// Do something with arguments:
console.log(arguments);
return original.apply(this, arguments);
};
The context, this is preserved.
All arguments are correctly passed.
The return value is correctly passed.
NOTE: This method works in most cases. Though there are edge cases where this method will fail, including:
Read-only properties (e.g. defined using Object.defineProperty with writable:false)
Properties that are defined using getters/setters, when the getter/setter is not symmetric.
Host objects and plugin APIs: E.g. Flash and ActiveX.
Could it be as easy as
function callback() {
console.log(arguments);
}
?
Every function provides the arguments it has been called with in the automagic arguments collection.
Isn't this sort of the cart leading the horse?
Your function takes a callback. It's the method using your function that should be aware of what arguments the callback should accept.
You can even tell it which function's args to get using [functionName].arguments:
(function(arg1, arg2, agr3){
console.log('args are:', arguments);
return function fn(){
function m(){
console.log(
'fn.arguments:', fn.arguments,
'm.arguments:', m.arguments,
'argumentsX:', arguments
);
};
m('mArg1', 'mArg2', 'mArg3', 'mArg4');
};
})
(1, 2, Math.PI) // invoke closure
('fnArg1', 'fnArg2', 'fnArg3', 'fnArg4'); // invoke "fn"
Every function def rewrites the the arguments keyword to be of that scope btw (as seen with the "argumentsX" log).

context to use call and apply in Javascript?

Guys can any one explain context to use call and apply methods in Javascript?
Why to use call and apply instead of calling a function directly ?
You use call or apply when you want to pass a different this value to the function. In essence, this means that you want to execute a function as if it were a method of a particular object. The only difference between the two is that call expects parameters separated by commas, while apply expects parameters in an array.
An example from Mozilla's apply page, where constructors are chained:
function Product(name, price) {
this.name = name;
this.price = price;
if (price < 0)
throw RangeError('Cannot create product "' + name + '" with a negative price');
return this;
}
function Food(name, price) {
Product.apply(this, arguments);
this.category = 'food';
}
Food.prototype = new Product();
function Toy(name, price) {
Product.apply(this, arguments);
this.category = 'toy';
}
Toy.prototype = new Product();
var cheese = new Food('feta', 5);
var fun = new Toy('robot', 40);
What Product.apply(this, arguments) does is the following: The Product constructor is applied as a function within each of the Food and Toy constructors, and each of these object instances are being passed as this. Thus, each of Food and Toy now have this.name and this.category properties.
Only if you use call or apply you can modify the this context inside the function.
Unlike other languages - in JavaScript this does not refer to the current object - rather to the execution context and can be set by the caller.
If you call a function using the new keyword this will correctly refer to the new object (inside the constructor function)..
But in all other cases - this will refer to the global object unless set explicitly through call
You use .call() when you want to cause a function to execute with a different this value. It sets the this value as specified, sets the arguments as specified and then calls the function. The difference between .call() and just executing the function is the value of the this pointer when the function executes. When you execute the function normally, javascript decides what the this pointer will be (usually the global context window unless the function is called as a method on an object). When you use .call(), you specify exactly what you want this to be set to.
You use .apply() when the arguments you want to pass to a function are in an array. .apply() can also cause a function to execute with a specific this value. .apply() is most often used when you have an indeterminate number of arguments that are coming from some other source. It is often used too pass the arguments from one function call to another by using the special local variable arguments which contains an array of arguments that were passed to your current function.
I find the MDN references pages for .call() and .apply() helpful.
If you have experience with jQuery, you will know that most functions take use of the this object. For example, collection.each(function() { ... });
Inside this function, "this" refers to the iterator object. This is one possible usage.
I personally have used .apply() for implementing a queue of requests - I push an array of arguments into the queue, and when the time comes for executing it, I take an element, and pass it as the arguments for a handler function using .apply(), thus making the code cleaner then if having to pass an array of arguments as a first argument. That's another example.
In general, just keep in mind that those ways to call a function exist, and you may one day find them convenient to use for implementing your program.
If you have experience with Object Oriented Programming then call and apply will make sense if you compare it with inheritance and override the properties or method/functions of parent class from child class. Which is similar with call in javascript as following:
function foo () {
this.helloworld = "hello from foo"
}
foo.prototype.print = function () {
console.log(this.helloworld)
}
foo.prototype.main = function () {
this.print()
}
function bar() {
this.helloworld = 'hello from bar'
}
// declaring print function to override the previous print
bar.prototype.print = function () {
console.log(this.helloworld)
}
var iamfoo = new foo()
iamfoo.main() // prints: hello from foo
iamfoo.main.call(new bar()) // override print and prints: hello from bar
I can't think of any normal situation where setting the thisArg to something different is the purpose of using apply.
The purpose of apply is to pass an array of value to a function that wants those values as arguments.
It has been superseded in all regular everyday usage by the spread operator.
e.g.
// Finding the largest number in an array
`Math.max.apply(null, arr)` becomes `Math.max(...arr)`
// Inserting the values of one array at the start of another
Array.prototype.unshift.apply(arr1, arr2);
// which becomes
arr1 = [...arr2, ...arr1]

Calling a function in JavaScript without parentheses

Is there a way in JavaScript to call a function without parentheses?
For example in jQuery:
$('#wrap').text("asdf"); will work and so will $.ajax(ajaxOptions);
I'm mapping a function (class) to window.$ that has a set of functions I want to be able to call with or without parentheses. Like jQuery.
Here's a code example:
function Test(asdf) {
this.test = function(testVar) { return testVar + ' asdf'; }
}
And I map Test() to $:
window.$ = new Test();
I have to call the function (class) like this:
$('asfd').test('ASDF');
But I want to be able to call it like this:
$.test('asdf');
JavaScript is extremely flexible since objects are basically a map and can contain any number of key value pairs, where the value itself can be an object and thus you get nesting. Another interesting aspect of JavaScript is that functions are themselves first class objects, so in a key value pair, you can have a function as a value.
To get something like jQuery then becomes very simple. You start off with a function (constructor function, or class if you will),
function myFunction() {
// do some awesome web 2.0 stuff
}
Since myfunction is a function and also an object, so you attach key value pairs to it as well which is what jQuery does. To verify that myFunction is also an object, do an instanceof check:
myFunction instanceof Function; // true
myFunction instanceof Object; // true
So we can add properties to a function which could be simple values, or functions themselves.
// adding a simple property that holds a number
myFunction.firstPrime = 2;
// adding a function itself as a property to myFunction
myFunction.isPrime = function(number) {
// do some magic to determine if number is prime
};
But if that is not your question, and you really want to know if you can literally call a function without using parentheses, then the answer is yes, you can, using the additions in ECMAScript 5th ed.
Here's an example of calling a function without using parentheses using Object.defineProperty and defining it as a getter:
var o = {};
Object.defineProperty(o, "helloWorld", {
get: function() {
alert("hello world");
},
set: undefined
});
o.helloWorld; // will alert "hello world"
Try this example in Chrome or Safari, or Firefox nightly.
new
You can call a function using new. The twist is, within the function this becomes an object being constructed by the function instead of whatever it would normally be.
function test () { alert('it worked'); }
...
new test; // alerts "it worked"
call and apply
You can use call or apply to call a function indirectly. You'll still need parentheses (unless you use new), but you won't be directly invoking the function with parentheses.
function message() {return "Hello crazy world!"; }
Function.prototype.toString = function() { return this.call(this); };
alert(message);
The difference in those examples is that .text() is a function on jQuery objects (instances of them, wrappers of elements), it's on $.fn (jQuery's prototype shortcut) so it's specifically for jQuery objects.
$.ajaxSetup() is a method on the jQuery object/variable itself.
If you look at the API it's easy to tell which methods are where, the jQuery.something() methods are methods on the jQuery variable/object, not related to an element set, everything else that's just .something() are the methods to run against element sets (on jQuery object instances).
For what you want to do, yes it is possible, but it doesn't make much sense, for example:
$.myFuntion = $.fn.myFunction = function() { alert('hi'); };
You should place your methods where it makes sense, if it's for a set of elements, use $.fn.myFunction and this refers to the set of elements inside. If it's a static method unrelated to an element set place it outside separately or possibly at $.myFunction.
You can try something like this
var test = {
method1 : function(bar) {
// do stuff here
alert(bar);
},
method2 : function(bar) {
// do stuff here
alert(bar);
}
};
test.method1("foo");
test.method2("fooo");
You can make a function reference, but you won't be able to pass arguments:
function funky() { alert("Get down with it"); }
obj.onclick = funky;
Use template literals:
alert`WOW`
It is cleaner and shorter, without having to rely on jQuery, call, etc.

Categories

Resources