Is there any reason why something like this would not work?
var classReplace = function(object, newClass, originalClass = "") {
//do stuff
}
I keep getting a "Uncaught SyntaxError: Unexpected token = " error because I add in the
originalClass = ""
part
You can check to see if originalClass was defined and if not then assign it "",
var classReplace = function(object, newClass, originalClass) {
if( typeof(originalClass) === "undefined" ) originalClass = "";
//do stuff
}
As Pointy said, setting a default argument value in this way isn't possible in JavaScript,
You can, however, achieve similar results by checking if said argument is undefined, and if so, setting it equal to your desired default value:
var classReplace = function(object, newClass, originalClass) {
if (originalClass === undefined) originalClass = "";
//do stuff
}
While this doesn't exist in JS out of the box, I can think of two options in addition to the other answers, that actually mimick the feature you are looking for.
a. CoffeeScript
Among other cool features, CS supports default function values:
var classReplace = (object, newClass, originalClass = "") ->
console.log(originalClass)
Of course this just gets interpreted as:
var classReplace = function(object, newClass, originalClass) {
if (originalClass == null) {
originalClass = "";
}
return console.log(originalClass);
};
But still nice to have for readability, and like I said, CS has a lot of other cool features, may be worth a look.
b. Lo-Dash's partialRight method. (link)
usage:
var originalFunction = function(a,b) { return a + b; },
functionWithDefaultValues = _.partialRight(originalFunction, 1, 2);
jsfiddle
notes:
_.partial, for some reason, doesn't have the same behaviour.
_.partialRight appends the values from the right, so in the above, the default value for b is 1.
Hope this helps.
Related
In javascript using an object parameter is my preferred way of working with functions. To check that a function has the required parameters I either (Solution 1) loop through all the object parameters properties and throw an error or (Solution 2) wait until a required property is needed and throw an error. Solution two seems efficient but I have to throws in multiple places in the function. Solution 1 seems pragmatic but should probably be a reusable piece of code. Is there another solution I should be looking at?
You can actually do this
var propsNeeded = ["prop1", "prop2", "blah", "blah", "blah"],
obj = {
prop1: "Hi"
}
function hasRequiredProperties(props, obj){
return Object.keys(obj).sort().join() == propsNeeded.sort().join();
}
console.log(hasRequiredProperties(propsNeeded, obj)); // false
You can check for single properties like
function hasProperty(propName, obj){
return obj.hasOwnProperty(propName);
}
For consistency I would create require method and use it always when some property is required.
var require = function (key, object) {
if (typeof object[key] === 'undefined') {
throw new Error('Required property ' + key + ' is undefined');
}
};
I would test if required property exists as soon as I'm certain that property is needed. Like this:
var example = function (args) {
require('alwaysRequired', args);
// some code here which uses property alwaysRequired
if (args.something) {
require('sometimesRequired', args);
// some code here which uses property sometimesRequired
}
};
Using #Amit's answer I'd probably add a method to Object itself:
Object.prototype.hasAllProperties = function(props, fire){
var result = Object.keys(this).sort().join() == propsNeeded.sort().join();
if (fire && !result){
throw new Error('Object does not define all properties');
}
return result;
}
and in your function:
function someFunction(myObject){
var objComplete = myObject.hasAllProperties(["prop1", "prop2", "prop3"], false);
}
Update:
After noticing the problem with #Amit's original answer, here's what I suggest:
Object.prototype.hasAllProperties = function(props, fire){
var result = true;
$(props).each(function(i, e){
if (!this.hasOwnProperty(e) ) {
result = false;
return false;
}
});
if (fire && !result){
throw new Error('Object does not define all properties');
}
return result;
}
This is just a general case of checking for presence of keys on a object, which can be done easily enough with
requiredParams.every(function(prop) { return prop in paramObj; })
It almost reads like natural language. "Taking the required parameters, is EVERY one of them IN the parameter object?".
Just wrap this in function checkParams(paramObj, requiredParams) for easy re-use.
More generally, this is the problem of asking if one list (in this case the list of required parameters) is included in another list (the keys on the params object). So we can write a general routine for list inclusion:
function listIncluded(list1, list2) {
return list1.every(function(e) { return list2.indexOf(e) !== -1; });
}
Then our parameter-checking becomes
function checkParams(paramObj, requiredParams) {
return listIncluded(requiredParams, Object.keys(paramObj));
}
If you want to know if object has at least some properties you can use this function without third parameter:
function hasRequiredProperties(propsNeeded, obj, strict) {
if (strict) return Object.keys(obj).sort().join() == propsNeeded.sort().join();
for (var i in propsNeeded ) {
if (!obj.hasOwnProperty(propsNeeded[i])) return false;
}
return true;
};
Example:
options = {url: {
protocol: 'https:',
hostname: 'encrypted.google.com',
port: '80'
}
};
propsNeeded = ['protocol', 'hostname'];
hasRequiredProperties(propsNeeded, options.url); // true
hasRequiredProperties(propsNeeded, options.url, true); // false
Maybe some of you know about AOP, in some languages using AOP can lead you to be able to inject code after, before, or while a method is executing,etc.
What I want is to apply the same in Javascript, I am currently working on a massive app which has more than 300 ajax calls, and every time I need to do some change on the catch statement on them, I have to modify them one by one which is very tedious.
What I want to do is something like :
functionName.before("try {")
functionName.after("} catch(ex){
//dostuff
}")
Is it possible? I know there are things like .call, or the arguments object inside every function..which seem pretty meta-function (AOP) functionalities.
Not with before and after, but a wrap will work:
Function.prototype.wrapTry = function(handle) {
var fn = this;
return function() {
try {
return fn.apply(this, arguments);
} catch(e) {
return handle(e);
}
};
};
Then use it like
var safeFunction = functionName.wrapTry(doStuff);
In JavaScript, functions are first-class objects. That means you can manipulate or redeclare them.
Assuming that there is a "foo" function:
var originalFoo = foo;
foo = function()
{
// "before" code.
// Call the original function.
originalFoo.apply(this, arguments);
// "after" code.
};
After that, any call to foo() will call the new function: even with parameters.
Old question but you may take a look over this https://github.com/k1r0s/kaop-ts/blob/master/docs/api.md#available-join-points
import { onException } from "kaop-ts"
import handlingException from "./somewhere"
class Something {
#onException(handlingException)
method() {
// stuff that may throw an error
}
}
I also will give a late answer in order to shed some light onto this special case that every then and now pops up as JavaScript and AOP.
Firstly, cases like the very one presented by the OP always ask for modifying already existing functionality, thus targeting closed code that sometimes is not even owned by the party that sees itself challenged from modifying the control flow of such code.
Why then, not just name it like that ... JavaScript method modification or JavaScript method modifiers.
Secondly, because of already riding the horse of terminology, altering closed functionality in JavaScript has nothing to do with Aspect-oriented Programming unless an implementation that claims to be AO provides abstraction and code-reuse levels for at least Aspect, Advice and Pointcut.
Last, for what the OP is going to achieve and what also has been the accepted answer, there does exist a a whole bunch of before, after around / wrap solutions, almost always unfortunately mentioning AO(P), and in far too many cases not taking care of the context or target which is essential to method modification.
The example I do provide uses a prototypal implementation of afterThrowing. Because JavaScript already features a standardized bind, I'm firmly convinced that Function.prototype is the right place as well for some other method-modifiers
like before, after, around, afterThrowing
and afterFinally.
// OP's example pseudo code
//
// functionName.before("try {")
//
// functionName.after("} catch(ex){
// dostuff
// }")
function doStuffAfterThrowing(exception, originalArguments) {
"use strict";
var context = this;
console.log('context : ', context);
console.log('String(exception) : ', String(exception));
console.log('originalArguments : ', originalArguments);
return "safely handled exception";
}
function doFail() {
throw (new ReferenceError);
}
function oneOutOfManyAjaxCallbacks(payload) {
doFail();
}
var jsonData = {
"foo": "foo",
"bar": "bar"
};
var someModifiedAjaxCallback = oneOutOfManyAjaxCallbacks.afterThrowing(doStuffAfterThrowing, { x: 'y' });
// does fail controlled/handled.
console.log('someModifiedAjaxCallback(jsonData) : ', someModifiedAjaxCallback(jsonData));
// does fail "Uncaught".
console.log('oneOutOfManyAjaxCallbacks(jsonData) : ', oneOutOfManyAjaxCallbacks(jsonData));
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
(function (Function) {
var
isFunction = function (type) {
return (
(typeof type == "function")
&& (typeof type.call == "function")
&& (typeof type.apply == "function")
);
},
getSanitizedTarget = function (target) {
return ((target != null) && target) || null;
}
;
Function.prototype.afterThrowing = function (handler, target) { // afterThrowing
target = getSanitizedTarget(target);
var proceed = this ;
return (isFunction(handler) && isFunction(proceed) && function () {
var ret, args = arguments;
try {
ret = proceed.apply(target, args);
} catch (exc) {
ret = handler.call(target, exc, args);
//throw exc;
}
return ret;
}) || proceed;
};
}(Function));
</script>
Having come that far one might also consider reading ...
sandwich pattern in javascript code
Can you alter a Javascript function after declaring it?
This is my code (it's a bookmarklet)
javascript:(function(){
a=document.createElement('script');
a.setAttribute('src','//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js');
document.body.appendChild(a);
data='[["#txtapplicantlname","agrawal","text"],["#txtapplicantfname","aayush","text"],["#txtfather","Ranjan","text"],["#txtmother","Neelam","text"],["#txtPincode","452010","text"],["#txtPhone","2147483647","text"],["#txtEmail","aayush#mail.com","text"]]';
for(a=$.parseJSON(data),b=a.length-1;0<=b;b--){
c=a[b];
if (c[2] == 'text') {
console.log(c);
$(c[0]).val(c[1]);
}
}
})();
It used to work fine until I inserted the if statement, then it broke. The console doesn't give me any errors and I have googled a lot for javascript string comparison errors and found nothing useful.
I tried to use equals and compareTo and ended up with console errors and nothing working.
Uncaught TypeError: Cannot call method 'equals' of undefined fillform.php:1
Uncaught TypeError: Cannot call method 'compareTo' of undefined
Help is highly appreciated.
Note: The variables are named like that for a reason, it being that it was initially compiled with Google closure compiler and the if statement is being edited in.
There are several things wrong with this code; string comparison is not one of them.
1) You aren't waiting for the asynchronously loaded script to complete. This code should pretty much always fail because $.parseJSON() isn't available. In fact, once I fixed that problem, this code works fine for me:
(function(){
a=document.createElement('script');
a.setAttribute('src','//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js');
var afterJqueryLoad = function() {
data='[["#txtapplicantlname","agrawal","text"],["#txtapplicantfname","aayush","text"],["#txtfather","Ranjan","text"],["#txtmother","Neelam","text"],["#txtPincode","452010","text"],["#txtPhone","2147483647","text"],["#txtEmail","aayush#mail.com","text"]]';
for(a=$.parseJSON(data),b=a.length-1;0<=b;b--){
c=a[b];
if (c[2] == 'text') {
console.log(c);
$(c[0]).val(c[1]);
}
}
};
var jqueryReady = false;
a.onreadystatechange= function () {
if((this.readyState == 'complete' || this.readyState == 'loaded') && !jqueryReady) {
jqueryReady = true;
afterJqueryLoad();
}
};
a.onload = function() {
if(!jqueryReady) {
jqueryReady = true;
afterJqueryLoad();
}
};
document.body.appendChild(a);
})();
2) Use better var names (a, b, and c are not good var names).
3) Use var to scope vars correctly. Right now your code is shadowing globals and stomping on vars even within the same scope; the a var, for example, would stomp on your script elem var. (You should still change the var names per (2) but using var is not optional; you must always do this to scope vars correctly.)
4) Use spaces to help readability; your for line is unnecessarily difficult to read having no spaces.
All together now:
(function(){
var jqueryScriptElem = document.createElement('script');
jqueryScriptElem.setAttribute('src', '//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js');
var afterJqueryLoad = function() {
var data = '[["#txtapplicantlname","agrawal","text"],["#txtapplicantfname","aayush","text"],["#txtfather","Ranjan","text"],["#txtmother","Neelam","text"],["#txtPincode","452010","text"],["#txtPhone","2147483647","text"],["#txtEmail","aayush#mail.com","text"]]',
dataParsed = $.parseJSON(data);
for(var dataItemIndex = dataParsed.length - 1; 0 <= dataItemIndex; dataItemIndex--) {
var dataItem = dataParsed[dataItemIndex];
if (dataItem[2] == 'text') {
console.log(dataItem);
$(dataItem[0]).val(dataItem[1]);
}
}
};
var jqueryReady = false;
jqueryScriptElem.onreadystatechange = function () {
if((this.readyState == 'complete' || this.readyState == 'loaded') && !jqueryReady) {
jqueryReady = true;
afterJqueryLoad();
}
};
jqueryScriptElem.onload = function() {
if(!jqueryReady) {
jqueryReady = true;
afterJqueryLoad();
}
};
document.body.appendChild(jqueryScriptElem);
})();
I'm having an issue with prototype inheritance and I can't find out why it's not working properly.
The snippet is this:
function Field(newKey, val) {
this.key = newKey || "";
this.value = val || "";
}
Field.prototype.isEmpty = function() {
return this.value === undefined || this.value === null || this.value === "";
};
function DoubleField(newKey, val) {
this.base = Field;
this.base(newKey, val);
}
DoubleField.prototype = new Field();
DoubleField.prototype.constructor = DoubleField;
function IntegerField(newKey, val) {
this.base = DoubleField;
this.base(newKey, val);
}
IntegerField.prototype = new DoubleField();
IntegerField.prototype.constructor = IntegerField;
var f = new Field('keyfield', 'valField');
var d = new DoubleField('keydouble', 'valDouble');
var i = new IntegerField('keyinteger');
var res = f.isEmtpy();
The call to f.isEmpty is failing? Why? Calls to d.isEmpty or i.isEmpty work just fine as expected.
I cannot realize what I'm doing wrong. Any help would be much appreciated!
The error is in the last line of code:
var res = f.isEmtpy();//That's what you wrote
The correct is:
var res = f.isEmpty();
You haven't said how it's failing. As I noted in a comment, at the very least there's a typo: var res = f.isEmtpy(); should be var res = f.isEmpty();
But beyond that (which I assume is just a typo in the question, not the code), note that this line in your derived constructors:
this.base = DoubleField;
...will not work as you intend. Consider the case of IntegerField. You call new IntegerField and it sets this.base to DoubleField and calls it. But the DoubleField constructor then sets this.base to Field. So you have an IntegerField instance with a base property pointing to Field, not DoubleField.
You can't use instance properties to trace lineage. It works for a parent/child situation, but you run into the "grandchild" problem described above.
I recommend using one of the inheritance helper scripts out there. There are several, including my own Lineage. These scripts help you build inheritance hierarchies, handling the plumbing for you so you can focus on building whatever it is you're trying to build. We only need them temporarily; JavaScript is getting syntactic sugar to help with this in the next version (but of course, it'll be years before we see widespread support of it, once it's actually finalized).
In trying to make my Javascript unobtrusive, I'm using onLoads to add functionality to <input>s and such. With Dojo, this looks something like:
var coolInput = dojo.byId('cool_input');
if(coolInput) {
dojo.addOnLoad(function() {
coolInput.onkeyup = function() { ... };
});
}
Or, approximately equivalently:
dojo.addOnLoad(function() {
dojo.forEach(dojo.query('#cool_input'), function(elt) {
elt.onkeyup = function() { ... };
});
});
Has anyone written an implementation of Ruby's andand so that I could do the following?
dojo.addOnLoad(function() {
// the input's onkeyup is set iff the input exists
dojo.byId('cool_input').andand().onkeyup = function() { ... };
});
or
dojo.byId('cool_input').andand(function(elt) {
// this function gets called with elt = the input iff it exists
dojo.addOnLoad(function() {
elt.onkeyup = function() { ... };
});
});
I don't know Dojo, but shouldn't your first example read
dojo.addOnLoad(function() {
var coolInput = dojo.byId('cool_input');
if(coolInput)
coolInput.onkeyup = function() { ... };
});
Otherwise, you might end up trying to access the element before the DOM has been built.
Back to your question: In JavaScript, I'd implement andand() as
function andand(obj, func, args) {
return obj && func.apply(obj, args || []);
}
Your example could then be written as
dojo.addOnLoad(function() {
andand(dojo.byId('cool_input'), function() {
this.onkeyup = function() { ... };
});
});
which isn't really that much shorter than using the explicit if statement - so why bother?
The exact syntax you want is not possible in JavaScript. The way JavaScript executes would need to change in a pretty fundamental fashion. For example:
var name = getUserById(id).andand().name;
// ^
// |-------------------------------
// if getUserById returns null, execution MUST stop here |
// otherwise, you'll get a "null is not an object" exception
However, JavaScript doesn't work that way. It simply doesn't.
The following line performs almost exactly what you want.
var name = (var user = getUserById(id)) ? user.name : null;
But readability won't scale to larger examples. For example:
// this is what you want to see
var initial = getUserById(id).andand().name.andand()[0];
// this is the best that JavaScript can do
var initial = (var name = (var user = getUserById(id)) ? user.name : null) ? name[0] : null;
And there is the side-effect of those unnecessary variables. I use those variables to avoid the double lookup. The variables are mucking up the context, and if that's a huge deal, you can use anonymous functions:
var name = (function() {return (var user = getUserById(id)) ? user.name : null;})();
Now, the user variable is cleaned-up properly, and everybody's happy. But wow! what a lot of typing! :)
You want dojo.behavior.
dojo.behavior.add({
'#cool_input': {
onKeyUp: function(evt) { ... }
}
});
How about something like this:
function andand(elt, f) {
if (elt)
return f(elt);
return null;
}
Call like this:
andand(dojo.byId('cool_input'), function(elt) {
// this function gets called with elt = the input iff it exists
dojo.addOnLoad(function() {
elt.onkeyup = function() { ... };
});
});
As far as I know there isn't a built-in JavaScript function that has that same functionality. I think the best solution though is to query by class instead of id and use dojo.forEach(...) as you will be guaranteed a non-null element in the forEach closure.
You could always use the JavaScript equivalent:
dojo.byId('cool_input') && dojo.byId('cool_input').whateverYouWantToDo(...);
I've never used dojo, but most javascript frameworks (when dealing with the DOM) return the calling element when a method is called from the element object (poor wording, sorry). So andand() would be implicit.
dojo.addOnLoad(function() {
dojo.byId('cool_input').onkeyup(function(evt) { /*event handler code*/
});
});
For a list:
Array.prototype.andand = function(property, fn) {
if (this.filter(property).length > 0) this.map(fn);
}