why would I return a named function (object) from a javascript function? - javascript

Maybe I'm missing something obvious ... I've just started working with JavaScript, and when looking through an article about unit testing JavaScript with Jasmine, I came across this code:
function Convert(number, fromUnit) {
var conversions = {
distance : {
meters : 1,
cm : 0.01,
feet : 0.3048,
inches : 0.0254,
yards : 0.9144
},
volume : {
litres : 1,
gallons: 3.785411784,
cups : 0.236588236
}
},
betweenUnit = false,
type, unit;
for (type in conversions) {
if (conversions[type]) {
if ( (unit = conversions[type][fromUnit]) ) {
betweenUnit = number * unit * 1000;
}
}
}
return {
to : function (toUnit) {
if (betweenUnit) {
for (type in conversions) {
if (conversions.hasOwnProperty(type)) {
if ( (unit = conversions[type][toUnit]) ) {
return fix(betweenUnit / (unit * 1000));
}
}
}
throw new Error("unrecognized to-unit");
} else {
throw new Error("unrecognized from-unit");
}
function fix (num) {
return parseFloat( num.toFixed(2) );
}
}
};
}
It puzzled me as to why/how it is used, and what's the reason for it. It appears to return an object, which is a labeled function (method really, according to JavaScript naming convention), which wouldn't be called or returned upon creation.
After pondering about this and running it in chrome dev tools, it hit me that being called Convert with a capital C, it might be used as a constructor that would be used with new (again, according to JavaScript naming convention) so I might create an object like:
var tenFeet = new Convert(10, 'feet'); and then use it as tenFeet.to('cm');.
This still makes no sense, since I wouldn't call the object (read: class) Convert, since it's not converting. I'd call the to method convertTo, and probably name Convert to Measurement or something.
Is this simply bad code with bad naming, am I simply rooted too deeply in conventional OO and "formal" languages, or am I missing something basic?
When / where / why would I use something like the above: "return labeled method" from an object in JavaScript?
Couldn't the same be achieved by enhancing the prototype of Convert with the same method?
Cheers, and thanks in advance.

This is following the "read like a sentence" paradigm that some people like:
Convert(10, 'meters').to('feet') === 32.81
// Convert 10 meters to feet
You're right, the function goes against common naming conventions, but you can sort of guess that it shouldn't be created with the new keyword because there are no references to this in the function body.
This problem could've been avoided with proper documentation.

