javascript

JS – Types & Grammar

Most developers would say that a dynamic language (like JS) does not have *types*.

An ECMAScript language type corresponds to values that are directly manipulated by an ECMAScript programmer using the ECMAScript language. The ECMAScript language types are Undefined, Null, Boolean, String, Number, and Object.

JavaScript defines seven built-in types:

  • null
  • undefined
  •  boolean
  • number
  • string
  • object
  • symbol — added in ES6!

**Note:** All of these types except `object` are called “primitives”.

The `typeof` operator inspects the type of the given value, and always returns one of seven string values — surprisingly, there’s not an exact 1-to-1 match with the seven built-in types we just listed.

 

Capture.PNG

As you may have noticed, I excluded null from the above listing. It’s special — special in the sense that it’s buggy when combined with the `typeof` operator:

 

typeof null === "object"; // true

If you want to test for a `null` value using its type, you need a compound condition:

var a = null;
(!a && typeof a === "object"); // true

It’s easy to think that function would be a top-level built-in type in JS, especially given this behavior of the `typeof` operator. However, if you read the spec, you’ll see it’s actually a “subtype” of object. Specifically, a function is referred to as a callable object — an object that has an internal Call property that allows it to be invoked.

he fact that functions are actually objects is quite useful. Most importantly, they can have properties. For example:

function a(b,c) {
/* .. */
}

The function object has a `length` property set to the number of formal parameters it is declared with.

a.length;    // 2

Since you declared the function with two formal named parameters (`b` and `c`), the “length of the function” is `2`.

What about arrays? They’re native to JS, so are they a special type?

typeof [1,2,3] === "object"; // true

Nope, just objects. It’s most appropriate to think of them also as a “subtype” of object, in this case with the additional characteristics of being numerically indexed (as opposed to just being string-keyed like plain objects) and maintaining an automatically updated `.length` property.

In JavaScript, variables don’t have types — values have types. Variables can hold any value, at any time.

Another way to think about JS types is that JS doesn’t have type enforcement, in that the engine doesn’t insist that a variable always holds values of the same initial type that it starts out with. A variable can, in one assignment statement, hold a `string`, and in the next hold a `number`, and so on.

var a = 42;
typeof a; // "number"

a = true;
typeof a; // "boolean"

undefined vs undeclared

Variables that have no value currently, actually have the `undefined` value. Calling `typeof` against such variables will return `”undefined”`

var a;
typeof a; // "undefined"

var b = 42;
var c;
b = c;

typeof b; // "undefined"
typeof c; // "undefined"

It’s tempting for most developers to think of the word “undefined” and think of it as a synonym for “undeclared.” However, in JS, these two concepts are quite different.

An “undefined” variable is one that has been declared in the accessible scope, but *at the moment* has no other value in it. By contrast, an “undeclared” variable is one that has not been formally declared in the accessible scope.

var a;
a; // undefined
b; // ReferenceError: b is not defined

Values

Arrays: as compared to other type-enforced languages, JavaScript `array`s are just containers for any type of value, from `string` to `number` to `object` to even another `array` (which is how you get multidimensional `array`s).

var a = [ 1, "2", [3] ];
a.length;       // 3
a[0] === 1;     // true
a[2][0] === 3;  // true

You don’t need to presize your `array`s, you can just declare them and add values as you see fit:

var a = [ ];
a.length;   // 0

a[0] = 1;
a[1] = "2";
a[2] = [ 3 ];

a.length;   // 3

Be careful about creating sparse `array`s (leaving or creating empty/missing slots):

var a = [ ];

a[0] = 1;
// no `a[1]` slot set here
a[2] = [ 3 ];

a[1]; // undefined
a.length; // 3

