Learning the expressive power of Javascript - javascript

I have lots of functions of the following template:
function (field, filter){
var q = {};
q[field] = doSomething(filter);
return q;
}
I am new to Javascript, but already got the idea that it is a very expressive language. So, my question is can the body of such a function be written more concisely?
(I cannot pass q as the parameter and eliminate var q = {}, since the output of these functions should not be merged into a single object hash).
I am perfectly aware that knowing the answer to this question is likely to make my code neither faster nor clearer. I just want to learn, that's all - so no flames, please.

No, you can't simplify it more.
You are trying to do something like this:
({})[field] = value;
but then you want to return the (now modified) {}. In order to do that you have to keep a reference to it. That's what your var q = {} does.

You can always avoid repetition in your code, using something like
function objectWith(field, value) {
var q = {}; q[field] = value; return q;
}
Which would make your original code into
function f(field, filter) {
return objectWith(field, doSomething(filter);
}
Or even better, avoid 'f' altogether and use
var o = objectWith(field, doSomething(filter);
whenever you would originally use it, assuming field is always a variable. Were it a literal, it would be even clearer to write
var o = { fieldLiteral: doSomething(filter) };

Just my devious tidbit, but what you are doing is the equivalent as the following piece of code :
function f(field, filter){
var q = {};
q.__defineGetter__(field, doSomething(filter));
return q;
}
There might be some way to shorten it in that form.

Related

Is it bad practice to use JavaScript Array.prototype.map with scoped variables

I know that map() is meant to iterate lists, run a function on each member and return (or not return) a value as a list item. But what if I use it the same way as I would use forEach()?
Example:
var stuff = [1, 2, 3];
var newStuff = {};
var moreStuff = [];
stuff.forEach(function(value, key){
newStuff[value] = {'value' : value};
moreStuff.push({'other_stuff': value});
});
$('body').append('<div>1. ' + JSON.stringify(newStuff) + '/' + JSON.stringify(moreStuff) + '</div>');
//vs.
newStuff = {};
moreStuff = [];
stuff.map(function(value, key){
newStuff[value] = {'value' : value};
moreStuff.push({'other_stuff': value});
});
$('body').append('<div>2. ' + JSON.stringify(newStuff) + '/' + JSON.stringify(moreStuff) + '</div>');
results...
1. {"1":{"value":1},"2":{"value":2},"3":{"value":3}}/[{"other_stuff":1},{"other_stuff":2},{"other_stuff":3}]
2. {"1":{"value":1},"2":{"value":2},"3":{"value":3}}/[{"other_stuff":1},{"other_stuff":2},{"other_stuff":3}]
https://jsfiddle.net/0n7ynvmo/
I'm pretty sure, async considerations are the same (and might botch this example), and results are the same, but for the sake of discussion of best practices, what are the drawbacks to one way vs. another? Is it an abuse of the map() method to change a previously scoped variable within the function and not actually return anything? Is the correct answer simply a matter of discretion?
It works. However, applying Principle of Least Astonishment here suggests that it's better to use the function whose known / primary use matches your intent rather than using a different one 'off-label'. There's no TECHNICAL reason to use .forEach() over .map() here that I know of, but because generating side-effects out of .map() is an uncommon pattern, it's going to make your code less legible than it would be if you used .forEach().
If you're working in a team environment where many hands will touch your code, then it's worth being conventional in order to maximize future understanding of intent.
It's really, really poor practice to pass a callback with side effects to map, filter, reduce, etc. Most folks reading your code would expect the callback to return the new array element and do nothing else.
Something like the following is more tightly tailored to your needs and more readable.
var moreStuff = stuff.map(function(value) {
return { otherStuff: value };
});
var newStuff = {};
stuff.forEach(function(value) {
newStuff[value] = { value: value };
});
Not really many drawbacks - except in forEach you're not returning a new list. But in your case, you want a new list, so use .map
var newStuff = {};
moreStuff = stuff.map(function(value, key){
newStuff[value] = {'value' : value};
return {'other_stuff': value};
});

Assigning the same function to different variables

I was going through the WURFL.js source, I saw this towards the bottom of the page:
var logo=document.getElementById("hero"),heroText=document.getElementById("hero"), ...
Obviously, the variables, logo and heroText, are referring to the same thing. Isn't that an unnecessary overhead on the DOM parsing in JavaScript (since JavaScript has to look for the id hero each time)? Apparently, a more efficient one is:
var logo=document.getElementById("hero");
var heroText = logo;
In that case, heroText could be another object or could also be referring to the same object as logo. I don't know which because I don't know how the JavaScript interpreter works (I'm a C# person, a learner, though).
So my question is really this: (I'm assuming WURFL didn't make a mistake) how does JavaScript interpret the two lines? Thanks, in advance.
The difference would be (if it weren't returning a DOM element) that if you do
var getObj = function() { return {} };
var a = getObj();
var b = getObj();
a.test = 'hi';
console.log(b);
// Object {}
But if you do:
var getObj = function() { return {} };
var a = getObj();
var b = a;
a.test = 'hi';
console.log(b);
// Object {test: "hi"}
One results in two unique objects, the other in two references to the same object.
var a = document.getElementById('notify-container')
var b = document.getElementById('notify-container')
a.test = 'hi'
console.log(b.test);
//"hi"
So, in the instance you are showing, yes it is more efficient to do
var logo=document.getElementById("hero");
var heroText = logo;
There clearly seams to be a mistake. Because document.getElementById returns a 'live' representation of a node. That means that whenever that element changes, the variable that holds tha node ( logo, heroText ) is also changed; also, if you check those two variables for equality they will be the same.

Javascript prototypes - what's the advantage in this case?

"Oh no", I hear you groan, "not this question again", but bear with me for a minute.
I am trying to get to grips with prototyping and I understand the benefits of sharing common functionality across instances of an object. However, in the following case what am I gaining by using a prototype instead of just a standalone function?
I want to take a sentence and randomise the positions of each word, so I can either do the following:
Array.prototype.shuffle = function() {
var i = this.length;
if (i == 0) return this;
while (--i) {
var j = Math.floor(Math.random() * (i + 1 ));
var a = this[i];
var b = this[j];
this[i] = b;
this[j] = a;
}
return this;
};
function randomiser(){
var s = "My name is Bob";
var shuffledSentence = s.split(' ').shuffle().join(' ');
console.log(shuffledSentence); // "Bob My name is"
}
or, I can use a simple function call instead to randomise my string:
function randomise(arrayToRandomise){
var i = arrayToRandomise.length;
if (i == 0) return arrayToRandomise;
while (--i) {
var j = Math.floor(Math.random() * (i + 1 ));
var a = arrayToRandomise[i];
var b = arrayToRandomise[j];
arrayToRandomise[i] = b;
arrayToRandomise[j] = a;
}
return arrayToRandomise;
}
function randomiser(){
var s = "My name is Bob";
var shuffledSentence = s.split(' ');//.shuffle().join(' ');
var myShuffledString = this.randomise(shuffledSentence).join(' ');
console.log(myShuffledString); // "Bob My name is"
}
What am I gaining here by using a prototype (apart from more elegant code!)?
There is no difference or any pros or cons to distinguish between them, I go with the second solution, because we better not modify the JavaScript native prototypes, unless (the only case that I would agree on modifying native prototypes) to cover some cross-browser issues. for instance, if your browser doesn't support forEach in Array, as you might need it in your code, IMHO, it is not a bad thing to add it to your Array.prototype.
But the point is the better way to do that is code this in a way that doesn't affect anything, like when you change the Array.prototype like this:
Array.prototype.shuffle = ...
then if you iterate the array with for(..in), your shuffle would show up.
So if I were you and I wanted to modify a native prototype, I would do that like this:
Object.defineProperty(Array.prototype, "shuffle", {
enumerable:false,
value:function(){
//your code
}
});
this way, using the enumerable property, you can prevent it from showing up in for(..in) loops or Object.keys().
I don't have that much to say about this, but IMHO here are some points:
prototype should best not be extended to native objects, this is somewhat akin to polluting the global namespace, but that's somewhat a matter of opinion.
prototype is really just a construct for OO, which philosophically just makes code more elegant, so in this case I think that's all prototype does here.

Using variables with nested Javascript object

Suppose I have this:
var a = { A : { AA : 1 }, B : 2 };
Is there a way for me to create a variable that could allow me to reference either AA or B? What would the syntax look like?
// I know I can do this:
a['B']; // 2
a['A']['AA']; // 1
// something like this?
var myRef = ???;
a[myRef]; 1 or 2 depending on myRef
If not, what's a better way to get what I'm going for here?
Not directly.
Solution 1 - use object flattening
Flatten object, to have new object var a = { 'A.AA' : 1; B : 2 };.
See compressing object hierarchies in JavaScript
or Flattening a complex json object for mvc binding to get the javascript function for it.
Soution 2 - write key-path accessor
I can see it was already addressed by Eugen.
Reposted code-reviewed version:
function Leaf(obj,path) {
path=path.split('.');
var res=obj;
for (var i=0;i<path.length;i++) res=res[path[i]];
return res;
}
Solution 3 - use eval
var x = eval("a." + myRef); // x will be 1 for myRef == "A.AA", 2 for "B"
Be careful with this solution as you may introduce some security issues. It is more of the curiosity.
Since i also encounter this problem, i wrote also a one line util for this (ES6):
const leaf = (obj, path) => (path.split('.').reduce((value,el) => value[el], obj))
Example:
const objSample = { owner: { name: 'Neo' } };
const pathSample = 'owner.name';
leaf(objSample, pathSample) //'Neo'
function Leaf(obj,path) {
path=path.split('.');
var res=obj;
for (var i=0;i<path.length;i++) obj=obj[path[i]];
return res;
}
Leaf(a,'B')=2
Leaf(a,'A.AA')=1
Decorate with error handling etc. according to your needs.
With lodash _.get function, you can access nested properties with dot syntax.
Node server-side example:
const _ = require('lodash');
let item = { a: {b:'AA'}};
_.get(item, 'a.b');
Actually no, because js object are seen as property bags and doing a[X] is for accessing first level properties only...
But you could wrap the logic a['A']['AA']; // 1 in a function that does the same, like this
//WARN... no undefined check here => todo !
function _(o, path) {
var tmp = o
for (var i=0 ; i < path.length ; i++) {
tmp = tmp[path[i]]
}
return tmp
}
var r = _(a, ['A', 'AA'])
This is pretty much the same as other answers, but the difference is when dummy boy create object property name containing dots... Like var a = {"a.a" : 3 } is valid.
Now, such problem would occurs maybe more often now with the help of IndexedDB to store anything locally...

How best to inherit from native JavaScript object? (Especially String)

I'm a long-time browser but a first time participator. If I'm missing any etiquette details, please just let me know!
Also, I've searched high and low, including this site, but I haven't found a clear and succinct explanation of exactly what I'm looking to do. If I just missed it, please point me in the right direction!
Alright, I want to extend some native JavaScript objects, such as Array and String. However, I do not want to actually extend them, but create new objects that inherit from them, then modify those.
For Array, this works:
var myArray = function (n){
this.push(n);
this.a = function (){
alert(this[0]);
};
}
myArray.prototype = Array.prototype;
var x = new myArray("foo");
x.a();
However, for String, the same doesn't work:
var myString = function (n){
this = n;
this.a = function (){
alert(this);
};
}
myString.prototype = String.prototype;
var x = new myString("foo");
x.a();
I've also tried:
myString.prototype = new String();
Now, in trying to research this, I've found that this does work:
var myString = function (n){
var s = new String(n);
s.a = function (){
alert(this);
};
return s;
}
var x = myString("foo");
x.a();
However, this almost feels like 'cheating' to me. Like, I should be using the "real" inheritance model, and not this shortcut.
So, my questions:
1) Can you tell me what I'm doing wrong as regards inheriting from String? (Preferably with a working example...)
2) Between the "real" inheritance example and the "shortcut" example, can you name any clear benefits or detriments to one way over the other? Or perhaps just some differences in how one would operate over the other functionally? (Because they look ultimately the same to me...)
Thanks All!
EDIT:
Thank you to everyone who commented/answered. I think #CMS's information is the best because:
1) He answered my String inheritance issue by pointing out that by partially redefining a String in my own string object I could make it work. (e.g. overriding toString and toValue)
2) That creating a new object that inherits from Array has limitations of its own that weren't immediately visible and can't be worked around, even by partially redefining Array.
From the above 2 things, I conclude that JavaScript's claim of inheritablity extends only to objects you create yourself, and that when it comes to native objects the whole model breaks down. (Which is probably why 90% of the examples you find are Pet->Dog or Human->Student, and not String->SuperString). Which could be explained by #chjj's answer that these objects are really meant to be primitive values, even though everything in JS seems to be an object, and should therefore be 100% inheritable.
If that conclusion is totally off, please correct me. And if it's accurate, then I'm sure this isn't news to anyone but myself - but thank you all again for commenting. I suppose I now have a choice to make:
Either go forward with parasitic inheritance (my second example that I now know the name for) and try to reduce its memory-usage impact if possible, or do something like #davin, #Jeff or #chjj suggested and either psudo-redefine or totally redefine these objects for myself (which seems a waste).
#CMS - compile your information into an answer and I'll choose it.
The painfully simple but flawed way of doing this would be:
var MyString = function() {};
MyString.prototype = new String();
What you're asking for is strange though because normally in JS, you aren't treating them as string objects, you're treating them as "string" types, as primitive values. Also, strings are not mutable at all. You can have any object act as though it were a string by specifying a .toString method:
var obj = {};
obj.toString = function() {
return this.value;
};
obj.value = 'hello';
console.log(obj + ' world!');
But obviously it wouldn't have any string methods. You can do inheritence a few ways. One of them is the "original" method javascript was supposed to use, and which you and I posted above, or:
var MyString = function() {};
var fn = function() {};
fn.prototype = String.prototype;
MyString.prototype = new fn();
This allows adding to a prototype chain without invoking a constructor.
The ES5 way would be:
MyString.prototype = Object.create(String.prototype, {
constructor: { value: MyString }
});
The non-standard, but most convenient way is:
MyString.prototype.__proto__ = String.prototype;
So, finally, what you could do is this:
var MyString = function(str) {
this._value = str;
};
// non-standard, this is just an example
MyString.prototype.__proto__ = String.prototype;
MyString.prototype.toString = function() {
return this._value;
};
The inherited string methods might work using that method, I'm not sure. I think they might because there's a toString method. It depends on how they're implemented internally by whatever particular JS engine. But they might not. You would have to simply define your own. Once again, what you're asking for is very strange.
You could also try invoking the parent constructor directly:
var MyString = function(str) {
String.call(this, str);
};
MyString.prototype.__proto__ = String.prototype;
But this is also slightly sketchy.
Whatever you're trying to do with this probably isn't worth it. I'm betting there's a better way of going about whatever you're trying to use this for.
If you want an absolutely reliable way of doing it:
// warning, not backwardly compatible with non-ES5 engines
var MyString = function(str) {
this._value = str;
};
Object.getOwnPropertyNames(String.prototype).forEach(function(key) {
var func = String.prototype[key];
MyString.prototype[key] = function() {
return func.apply(this._value, arguments);
};
});
That will curry on this._value to every String method. It will be interesting because your string will be mutable, unlike real javascript strings.
You could do this:
return this._value = func.apply(this._value, arguments);
Which would add an interesting dynamic. If you want it to return one of your strings instead of a native string:
return new MyString(func.apply(this._value, arguments));
Or simply:
this._value = func.apply(this._value, arguments);
return this;
There's a few ways to tackle it depending on the behavior you want.
Also, your string wont have length or indexes like javascript strings do, a way do solve this would be to put in the constructor:
var MyString = function(str) {
this._value = str;
this.length = str.length;
// very rough to instantiate
for (var i = 0, l = str.length; i < l; i++) {
this[i] = str[i];
}
};
Very hacky. Depending on implementation, you might just be able to invoke the constructor there to add indexes and length. You could also use a getter for the length if you want to use ES5.
Once again though, what you want to do here is not ideal by any means. It will be slow and unnecessary.
This line is not valid:
this = n;
this is not a valid lvalue. Meaning, you cannot assign to the value referenced by this. Ever. It's just not valid javascript. Your example will work if you do:
var myString = function (n){
this.prop = n;
this.a = function (){
alert(this.prop);
};
}
myString.prototype = new String; // or String.prototype;
var x = new myString("foo");
x.a();
Regarding your workaround, you should realise that all you're doing is making a String object, augmenting a function property, and then calling it. There is no inheritance taking place.
For example, if you execute x instanceof myString in my example above it evaluates to true, but in your example it isn't, because the function myString isn't a type, it's just a regular function.
You can't assign the this in a constructor
this=n is an error
Your myarray is just an alias for the native Array- any changes you make to myarray.prototype are changes to Array.prototype.
I would look into creating a new object, using the native object as a backing field and manually recreating the functions for the native objects. For some very rough, untested sample code...
var myString = {
_value = ''
,substring:_value.substring
,indexOf:_value.indexOf
}
Now, I'm sure this wont work as intended. But I think with some tweaking, it could resemble on object inherited from String.
The ECMAScript6 standard allows to inherit directly from the constructor functions of a native object.
class ExtendedNumber extends Number
{
constructor(value)
{
super(value);
}
add(argument)
{
return this + argument;
}
}
var number = new ExtendedNumber(2);
console.log(number instanceof Number); // true
console.log(number + 1); // 4
console.log(number.add(3)); // 5
console.log(Number(1)); // 1
In ECMAScript5 it is possible to inherit like this:
function ExtendedNumber(value)
{
if(!(this instanceof arguments.callee)) { return Number(value); }
var self = new Number(value);
self.add = function(argument)
{
return self + argument;
};
return self;
}
However, the generated objects are not primitive:
console.log(number); // ExtendedNumber {[[PrimitiveValue]]: 2}
But you can extend a primitiv type by extending his actual prototype that is used:
var str = "some text";
var proto = Object.getPrototypeOf(str);
proto.replaceAll = function(substring, newstring)
{
return this.replace(new RegExp(substring, 'g'), newstring);
}
console.log(str.replaceAll('e', 'E')); // somE tExt
A cast to a class-oriented notation is a bit tricky:
function ExtendedString(value)
{
if(this instanceof arguments.callee) { throw new Error("Calling " + arguments.callee.name + " as a constructor function is not allowed."); }
if(this.constructor != String) { value = value == undefined || value == null || value.constructor != String ? "" : value; arguments.callee.bind(Object.getPrototypeOf(value))(); return value; }
this.replaceAll = function(substring, newstring)
{
return this.replace(new RegExp(substring, 'g'), newstring);
}
}
console.log(!!ExtendedString("str").replaceAll); // true

Categories

Resources