Blender answered this correctly, but just in case other people stumble upon page, here's a little more info on what's happening.
Coming from more "formal" languages, I guess I was having issues with the "label" appearance of the to in the return statement.
from MDN on Label:
Summary
Provides a statement with an identifier that you can refer to using a break or continue statement.
For example, you can use a label to identify a loop, and then use the break or continue statements to indicate whether a program should interrupt the loop or continue its execution.
Syntax
label : statement
Do notice that if you're creating an object, the syntax is similar. For example:
person={firstname:"John",lastname:"Doe",age:50,eyecolor:"blue"};
This will result in an object looking like:
object {firstname: "John", lastname: "Doe", age: 50, eyecolor: "blue"}
Another note is that if you're creating an array, you'll just use the commas, like this:
person=["John","Doe",50,"blue"];
This will give an array looking like:
["John", "Doe", 50, "blue"]
It takes a bit of time to get used to JavaScript syntax and logic, but all that really happens in my example is that the function returns an object, that has a method (named to) defined on it.
Once you have that object, you can call the method on it using the usual dot notation, which results in the chaining that is used in the above case. Or reusing Blender's example:
Convert(10, 'meters').to('feet') === 32.81`

Related

Benefit of using 'set' in a property of a object?

I'm working on a Codecademy course and have written the following code that works:
let person = {
_name: 'Lu Xun',
_age: 137,
set age (newAge) {
if (typeof newAge === 'number'){
this._age= newAge;
} else {
console.log('Invalid input');
}
}
}
person.age= 22;
console.log(person['_age']);
I was getting my head around how the set works and thought that it essentially worked the same as defining a method within the person object just with different syntax.
So I tried it out, called the age method passing 22 to it from outside the object using the normal way you would call a method. It worked the same. See below.
let person = {
_name: 'Lu Xun',
_age: 137,
age: function (newAge) {
if (typeof newAge === 'number'){
this._age= newAge;
} else {
console.log('Invalid input');
}
}
}
person.age(22);
console.log(person['_age']);
What are the benefits of using the 'set' syntax? ie why use it when a normal function/method definition works in the same way?
Imagine you wrote a very huge code and a lot of them access a variable like this:
// The object
const person = { age: 12 }
// You access it very often
console.log(person.age);
Now you certainly want to calculate the age based on the birthdate. With a method, you would have to replace person.age with person.getAge() everywere you use it. That can take time. With getters / setters you can just replace one single line and everything keeps working.
If you don't think that this is a problem, you might wanna look at this java thread. Java doesnt have getters / setters.
The get/set syntax is for a property whereas using a method is just that, a method. Most of the time a simple public variable will suffice however there are times, like in your example that you want to validate the input to ensure that it meets whatever expectations you have of the value.
Also consider how your code will be called and what that will look like. If you were to use a function to change the value and a function to get the value your code will look something like this:
var myObject = new CoolObject('some parameter');
myObject.setComment('this is a super cool comment');
console.log(myObject.getComment());
Where as if you implemented the get/set functions on your property it will look like this
var myObject = new CoolObject('some parameter');
myObject.comment = 'This is a super cool comment';
console.log(myObject.comment);
At the end of the day it really doesn't matter as both code snippets will do the exact same thing however one reads easier and wont get you yelled at by your co-workers. Keep in mind the second example doesn't change if you simply expose a public variable.

Conditional grammar rule in PEGjs

I'm trying to implement a simple DSL that parses basic arithmetic expressions. This needs to be done in the browser, so I'm using PEGjs to generate the parser.
Terms in the expression can be numbers (integers or real), variables (variables are properties on a context object passed to the parser), conditionals or properties accessed via dot notation.
I want the conditionals to look like this condition?value, where if condition is true, the term equates to value. The variables on either side of the ? could also be dot notation accessed properties of an object like this object.property1?object.property2.
So if the parser is passed an object like this:
context = {
depth: 100,
material: {
thickness: 20
include: true
}
edge: {
face: 4.5
}
}
The expression:
500 + depth + material.include?edge.face + material.thickness should equate to 624.5.
I've been using the PEGjs online editor. I've tried lots of different approaches, but I can't seem to nail the conditional. Everything else works. Here are the relevant rules:
Variable "variable"
= variable:identifier accessor:("." identifier)* {
var result = context[variable], i
for (i = 0; i < accessor.length; i++) {
result = result[accessor[i][1]]
}
return result
}
identifier
= identifier:$([0-9a-zA-Z_\$]+)
Conditional
= condition:Variable "?" value:Variable {
return condition ? value : 0
}
I've looked at the example grammar for javascript in the PEGjs github repo, and the conditional rule looks a lot like what I've got here, but I still can't get it to work.
What would be the correct way to implement a conditional statement like the one I've described in a PEGjs rule?
I know that this is a bit late, but the issue is that your variable is a string evaluating to "material.include".
Look at this code:
var result = context[variable], i
You are trying to access a property named "material.include" from your context object, which would look like this:
{
"material.include": true
}
Rather than trying to access the object referenced by the "material" property, and then the "include" property off the resulting object, which would look like this:
{
"material": {
"include": true
}
}
The solution would be to split the variable string by "." characters and then recursively find your property:
Variable "variable"
= variable:identifier accessor:("." identifier)* {
var path = variable.split(".");
var result = path.reduce( (nextObject, propName) => nextObject[propName], context );
for (var i = 0; i < accessor.length; i++) {
result = result[accessor[i][1]]
}
return result
}
Note that this solution is not complete, as it will cause an error if you try to access material.include where material is never defined in your context. You may want to add additional error handling, but it does work for the given example.

Are the elements of a javascript object initialized in a specific order?

I have a function like that:
parsers[1] = function(buf) {
return {
type: "init",
name: buf.readUTF8String(),
capacity: buf.readUInt32(),
port: buf.readUInt16()
};
}
Do I have any guarantee that name, capacity, and port will be initialized one after the other? Otherwise, the buffer will be read in the wrong order.
I could of course fall back on:
parsers[1] = function(buf) {
var ret = {type: "init"};
ret.name = buf.readUTF8String();
ret.capacity = buf.readUInt32();
ret.port = buf.readUInt16();
return ret;
}
Thanks to #joews' comment, I can answer my own question.
From the link 11.1.5 Object initializer:
Syntax
ObjectLiteral :
{ }
{ PropertyNameAndValueList }
{ PropertyNameAndValueList , }
PropertyNameAndValueList :
PropertyAssignment
PropertyNameAndValueList , PropertyAssignment
In short, the object constructor takes as arguments either nothing, a list of initialization values, or a list of initialization values followed by a comma.
That list of initialization value is composed by a PropertyAssignment or a list of initialization values followed by a PropertyAssignment, meaning basically a list of PropertyAssignment by recursion.
Now the question is in the last PropertyNameAndValueList , PropertyAssignment, is there a specific order in which both components are evaluated?
The production
PropertyNameAndValueList : PropertyNameAndValueList , PropertyAssignment
is evaluated as follows:
Let obj be the result of evaluating PropertyNameAndValueList.
Let propId be the result of evaluating PropertyAssignment.
...
The order will be guaranteed if 2. is sure to follow 1..
From 5.2 Algorithm conventions:
The specification often uses a numbered list to specify steps in an algorithm. These algorithms are used to precisely specify the required semantics of ECMAScript language constructs. The algorithms are not intended to imply the use of any specific implementation technique. In practice, there may be more efficient algorithms available to implement a given feature.
...
For clarity of expression, algorithm steps may be subdivided into sequential substeps.
So, the expected initialization order is element after element, from what I can gather.
As in many other languages, I would not rely on any "order" of properties of an object nor on the way they are initialized or values are assigned to.
If an "external" order is necessary, I would try to achieve that with a kind of mapping of these properties.

Invoke a Function with the Correct Parameters from an Object in JavaScript

Let's say I have an object that looks like this:
{
'apple': 'nice',
'banana': 'decent',
'cherry': 'yuck',
}
and I have these two methods:
function eatItems(cherry, apple) { }
function throwItem(banana) { }
My two questions:
Is it possible for me to invoke eatItem and send the arguments in the correct order? Maybe something like:
eatItems.call(this, {'cherry': cherry, 'apple': apple});
What if I don't know what arguments eatItems receives, can I dynamically look up the names of the arguments for a function so I can know the order that I need to throw them in?
There's a way, indeed, and it involves calling toString on a function:
var source = eatItems.toString();
// => "function eatItems(cherry, apple) { }"
The next step is to parse the string you've got to get the names of the arguments:
var args = source.substring(source.indexOf("(") + 1, source.indexOf(")")),
argNames = /\S/.test(args) ? args.split(/\s*,\s*/) : [];
A few caveats:
This solution has been kept quite simple. It doesn't handle comments in the function definition.
Not every browser can correctly convert a function to a string (the PS3 browser comes to my mind), but they're a really small minority anyway.
I haven't tested it, but there may be some performance issues on slower machines and/or older browsers with large functions.
And, overall, this solution is more like an exercise. I wouldn't recommend taking this pattern in Javascript. Don't forget that some functions handle a variable number of arguments, and you won't find them listed in their definition. Rethink your code, and find a better way.
If I understand correctly you want extract the argument names from the function, and inject data from an object based on those names. This can be accomplished by converting the function to a string, extracting the arguments, and applying the function with those arguments:
function inject(data, f) {
var args = f.toString()
.match(/function\s*?\((.+?)\)/)
.pop()
.split(',')
.map(function(a){return data[a.trim()]})
return function() {
return f.apply(this, args)
}
}
var data = {
apple: 'nice',
banana: 'decent',
cherry: 'yuck',
}
var eat = inject(data, function(cherry, apple) {
console.log(cherry, apple)
})
eat() //=> yuck, nice
The obvious problem with this approach is that it is highly dependent on the variable names, so when you minify your code, the variables will get mangled and the function will stop working. This is a known problem in AngularJS, which uses something similar for their dependency injection.
This is often an XY problem, or an anti-pattern at the very least.

Javascript convention for variable length arguments

I am getting more in to javascript development, and want to ensure I am following popular conventions.
Currently I have a library which consists of functions that can be passed either 1 model to operate on, or many models.
Given the climate that a few javascript libraries are very popular, I am curious; would I be conforming to the 'defacto standard' by achieving my 'single-item or list-of' requirement, by enumerating the arguments variable, or by allowing one of the arguments to be an array?
Scenario 1: argument enumeration
// passing a single entity to my function
sendMail( email, recipient1 );
// passing multiple entities to my function
sendMail( email, recipient1, recipient2 );
Scenario 2: entity argument is either single instance, or array
// pass a single entity
sendMail( email, recipient1 );
// passing multiple entities
sendMail( email, [recipient1, recipient2] );
I have seen areas of jQuery which use 'scenario 2', but I would still like to ask - which approach is the most popular, and why?
Thanks
[EDIT]
A couple of comments have followed the same vein, of using an arguments object - which is similar to 'scenario 2' - but I feel it introduces unnecessary complexity - the elements dont need to be named, because they are just a variable length list. I thought I would just add that here in case my question wasn't clear enough.
[EDIT]
I see code like this all through jQuery-1-7.js
queue: function( elem, type, data ) {
var q;
if ( elem ) {
type = ( type || "fx" ) + "queue";
q = jQuery._data( elem, type );
// Speed up dequeue by getting out quickly if this is just a lookup
if ( data ) {
if ( !q || jQuery.isArray(data) ) {
q = jQuery._data( elem, type, jQuery.makeArray(data) );
} else {
q.push( data );
}
}
return q || [];
}
}
[EDIT]
After some discussion with JP, I came up with this - which I'm not saying is the right choice, but it is very flexible...
lastArgumentAsParams: function()
{
var callerArgs = jQuery.makeArray(this.lastArgumentAsParams.caller.arguments);
// return empty set if caller has no arguments
if ( callerArgs.length == 0 )
return [];
callerArgs.splice(0, callerArgs.length - 1)
// remove all but the last argument
if ( callerArgs.length == 1 && jQuery.isArray(callerArgs[0]))
return callerArgs[0];
else
return callerArgs;
}
If you call this function at the beginning of any function - it will treat the last arg in the caller as a 'variable length argument' - supporting any of the conventions.
For example, I can use it like this
function sendEmail( body, recipients )
{
recipients = lastArgumentAsParams();
// foreach( recipient in recipients )...
}
Now, I can call 'sendEmail' in any of the following ways and it will work as expected
sendEmail('hello world', "bill#microsoft.com" );
sendEmail('hello world', "bill#microsoft.com", "steve#apple.com" );
sendEmail('hello world', ["bill#microsoft.com", "steve#apple.com"] );
I personally prefer using object literals for arguments to support named params, like this:
var myfunc = function(params){ //same as: function myfunc(params){....
alert(params.firstName);
alert(params.lastName);
};
myfunc({firstName: 'JP', lastName: 'Richardson'});
I think that it makes code very readable and order won't matter.
OR
You can also access the arguments object. Note, it's not an array, but it's "array-like". You can read about it here: http://javascriptweblog.wordpress.com/2011/01/18/javascripts-arguments-object-and-beyond/
Edit:
You seem to have a misunderstanding here. You're using the phrase "arguments object" and are thinking that it's the same as object literal notation. They are not.
The arguments object allows you to do this:
function myfunc(){
alert(arguments[0]); //JP
alert(arguments[1]); //Richardson
}
myfunc('JP', 'Richardson');
Does that help?
Another common way is to use object literal as variables:
myFunction(true, {option: value, option2: value});
I personally prefer this method for it is more verbose, and with javascript loose types, it gives you a better hint for what this variables is, and ignores order.
Backbone.js is using this as the preferred method.
To expand on the other answers, there are two main alternatives I usually see: optional arguments and keyword arguments. I don't remember seeing any good examples of the "array-using" idiom and it is kind of obsolete given how the arguments array is always available anyway.
Anyway, my rule of thumb is.
If I have many arguments, or the argument list is likely to change, or if the arguments don't have a good natural order, use the named arguments pattern
My favorite part about this style is that it is really flexible and future proof, while also being kind of self-documenting (in a smalltalk style).
foo({x1:'1', x2:'2', x3:'3'});
function foo(kwargs){
//I try to always copy the arguments back into variables.
//Its a little verbose but it helps documentation a lot and also
// lets me mutate the variables if I want to
var x1 = kwargs.x1,
x2 = kwargs.x2,
x3 = kwargs.x3;
}
If I have few arguments, that are not likely to change, and have a natural order to them, use a plain function (with the optional arguments last in the order)
foo(x1, x2);
foo(x1, x2, x3);
There are three main variations I can think right now of how to handle the optional arguments in the function:
var foo = function(x1, x2, x3){
//variation 1: truthy/falsy
// Short, but I tend to only use it when the variable stands
// for an object or other always-truthy kind of value
x3 = x3 || 'default_value';
//variation 2: using a special placeholder value for blank arguments.
// Usually this is null or undefined. (and undefined works if the arg is not passed too)
if(typeof x3 === 'undefined'){ x3 = 'default_value'; }
//variation 3: explicitly check the number of arguments
// I really like this one since it makes clear if the argument was passed or not.
if(arguments.length < 3){ x3 = 'default_value'; }
}
Also, there are so things I try to avoid:
Don't have functions that receive a large argument list. It can become a mess if they start becoming optional and you forget the order
foo(1, 2, null, null, 3, null, null); //ugh
Don't use fixed-length arrays to be tricky. They are redundant with no arrays at all and when I see an array I usually expect it to 1) be homogeneous and 2) be able to be as long as I want to
foo(true, [1, 2]); //should be foo(true, 1, 2)

Categories