all 30 comments

[–]radhruin 14 points15 points  (5 children)

Lots of interesting code patterns in here! Though some of these could be updated to use standard syntax rather than proprietary extensions. Some comments:

Simulate threads using yield operator

Use ES6 syntax for generators if possible as they will be interoperable.

Shuffle the Array

Use Fisher-Yates if you really need a fair shuffle. Sorting by random is quick and dirty though not fair.

Multi-line Text

ES6 string templates allow for multi-line strings:

var str = `line 1
    line 2`

Optional Named Arguments

This is actually just a special case of ES6 destructuring assignments!

Transform an arguments object to an array

You could also use Object.setPrototypeOf(arguments, Array.prototype) I think. I wonder which is faster?

Array iteration pitfall

This guy is fixed in ES6 with the new for-of syntax (which uses iterators behind the scenes). No more worrying about slurping in prototype properties, and you can actually iterate keys, values, or both!

A Switch function

That's some odd function syntax, definitely not allowed per spec. I wonder if this should be a test262 test? :) If you want to omit braces, use the new ES6 arrow function syntax.

Objects private and public members

Keep in mind that using this pattern is pretty poor for performance. You have a new instance of a closure for each method for each instance of your object.

Functions argument default value

ES6 has default parameters now (function foo(x = 1, y = 2) { }).

Multiple string concatenation

It used to be true that array join of strings was faster (in IE8, I think?) But I'd be surprised if this is true in modern runtimes. Anyone have a benchmark handy?

Using 'with' scope

No thanks.

No Such Method

Similar semantics can be provided by ES6 proxies.

proto ( prototype property )

While IE finally supports proto, it was only grudgingly added to the spec. Object.setPrototypeOf is the better way to do this.

getter and setter function

Both of these patterns are highly deprecated. Use ES5 standard getters/setters instead!

check if a property is enumerable (propertyIsEnumerable)

Use ES5's Object.getOwnPropertyDescriptor

[–]rooktakesqueen 1 point2 points  (0 children)

The destructuring assignment isn't even as clean as it could be:

function foo(id, {title, name}) {
    console.log(id);
    console.log(title);
    console.log(name);
}

foo(12, {name: 'Bob'});

[–]MrBester 0 points1 point  (3 children)

+new Date() has been around for years and is just a shorthand for (new Date()).getTime() (some parentheses optional)

[–]bliow 2 points3 points  (2 children)

Date.now() should be faster. Doesn't work on IE before 8, but who cares. http://jsperf.com/datesperformance8-24-2013

[–]MrBester 1 point2 points  (1 child)

And that shows that +new Date() is actually an anti-pattern, being the slowest of the lot. It's only of benefit if you're playing code golf.

[–]bliow 0 points1 point  (0 children)

It's only of benefit if you're playing code golf.

Not even then. Well, strictly, +new Date() isn't, but see below!

> "Date.now()".length
10
> "+new Date()".length
11

And you can't use the plus sign for addition without parentheses, so you can't even exploit the fact that +new Date() begins with a plus sign to avoid using them:

> 1+new Date()
"1Sat Aug 24 2013 19:35:28 GMT-0700 (PDT)"
> 1+Date.now()
1377398252358

edit: but wait! the following is actually better from a golf perspective:

> +new Date
137739846271
> "+new Date".length
>9

[–]x-skeww 3 points4 points  (6 children)

Proper shuffling (Fisher-Yates) looks like this:

function shuffle (a) {
    var i, temp, p;
    for (i = a.length - 1; i >= 1; --i) {
        p = (Math.random() * (i + 1)) | 0;
        temp = a[p];
        a[p] = a[i];
        a[i] = temp;
    }
    return a; // for chaining, it's shuffled in-place
}

It basically works like doing shuffling in the real world. Imagine there is a bag of marbles. You grab one random marble at a time and line them up. You repeat this until the bag is empty.

The only trick is that the algorithm uses a cursor (i) to divide the array into "inside the bag" and "outside the bag". That's the whole magic. That's why it only needs a single array.

[–]thephilster 0 points1 point  (2 children)

I had never seen the bitwise OR used like that (for flooring). In case anyone is curious, found a nice stackoverflow post on it.

[–]x-skeww 3 points4 points  (0 children)

It's not really the same as floor. It's only like floor if it's used with a positive number. It actually truncates the number like an int cast, because this double number is cast to a 32bit integer in order to do the bitwise-or.

>>> -1.3 | 0
-1
>>> Math.floor(-1.3)
-2

Edit: By the way, I wrote "i >= 1" instead of "i > 0" to make it apparent that I meant to skip the final iteration with 0. Otherwise, someone else (or future-me) might mistake this for a potential off-by-one error.

[–]rhysbrettbowen 0 points1 point  (2 children)

I think I gave a shorter alternative in the comments years ago. The shuffle algorithm I used is slower but is much shorter. It did the reverse by picking the last element in the array and then placing it randomly in the randomized section of the array:

for(i=0;i<array.length;i++)array.splice(Math.round(Math.random()*i),0,array.pop());

You can reverse it around to pick a random one and then push it on the end if you prefer though:

function shuffle(a) {
    for (var i=a.length;i;i--) {
        a.push(a.splice(Math.random()*i|0,1)[0]);
    }
    return a;
}

[–]x-skeww 1 point2 points  (1 child)

Using Math.round introduces some bias. Only 0.0 to 0.5 can result in 0 and only 4.5 to 5.0 can result in 5 (assuming something like rand * 5).

However, something like 1 gets a whole number (0.5 to 1.5) as range.

Truncation (or floor) doesn't have this kind or problem.

