Binding an argument as this, with a new constructor? - javascript

I am trying to bind an argument for this to a function named Sequence the binding works, problem is more than one Sequence overwrites each other, so I have to use new here's the issue...
//js code
function init(cmd) {
cmd.exec.call(e,Sequence.bind(cmd));
}
Example
init({
exec:function(seq){
seq("a",function(){
console.log(this);// returns init object itself
});
}
});
Works great but when I do
//init for js above
...,function(seq) {
seq("a",function(){
console.log ("hello");
},document.getElementById("google"));
});
...,function(seq) {
seq("d",function(){
console.log("goodbye");
});
});
The second sequence is ran goodbye. never the first because it is being written over.
Sequence function
function Sequence(key, fn, location) {
if (!location) location = document;
var self = this; //object that is bound to Sequence
location.addEventListener("keydown", function sequenceMode(e) {
if(self.waiting)
{
if (keyCodes.literal[key.toUpperCase()] === e.which) {
fn.call(self,e);
self.waiting = false;
this.removeEventListener("keydown", sequenceMode);
}
} else location.removeEventListener("keydown", sequenceMode);
});
}
So my issue here is how do I A bind the this property to be the object calling Sequence or B how do I create a new instance of Sequence and still allow the user to define inside the function?
cmd.call(e,new Sequence().bind(cmd)); //can not call bind from Constructor
So basically I need to have the user still be able to define the arguments themselves for Sequence and this be bound to the object calling it. Any suggestions?
EDIT
http://jsbin.com/dulesejame
Not getting the same results so I'm overlooking my code now,
So I've edited the bin with my actual JavaScript. It's doing it now.
Open the developer panel to read console. Press ctrl+a then b, then press ctrl+b press a, doesn't show any so press b and it's running ctrl+a seq function.

One main issue I can tell you is using the same default object, by which your commands.cmd[combinator] will point to the same object which came last.
Make a copy of default before assigning
var def = Object.create(defaults);
for(var option in options)
{
if(option !== "executed" && option !== "called")
{
def[option] = options[option];
}
}

After realizing what I did wrong thank's to code-jaff which I'll accept his answer since he did give me the way to it. Except instead of using Object.create I just created a new Object literal.
var def = {};
for(var option in defaults)
{
if(options.hasOwnProperty(option) && option !== "executed" && option !== "called" && option !== "waiting")
{
def[option] = options[option];
} else {
def[option] = defaults[option];
}
}

Related

Compare functions in Javascript

I have an API that takes a function as an input, and then inside the API, the intent is to add the function to an Array if the function is not already added to the Array.
The call to the API is of the form:
myApiHandle.addIfUnique(function(){
myResource.get(myObj);
});
The API is:
myApiHandle.addIfUnique(myFunc) {
if (myArray.indexOf(myFunc) === -1) {
return;
}
// add to array
}
Now this obviously does not work as expected, since each time a new function is being passed in.
My Question is: Is there a way to pass in a function into the myApiHandle.addIfUnique call that will allow me to compare the existing functions in the array with this function that is currently passed in? The comparison should compare the function name and the object, and if both are the same, then not add the function to the array. I want to avoid adding another argument to the addIfUnique call if at all possible.
In other words, is the below possible:
myApiCall.addIfUnique (someFunc) {
}
If so, what is the someFunc. And what would be the logic inside the API to detect if the function already exists in myArray?
The same problem occurs with addEventListener and removeEventListener, where the callback must be identical (in the === sense) for removeEventListener to remove it.
As you've found, obviously if you call addIfUnique like this:
addIfUnique(function() { })
the function passed each time will be a unique object. The solution is to create the function once:
var fn = function() { };
addIfUnique(fn);
addIfUnique(fn);
A related problem occurs when the function being passed in is a method invocation, so I need to bind it:
var x = { val: 42, method: function() { console.log(this.val); } };
I want to pass a bound version of it, so
addIfUnique(x.method.bind(x));
addIfUnique(x.method.bind(x));
But again, each call to x.method.bind(x) will return a separate function. So I need to pre-bind:
var boundMethod = x.method.bind(x);
addIfUnique(boundMethod);
addIfUnique(boundMethod);
First of all, comparing functions is meaningless, even if two functions are literally different, they may be functionally the same.
And for your problem, you can compare whether it's exactly the same object, or you can compare it literally by using toString() function and regExp.
var addIfUnique = (function() {
var arr = [];
return function(func) {
if (~arr.indexOf(func)) return false;
var nameArr = [];
var funcName = func.name;
var funcRegExp = new RegExp('[^\{]+\{(.+)\}$', 'i');
var funcStr = func.toString().match(funcRegExp);
funcStr = funcStr && funcStr[1];
if (!funcStr) return false;
var strArr = arr.map(function(v){
nameArr.push(v.name);
return v.toString().match(funcRegExp)[1];
});
if (~strArr.indexOf(funcStr) && ~nameArr.indexOf(funcName)) return false;
arr.push(func);
};
}());