array`s are numerically indexed (as you’d expect), but the tricky thing is that they also are objects that can have `string` keys/properties added to them (but which don’t count toward the `length` of the `array`)

var a = [ ];

a[0] = 1;
a["foobar"] = 2;

a.length;    // 1
a["foobar"]; // 2
a.foobar;    // 2

However, a gotcha to be aware of is that if a `string` value intended as a key can be coerced to a standard base-10 `number`, then it is assumed that you wanted to use it as a `number` index rather than as a `string` key!

 

var a = [ ];
a["13"] = 42;
a.length; // 14

Generally, it’s not a great idea to add `string` keys/properties to `array`s. Use `object`s for holding values in keys/properties, and save `array`s for strictly numerically indexed values.


Strings

It’s a very common belief that `string`s are essentially just `array`s of characters. While the implementation under the covers may or may not use `array`s, it’s important to realize that JavaScript `string`s are really not the same as `array`s of characters. The similarity is mostly just skin-deep.

a.length; // 3
b.length; // 3

a.indexOf( "o" ); // 1
b.indexOf( "o" ); // 1

var c = a.concat( "bar" ); // "foobar"
var d = b.concat( ["b","a","r"] ); // ["f","o","o","b","a","r"]

a === c; // false
b === d; // false

a; // "foo"
b;

JavaScript `string`s are immutable, while `array`s are quite mutable.

c = a.toUpperCase();
a === c; // false
a;       // "foo"
c;       // "FOO"

b.push( "!" );
b;       // ["f","O","o","!"]

Also, many of the `array` methods that could be helpful when dealing with `string`s are not actually available for them, but we can “borrow” non-mutation `array` methods against our `string`:

a.join; // undefined
a.map;  // undefined

var c = Array.prototype.join.call( a, "-" );
var d = Array.prototype.map.call( a, function(v){
 return v.toUpperCase() + ".";
} ).join( "" );

c; // "f-o-o"
d; // "F.O.O."

Let’s take another example: reversing a string (incidentally, a common JavaScript interview trivia question!). `array`s have a `reverse()` in-place mutator method, but `string`s do not

a.reverse; // undefined

b.reverse(); // ["!","o","O","f"]
b; // ["!","o","O","f

Unfortunately, this “borrowing” doesn’t work with `array` mutators, because `string`s are immutable and thus can’t be modified in place

Array.prototype.reverse.call( a );
// still returns a String object wrapper (see Chapter 3)
// for "foo" 😦

Another workaround (aka hack) is to convert the `string` into an `array`, perform the desired operation, then convert it back to a `string`.

var c = a
 // split `a` into an array of characters
 .split( "" )
 // reverse the array of characters
 .reverse()
 // join the array of characters back to a string
 .join( "" );

c; // "oof"

Numbers

JavaScript has just one numeric type: `number`. This type includes both “integer” values and fractional decimal numbers. I say “integer” in quotes because it’s long been a criticism of JS that there are not true integers, as there are in other languages. That may change at some point in the future, but for now, we just have `number`s for everything.

So, in JS, an “integer” is just a value that has no fractional decimal value. That is, `42.0` is as much an “integer” as `42`

Number literals are expressed in JavaScript generally as base-10 decimal literals. For example:

var a = 42;
var b = 42.3;

The leading portion of a decimal value, if `0`, is optional

var a = 0.42;
var b = .42;

Very large or very small `number`s will by default be outputted in exponent form, the same as the output of the `toExponential()` method, like:

var a = 5E10;
a; // 50000000000
a.toExponential(); // "5e+10"

var b = a * a;
b; // 2.5e+21

var c = 1 / a;
c; // 2e-11

the `toFixed(..)` method allows you to specify how many fractional decimal places you’d like the value to be represented with:

var a = 42.59;

a.toFixed( 0 ); // "43"
a.toFixed( 1 ); // "42.6"
a.toFixed( 2 ); // "42.59"
a.toFixed( 3 ); // "42.590"
a.toFixed( 4 ); // "42.5900"

Notice that the output is actually a string representation of the number, and that the value is 0-padded on the right-hand side if you ask for more decimals than the value holds.

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s