Table of Contents

  1. == and ===
  2. Dig deeper
    1. What about arrays?
    2. What about objects
  3. Implicit conversions
  4. Conclusion

== and ===

Likely you know the difference between == and ===: basically, === means strict equality where no implicit conversion is allowed whereas == is loose equality.

1
2
3
4

'a' === 'a' // true
0 == false // true

Dig deeper

OK but this is too boring since we all know that.

How about this:

1
2
3
4

String('a') === 'a'
new String('a') === 'a'

Well the answers are true and false because String() returns a primitive string while new String() returns a string object. Surely new String('a') == 'a' yields true. No surprise.

What about arrays?

[] === []

Well this returns false because for non-primitive objects, they are compared by reference. This always returns false because they are different in terms of memory location.

However surprisingly you can compare arrays like this:

[1, 2, 3] < [2, 3]      // true
[2, 1, 3] > [1, 2, 3]   // true
Blonde hmmm

(Wait a sec. I think I have an idea.)

How about this:

function arrEquals(arr1, arr2) {
    return !(arr1 < arr2) && !(arr2 < arr1);
}
Fuck yeah smile

Well this is wrong because arrays will be flattened when compared, like this

[[1, 2], 1] < [1, 2, 3]     // true

What about objects

What’s the result of this expression?

{} === {}

Well it’s neither true nor false but you get SyntaxError because in this case {} is not an object literal but a code block and thus it cannot be followed with =. Anyway we are drifting away from the original topic…

Implicit conversions

Well that’s just warm-up. Let’s see something serious.

If you read something about “best practices”, you would probably be told not to use == because of the evil conversion. However chances are you’ve used it here and there and most likely that’s also part of the “best practices”.

For example:

var foo = bar();
if (foo) {
    doSomething();
}

This works because in JavaScript, only 6 object/literals are evaluated to false. They are 0, '', NaN, undefined, null and of course false. Rest of the world evaluates to true, including {} and [].

Hmm here’s something wacky:

1
2
3
4
5
6
7
8
9
10
11

var a = {
valueOf: function () {
return -1;
}
};

if (!(1 + a)) {
alert('boom');
}

Your code does go boom because 1 + a gets implicitly converted to 1 + a.valueOf() and hence yields 0.

The actual behavior is documented in ECMA standard - http://www.ecma-international.org/ecma-262/6.0/#sec-abstract-equality-comparison

In most cases, implicit conversion would cause valueOf() to be called or falls back to toString() if not defined.

For example:

1
2
3
4
5
6
7
8
9
10
11
12

var foo = {
valueOf: function () {
return 'value';
},
toString: function () {
return 'toString';
}
};

'foo' + foo // foovalue

This is because according to standard, when toPrimitive is invoked for implicit conversion with no hint provided (e.g. in the case of concatenation, or when == is used between different types), it by default prefers valueOf. There are a few exceptions though, including but not limited to Array.prototype.join and alert. They would call toPrimitive with string as the hint so toString() will be favored.

Conclusion

In general, you probably want to avoid using == and use === most of the time if not always to avoid worrying about wonky implicit conversion magic.

However, you can’t be wary enough. For example:

isNaN('1') === true

You might think that '1' is a string and hence this should be false but unfortunately isNaN always calls toNumber internally (spec) and hence this is true.

Computer stare

Comment and share

Table of Contents

  1. Have you seen eval() written like this?
  2. Regular eval
  3. Global eval
  4. Back to the original topic

Recently I’ve been writing quite a bit of front-end stuff and seen quite a few tricks from other people’s libraries. It turns out JavaScript is a pretty wonky and fked up interesting language, which tempts me to write a series about it and this is the first one. This is by no means supposed to show how to write JS but just to show some “wacky” stuff.

Have you seen eval() written like this?

(0, eval)('something');
{% rage_face 'Are you fucking kidding me' style:width:200px %}

Regular eval

Eval basically allows you to execute any script within the given context.

For example:

{% codeblock lang:js %} eval('console.log("123");'); // prints out 123 (function A() { this.a = 1; eval('console.log(this.a);'); // 1 })(); {% endcodeblock %}

So far everything is normal: eval runs inside the current scope. this is pointed to the instance of A.

Global eval

Things get interesting when you do this:

{% codeblock lang:js %} var someVar = 'outer'; (function A() { this.someVar = 'inner'; eval('console.log(someVar);'); // you may want 'outer' but this says 'inner' })(); {% endcodeblock %}

Well in this scenario eval cannot get the value of someVar in the global scope.

However ECMA5 says, if you change eval() call to indirect, in other words, if you use it as a value rather than a function reference, then it will evaluate the input in the global scope.

So this would work:

{% codeblock lang:js %} var someVar = 'outer'; (function A() { var geval = eval; this.someVar = 'inner'; geval('console.log(someVar);'); // 'outer' })(); {% endcodeblock %}

Although geval and eval call the exact same function, geval is a value and thus it becomes an indirect call according to ECMA5.

Back to the original topic

So what the hell is (0, eval) then? Well a comma separated expression list evaluates to the last value, so it essentially is a shortcut to

var geval = eval;
geval(...);

0 is only a puppet here. It could be any value.

So much win

Comment and share

Stop bundling in the http/2 world since it does it for you.

Modularization is a great idea

Back in the old days where there were no concept regarding frontend package management, we would lay out all the scripts in order in the html file, and hope for the best that they would somehow work together if order were right. This surely doesn’t work well with huge projects, but luckily back then JavaScripts weren’t so shiny anyways - UIs weren’t so cool and logic was much simpler. However, things do evolve. People soon noticed that this approach wouldn’t scale - cooperation across multiple teams becomes super tricky, if not impossible, and it doesn’t play well with DRY either.

Then people came up with a great idea of modularizing JS code (probably back in 2003?) the same way you would do for your beloved Java/C++ code libraries. And then there came the CommonJS definition concept by Kevin Dangoor back in 2009. Many people got to know this idea thanks to Node.js, and it works quite well, especially for server side code. Now you can easily use npm and build both the frontend and backend using the same tool very quickly, thanks to the JS community. Since people have the same interface for code modularization, team cooperation becomes much easier and projects gain benefit from much better encapsulation.

Continue reading
  • page 1 of 1
Author's picture

Shawn Xu

Software Engineer in Bay Area


author.job