From 93ca2c03893526e619c2c08f83825b0fc7ebe394 Mon Sep 17 00:00:00 2001 From: Kyle Simpson Date: Sat, 27 Dec 2014 09:15:57 -0600 Subject: [PATCH] types+grammar: clarifications around 'primitive', closes #271 --- types & grammar/ch1.md | 18 ++++++++++-------- types & grammar/ch2.md | 10 +++++----- types & grammar/toc.md | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/types & grammar/ch1.md b/types & grammar/ch1.md index e2b0f3134..1eb4349e1 100644 --- a/types & grammar/ch1.md +++ b/types & grammar/ch1.md @@ -33,9 +33,9 @@ Coercion confusion is perhaps one of the most profound frustrations for JavaScri Armed with a full understanding of JavaScript types, we're aiming to illustrate why coercion's *bad reputation* is largely over-hyped and somewhat undeserved -- to flip your perspective, to seeing coercion's power and usefulness. But first, we have to get a much better grip on values and types. -## Primitives +## Built-in Types -JavaScript defines seven built-in types, that we often call "primitives". These are: +JavaScript defines seven built-in types. These are: * `null` * `undefined` @@ -45,7 +45,9 @@ JavaScript defines seven built-in types, that we often call "primitives". These * `object` * `symbol` -- added in ES6! -The `typeof` operator inspects the type of the given value, and always returns one of seven string values (though, strangely, there's not an exact 1-to-1 match with the seven primitive types we just listed). +**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. ```js typeof undefined === "undefined"; // true @@ -58,15 +60,15 @@ typeof { life: 42 } === "object"; // true typeof Symbol() === "symbol"; // true ``` -These 6 listed types have values of the corresponding type and return a string of the same name, as shown. `Symbol` is a new data type as of ES6, and will be covered later. +These 6 listed types have values of the corresponding type and return a string value of the same name, as shown. `Symbol` is a new data type as of ES6, and will be covered in Chapter 3. -As you may have noticed, I excluded `null` from the above listing. It's *special* -- special in the sense that it's buggy. +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: ```js typeof null === "object"; // true ``` -It would have been nice (and correct!) if it returned `"null"`, but this original bug in JS has persisted for nearly 2 decades, and will likely never be fixed because there's too much existing web content that relies on its buggy behavior that "fixing" the bug would *create* "more bugs" and break a lot of web software. +It would have been nice (and correct!) if it returned `"null"`, but this original bug in JS has persisted for nearly 2 decades, and will likely never be fixed because there's too much existing web content that relies on its buggy behavior that "fixing" the bug would *create* more "bugs" and break a lot of web software. If you want to test for a `null` value using its type, you need a compound condition: @@ -78,13 +80,13 @@ var a = null; `null` is the only value that is "falsy" (aka false-like; see Chapter 4) but that also returns `"object"` from the `typeof` check. -So what's the seventh string value that `typeof` can return? And why is it not actually a top-level type? +So what's the seventh string value that `typeof` can return? ```js typeof function a(){ /* .. */ } === "function"; // true ``` -It's easy to think that `function` would be a top-level primitive type in JS, especially given this behavior of the `typeof` operator. However, if you read the spec, you'll see it's actually somewhat of 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. +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. The fact that functions are actually objects is quite useful. Most importantly, they can have properties. For example: diff --git a/types & grammar/ch2.md b/types & grammar/ch2.md index 0ea8a3807..9dc336045 100644 --- a/types & grammar/ch2.md +++ b/types & grammar/ch2.md @@ -846,11 +846,11 @@ d; // [1,2,3,4] Simple values (aka scalar primitives) are *always* assigned/passed by value-copy: `null`, `undefined`, `string`, `number`, `boolean`, and ES6's `symbol`. -Compound primitives -- `object` (including `array`s, and all boxed object wrappers -- see Chapter 3) and `function` -- *always* create a copy of the reference on assignment or passing. +Compound values -- `object` (including `array`s, and all boxed object wrappers -- see Chapter 3) and `function` -- *always* create a copy of the reference on assignment or passing. In the above snippet, because `2` is a scalar primitive, `a` holds one initial copy of that value, and `b` is assigned another *copy* of the value. When changing `b`, you are in no way changing the value in `a`. -But **both `c` and `d`** are seperate references to the same shared value `[1,2,3]`, which is a compound primitive. It's important to note that neither `c` nor `d` more "owns" the `[1,2,3]` value -- both are just equal peer references to the value. So, when using either reference to modify (`.push(4)`) the actual shared `array` value itself, it's affecting just the one shared value, and both references will reference the newly modified value `[1,2,3,4]`. +But **both `c` and `d`** are seperate references to the same shared value `[1,2,3]`, which is a compound value. It's important to note that neither `c` nor `d` more "owns" the `[1,2,3]` value -- both are just equal peer references to the value. So, when using either reference to modify (`.push(4)`) the actual shared `array` value itself, it's affecting just the one shared value, and both references will reference the newly modified value `[1,2,3,4]`. Since references point to the values themselves and not to the variables, you cannot use one reference to change where another reference is pointed: @@ -916,7 +916,7 @@ As you can see, `x.length = 0` and `x.push(4,5,6,7)` were not creating a new `ar Remember: you cannot directly control/override value-copy vs. reference -- those semantics are controlled entirely by the type of the underlying value. -To effectively pass a compound primitive (like an `array`) by value, you need to manually make a copy of it, so that the reference passing doesn't modify the original. For example: +To effectively pass a compound value (like an `array`) by value, you need to manually make a copy of it, so that the reference passing doesn't modify the original. For example: ```js foo( a.slice() ); @@ -924,7 +924,7 @@ foo( a.slice() ); `Array#slice(..)` with no parameters by default makes an entire (shallow) copy of the `array`. So, we pass in a reference only to the copied `array`, and thus `foo(..)` cannot affect the contents of `a`. -To do the reverse -- pass a scalar primitive value in a way where its value updates can be seen, kinda like a reference -- you have to wrap the value in another compound primitive (`object`, `array`, etc) which *can* be passed by reference: +To do the reverse -- pass a scalar primitive value in a way where its value updates can be seen, kinda like a reference -- you have to wrap the value in another compound value (`object`, `array`, etc) which *can* be passed by reference: ```js function foo(wrapper) { @@ -981,4 +981,4 @@ The `null` type has just one value: `null`, and likewise the `undefined` type ha `number`s include several special values, like `NaN` (supposedly "not a `number`", but really more appropriately "invalid `number`"); `+Infinity` and `-Infinity`; and `-0`. -Simple scalar primitives (`string`s, `number`s, etc.) are assigned/passed by value-copy, but compound primitives (`object`s, etc.) are assigned/passed by reference. References are **not** like references/pointers in other languages -- they're never pointed at other variables/references, only at the underlying values. +Simple scalar primitives (`string`s, `number`s, etc.) are assigned/passed by value-copy, but compound values (`object`s, etc.) are assigned/passed by reference. References are **not** like references/pointers in other languages -- they're never pointed at other variables/references, only at the underlying values. diff --git a/types & grammar/toc.md b/types & grammar/toc.md index f37b658f4..16e1973dc 100644 --- a/types & grammar/toc.md +++ b/types & grammar/toc.md @@ -5,7 +5,7 @@ * Preface * Chapter 1: Types * A Type By Any Other Name... - * Primitives + * Built-in Types * Values As Types * Chapter 2: Values * Strings