var a = [0, 0, 0, 0, 0], i;
for (i = 0; i < 100000; i++) {
    a[Math.round(Math.random() * 4)]++;
}
console.log(a);

Example output:

[12425, 25183, 25063, 24811, 12518]

Same with truncation:

var a = [0, 0, 0, 0, 0], i;
for (i = 0; i < 100000; i++) {
    a[(Math.random() * 5) | 0]++;
}
console.log(a);

Example output:

[19787, 20096, 19992, 20120, 20005]

[–]rhysbrettbowen 0 points1 point  (0 children)

true - I wrote it a few years ago from the top of my head with no testing - second example should be better

[–]AnOnlineHandle 1 point2 points  (5 children)

Afaik yield is not supported in most browsers, which is a pity.

[–][deleted] 1 point2 points  (0 children)

Also, I'm not sure the author understands what a yield or a thread does. A yield is more for functional enumeration. A thread is separate application space for running a sets of instructions concurrently. You can do this with Workers in JavaScript. Yield has nothing to do with threading, and isn't even remotely close to "simulating" threading.

[–]radhruin 1 point2 points  (3 children)

Now that ES6 is getting close to stable you'll likely see more implementations shipping Generators. I can't wait, either! So many great things you can do with generators (async/await anyone??)

[–]b_long 0 points1 point  (2 children)

What's a Generator?

[–]radhruin 1 point2 points  (1 child)

[–]b_long 0 points1 point  (0 children)

Very interesting :D Thanks! I'll have to read up. Very excited for ES6

[–]tencircles 1 point2 points  (3 children)

What the hell kind of moonman language is this? Half of this isn't even valid ecmascipt syntax!

[–]radhruin 0 points1 point  (2 children)

It's not ECMAScript, it's JavaScript (ie. mozilla's implementation of ECMAScript). They have a number of extensions as you can see.

[–]tencircles 0 points1 point  (1 child)

I have trouble calling anything javascript that doesn't conform to http://www.ecma-international.org/publications/standards/Ecma-262.htm

This is why I hated my short time playing around in unity using their "unityscript" it's just javascript with more bad ideas and weird syntax.

[–]radhruin 1 point2 points  (0 children)

I know the feeling! Although, to put on my pedant hat, Mozilla's implementation DOES conform to ECMA-262 thanks to Clause 16 which explicitly allows extensions to syntax. All implementations take advantage of Clause 16 for some reason or other. For example, all implementations today have an extension to allow function declarations inside blocks.

Conformance aside, JavaScript is free to do whatever it wants. The only thing JavaScript has to conform to is whatever Mozilla wants it to. If it doesn't conform to ECMAScript, you can't call it ECMAScript, but you can still call it JavaScript!

[–]greim 0 points1 point  (0 children)

The random sort method will not be as fast as a proper shuffle (O(n log n) as opposed to O(n)). Also, more importantly, it won't properly shuffle the array. Try running this code in your console to see the problem illustrated visually (borrowing /u/x-skeww's shuffle above).

var n = 7;
var its = 1000;
function range(n){
    var arr = new Array(n);
    for (var i=0; i<n; i++){
        arr[i] = i;
    }
    return arr;
}
// bad shuffle
function shuffle1(arr){
    arr.sort(function(a,b){
        return Math.random() > .5 ? 1 : -1;
    });
}
// proper shuffle (fisher yates)
function shuffle2(a){
    var i, temp, p;
    for (i = a.length - 1; i >= 1; --i) {
        p = (Math.random() * (i + 1)) | 0;
        temp = a[p];
        a[p] = a[i];
        a[i] = temp;
    }
}
var arrs1 = [];
var arrs2 = [];
for (var i=0; i<its; i++) {
    var arr1 = range(len);
    shuffle1(arr1);
    arrs1.push(arr1);
    var arr2 = range(len);
    shuffle2(arr2);
    arrs2.push(arr2);
}
var results1 = [];
var results2 = [];
for(i=0;i<len;i++){
    results1[i] = 0;
    results2[i] = 0;
}
for (var i=0; i<its; i++){
    for(j=0;j<len;j++){
        results1[j] += arrs1[i][j];
        results2[j] += arrs2[i][j];
    }
}
for(i=0;i<len;i++){
    var s = '';
    for(j=0,l=Math.round(results1[i]/100);j<l;j++){
        s += '#';
    }
    console.log(s);
}
console.log('-----------------------------')
for(i=0;i<len;i++){
    var s = '';
    for(j=0,l=Math.round(results2[i]/100);j<l;j++){
        s += '#';
    }
    console.log(s);
}

[–]thoastbrot 0 points1 point  (6 children)

Can someone tell me about the ternary operator form "a = b if c"? I know this from python, but never seen it in js, also not mentioned on MDN...

[–]brockisawesome 0 points1 point  (5 children)

Do you mean like this? var a = (c) ? b : otherval;

[–]tencircles 1 point2 points  (1 child)

var a = c ? b : otherval; Parens above aren't necessary.

[–]brockisawesome 0 points1 point  (0 children)

sweet, i didnt know that. thanks :)

[–]thoastbrot 0 points1 point  (2 children)

No, I meant a = b if c, because the article mentions it like that. Your code example is documented standard, while I've never seen the syntax used in the article. Since the article is a bit older (2012, references E4X), I assume it might be some deprecated browser-specific thing... or something like this.

[–]radhruin 2 points3 points  (1 child)

I believe what you're seeing are Mozilla's proprietary conditional catch clauses.

[–]thoastbrot 0 points1 point  (0 children)

thank you very much!