Accessing a JavaScript / jQuery Instance only once

I have a jQuery function which is called on several events (button click, change etc.)
This function is called in the documentReadyFunction and is feeded with start values..
everytime I call this function, parameters will be passed to the function.
My problem is: I don't want to create a new Object each time I call the function, because if I set a variable which decides if a part of the function is beeing executed or not, will be always overwritten..
What do I have to do, to access the first created instance instead of creating always a new one with every function call..
Down below is a simplyfied version of my function.. Maybe you understand my problem better then.
$.fn.doSomething = function(param1) {
var localParam = param1;
var amIcalledMoreThanOnce = parseInt(0, 10);
if (param1 == 1) {
amIcalledMoreThanOnce = amIcalledMoreThanOnce + 1;
if (amIcalledMoreThanOnce == 1) {
$('#run').val(amIcalledMoreThanOnce);
// fill form fields with URL parameters
// This shall be executed only once after getting the URL vals
} else {
// set the localParam to 0 to exit this loop and reach the outter else..
localParam = 0;
$.fn.doSomething(localParam);
}
} else {
$('#run').val(amIcalledMoreThanOnce);
// use the User Input Data not the URL Params
}
};
$.fn.doSomething(1);
$.fn.doSomething(1);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<input type="number" id="run">
you can use this pattern:
var nameOfYourFunction = (function() {
var initializedOnlyOnce = {};
return function() {
//use initializedOnlyOnce here.
}
})();
here what you see is; you create and run a function immediately when the code is run. The outer function immediately returns the inner function and it's assigned to nameOfYourFunction. Then you can use the nameOfYourFunction(); just as any other function. However any varible declared in the outer function will be available to the nameOfYourFunction() and initializedOnlyOnce will never be initialized again.

Checking if parameter is a contructor or an instance in javascript

I wanna do the following, if the parameter passed is a contructor, then do new 'constructor' if not, just use the instance. How can I do that?
This is what I've done so far, but it doesn't work. I think something is wrong with my code:
JS
var showList = function (view, options) {
// checking if view is a conctructor or not
if (view instanceof view) {
app.getRegion('main').show(view(options));
} else {
app.getRegion('main').show(new view(options));
}
}
so the above function can be used as:
var listView = new ListView;
showList(listView);
or straight:
showList(new ListView);
I think you're going to want to test whether the argument is an object or a function:
if (typeof view === "function")
will tell you it's a function (a constructor function in your context)
if (typeof view === "object")
will tell you that it's an already constructed object.
var showScreen = function (view, options) {
// check if view is already an object
if (typeof view === "object") {
app.getRegion('main').show(view(options));
} else {
app.getRegion('main').show(new view(options));
}
}
One thing I'm confused about in your code is if view is already an object, then why do you do view(options). That doesn't make sense to me. Doing new view(options) when view is a function makes sense, but not the other option so I think something also needs to be corrected with that line of code. Do you perhaps mean to call a method on that object?
FYI, I tend to avoid using instanceof as a general practice if there is another option because instanceof can have issues with cross frame code whereas typeof does not have those issues.
var showScreen = function (view, options) {
// checking if view is a conctructor or not
if (view instanceof Function) {
app.getRegion('main').show(new view(options));
} else {
app.getRegion('main').show(view(options));
}
}
Maybe not the best way but well.
function A(){}
var a = new A();
a instanceof A // true
a instanceof Function // false
A instanceof Function // true
This seems like a code smell to me. I think it's better pass an instance instead of a constructor function.
In this case you can do:
showScreen(new ListView(options))
If it's hard to construct a ListView you should wonder why that is.

Passing parameters to a jQuery closure not working on Multisuggest plugin

I have a question of which someone might find this much simpler than I do, but alas, I don't have much experience with custom jQuery plugins.
The previous developer at my place of work left me with a lot of left-over plugins that don't seem to work very well, most which I've been able to fix but this which has been bugging me for a while.
It is a custom Multiple Suggestion plugin (called multisuggest) written in jQuery, and it has a set of functions that it uses internally (*e.g. setValue to set the value of the box, or lookup to update the search)*
It seems he's tried to call these plugin functions from an external script (this exteranl script specifically imports newly created suggestions into the multisuggest via user input and sets the value) like this:
this.$input.multisuggest('setValue', data.address.id, address);
This seems to call the function as it should, except the second and third parameters don't seem to be passed to the function (setValue receives nothing), and I don't understand how I can get it to pass these. It says it is undefined when I log it in the console. The functions are set out like this (I've only including the one I'm using and an internal function from multisuggest called select that actually works):
MultiSuggest.prototype = $.extend(MultiSuggest, _superproto, {
constructor : MultiSuggest,
select: function () { // When selecting an address from the suggestions
var active, display, val;
active = this.$menu.find('.active');
display = active.attr('data-display');
val = active.attr('data-value');
this.setValue(display, val, false); // This works, however when I do it as shown in the above example from an external script, it doesn't. This is because it doesn't receive the arguments.
},
setValue : function(display, value, newAddress) { // Setting the textbox value
console.log(display); // This returns undefined
console.log(value); // This returns undefined
if (display && display !== "" &&
value && value !== "") {
this.$element.val(this.updater(display)).change();
this.$hiddenInput.val(value);
this.$element.addClass("msuggest-selected");
}
if(newAddress === false){
return this.hide();
}
},
});
Why does it listen to the function, but not the values passed to it? Do I need to include an extra line of code somewhere to define these arguments?
Anyone with jQuery experience would be of great help! This is bottlenecking progress on a current project. Thanks for your time!
EDIT:
I've missed out the code of how the arguments are trying to be passed from the external script to the internal function of the plugin. Here is the plugin definition with how the external call is handled, can anyone see a problem with this?
$.fn.multisuggest = function(option) {
return this.each(function() {
var $this = $(this), data = $this.data('multisuggest'), options = typeof option === 'object' && option;
if (!data) {
$this.data('multisuggest', ( data = new MultiSuggest(this, options)));
} else if (typeof(option) === 'string') {
var method = data[option];
var parameters = Array.prototype.slice.call(arguments, 1);
method.apply(this, parameters);
}
});
};
The "usual" plugin supervisor looks like this :
// *****************************
// ***** Start: Supervisor *****
$.fn.multisuggest = 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 );
} else {
$.error( 'Method ' + method + ' does not exist in jQuery.' + pluginName );
}
};
// ***** Fin: Supervisor *****
// ***************************
All the looping through this should be inside the method functions, not in the supervisor.
I'm a little worried that new MultiSuggest(...) appears in the current supervisor. That sort of thing is totally unconventional. The original author clearly had something in mind.
You need to extend the jQuery plugin function which is attached to $.fn['multisuggest'], that function is probably only taking and passing one parameter.

Define a function that will be implemented by the user

I have the following example code
var object = {
userDefinedFunction : function(){
//no implementation, this will be defined by the user
}
}
What i want to achieve is the user giving his own implementation of it:
object.userDefinedFunction = function(){
alert("just testing");
}
I tested this and works as i expected, what i want to know is:
is this the javascript way of solving this kind of problem?
let's say that it's mandatory that userDefinedFunction is implemented, how do i make sure of this? I could rely on something like the following, checking for implemented, but i'm learning javascript so i want to know how to leverage the language:
userDefinedFunction : function(){
implemented = false;
}
Thank you.
I don't know if this is the way to go, but if your object has to be initialized somehow by the user, you can test in this function, whether userDefinedFunction is defined and throw an exception if not.
One idea that feels to be a cleaner implementation, is to let the user provide some kind of configuration object that defines the functions, something like:
yourObject.initialize({
userDefinedFunction: function() {}
});
You could throw an error in the default implementation:
var object = {
userDefinedFunction : function(){
throw "userDefinedFunction must be implemented";
}
}
or show an alert box, depending on your application.
var object = {
userDefinedFunction : undefined,
anotoherDefinedFunc : undefined,
/* ... */
hasUserImplementedInterfaces : function() {
if (typeof object.userDefinedFunction !== 'function') return false;
if (typeof object.anotoherDefinedFunc !== 'function') return false;
/* ... */
return true;
}
};
console.log(object.hasUserImplementedInterfaces());
hasUserImplementedInterfaces() function checks for user function implementations so you can execute as first check using that object.

Categories

Resources