mirror of
https://github.com/getify/You-Dont-Know-JS.git
synced 2025-04-09 00:00:36 +08:00
get started: tweaks for typesetting
This commit is contained in:
parent
be15f0ec19
commit
90c4635f84
@ -276,7 +276,7 @@ mathClass.welcome();
|
||||
// Welcome, students!
|
||||
```
|
||||
|
||||
Here, a `mathClass` object is linked via its prototype to a `Classroom` object. Through this link, the `mathClass.welcome()` function call is delegated to the method defined on `Classroom`.
|
||||
Here, a `mathClass` object is linked via its prototype to a `Classroom` object. Through this linkage, the function call `mathClass.welcome()` is delegated to the method defined on `Classroom`.
|
||||
|
||||
The prototypal class pattern would have labeled this delegation behavior "inheritance," and alternatively have defined it (with the same behavior) as:
|
||||
|
||||
@ -297,11 +297,11 @@ mathClass.welcome();
|
||||
|
||||
All functions by default reference an empty object at a property named `prototype`. Despite the confusing naming, this is **not** the function's *prototype* (where the function is prototype linked to), but rather the prototype object to *link to* when other objects are created by calling the function with `new`.
|
||||
|
||||
We add a `welcome` property to that empty `Classroom.prototype` object, pointing at a `hello()` function.
|
||||
We add a `welcome` property on that empty object (called `Classroom.prototype`), pointing at the `hello()` function.
|
||||
|
||||
Then `new Classroom()` creates a new object (assigned to `mathClass`), and prototype links it to the existing `Classroom.prototype` object.
|
||||
|
||||
Though `mathClass` does not have a `welcome()` property/function, it successfully delegates to `Classroom.prototype.welcome()`.
|
||||
Though `mathClass` does not have a `welcome()` property/function, it successfully delegates to the function `Classroom.prototype.welcome()`.
|
||||
|
||||
This "prototypal class" pattern is now strongly discouraged, in favor of using ES6's `class` mechanism:
|
||||
|
||||
|
@ -73,10 +73,14 @@ function randMax(max) {
|
||||
}
|
||||
|
||||
var reel = {
|
||||
symbols: [ "♠", "♥", "♦", "♣", "☺", "★", "☾", "☀" ],
|
||||
symbols: [
|
||||
"♠", "♥", "♦", "♣", "☺", "★", "☾", "☀"
|
||||
],
|
||||
spin() {
|
||||
if (this.position == null) {
|
||||
this.position = randMax(this.symbols.length - 1);
|
||||
this.position = randMax(
|
||||
this.symbols.length - 1
|
||||
);
|
||||
}
|
||||
this.position = (
|
||||
this.position + 100 + randMax(100)
|
||||
@ -84,7 +88,9 @@ var reel = {
|
||||
},
|
||||
display() {
|
||||
if (this.position == null) {
|
||||
this.position = randMax(this.symbols.length - 1);
|
||||
this.position = randMax(
|
||||
this.symbols.length - 1
|
||||
);
|
||||
}
|
||||
return this.symbols[this.position];
|
||||
}
|
||||
@ -150,14 +156,20 @@ function scheduleMeeting(startTime,durationMinutes) {
|
||||
typeof meetingStartHour == "string" &&
|
||||
typeof meetingStartMinutes == "string"
|
||||
) {
|
||||
let durationHours = Math.floor(durationMinutes / 60);
|
||||
durationMinutes = durationMinutes - (durationHours * 60);
|
||||
let meetingEndHour = Number(meetingStartHour) + durationHours;
|
||||
let meetingEndMinutes = Number(meetingStartMinutes) + durationMinutes;
|
||||
let durationHours =
|
||||
Math.floor(durationMinutes / 60);
|
||||
durationMinutes =
|
||||
durationMinutes - (durationHours * 60);
|
||||
let meetingEndHour =
|
||||
Number(meetingStartHour) + durationHours;
|
||||
let meetingEndMinutes =
|
||||
Number(meetingStartMinutes) +
|
||||
durationMinutes;
|
||||
|
||||
if (meetingEndMinutes >= 60) {
|
||||
meetingEndHour = meetingEndHour + 1;
|
||||
meetingEndMinutes = meetingEndMinutes - 60;
|
||||
meetingEndMinutes =
|
||||
meetingEndMinutes - 60;
|
||||
}
|
||||
|
||||
// re-compose fully-qualified time strings
|
||||
@ -174,7 +186,7 @@ function scheduleMeeting(startTime,durationMinutes) {
|
||||
}`;
|
||||
|
||||
// NOTE: since expressions are all strings,
|
||||
// comparisons here are alphabetic, but that's
|
||||
// comparisons here are alphabetic, but it's
|
||||
// safe here since they're fully qualified
|
||||
// time strings (ie, "07:15" < "07:30")
|
||||
return (
|
||||
@ -245,10 +257,14 @@ function randMax(max) {
|
||||
}
|
||||
|
||||
var reel = {
|
||||
symbols: [ "♠", "♥", "♦", "♣", "☺", "★", "☾", "☀" ],
|
||||
symbols: [
|
||||
"♠", "♥", "♦", "♣", "☺", "★", "☾", "☀"
|
||||
],
|
||||
spin() {
|
||||
if (this.position == null) {
|
||||
this.position = randMax(this.symbols.length - 1);
|
||||
this.position = randMax(
|
||||
this.symbols.length - 1
|
||||
);
|
||||
}
|
||||
this.position = (
|
||||
this.position + 100 + randMax(100)
|
||||
@ -256,7 +272,9 @@ var reel = {
|
||||
},
|
||||
display() {
|
||||
if (this.position == null) {
|
||||
this.position = randMax(this.symbols.length - 1);
|
||||
this.position = randMax(
|
||||
this.symbols.length - 1
|
||||
);
|
||||
}
|
||||
return this.symbols[this.position];
|
||||
}
|
||||
@ -277,14 +295,20 @@ var slotMachine = {
|
||||
var lines = [];
|
||||
|
||||
// display all 3 lines on the slot machine
|
||||
for (let linePos = -1; linePos <= 1; linePos++) {
|
||||
let line = this.reels.map(function getSlot(reel){
|
||||
var slot = Object.create(reel);
|
||||
slot.position = (
|
||||
reel.symbols.length + reel.position + linePos
|
||||
) % reel.symbols.length;
|
||||
return reel.display.call(slot);
|
||||
});
|
||||
for (
|
||||
let linePos = -1; linePos <= 1; linePos++
|
||||
) {
|
||||
let line = this.reels.map(
|
||||
function getSlot(reel){
|
||||
var slot = Object.create(reel);
|
||||
slot.position = (
|
||||
reel.symbols.length +
|
||||
reel.position +
|
||||
linePos
|
||||
) % reel.symbols.length;
|
||||
return reel.display.call(slot);
|
||||
}
|
||||
);
|
||||
lines.push(line.join(" | "));
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ The only way multiple standalone .js files act as a single program is by sharing
|
||||
|
||||
Since ES6, JS has also supported a module format in addition to the typical standalone JS program format. Modules are also file-based. If a file is loaded via module-loading mechanism such as an `import` statement or a `<script type=module>` tag, all its code is treated as a single module.
|
||||
|
||||
Though you wouldn't typically think about a module—basically, a collection of state and publicly exposed methods to operate on that state—as a standalone program, JS does in fact still treat each module separately. Similar to how "global scope" allows standalone files to mix together at runtime, importing a module into another allows runtime interoperation between them.
|
||||
Though you wouldn't typically think about a module—a collection of state and publicly exposed methods to operate on that state—as a standalone program, JS does in fact still treat each module separately. Similar to how "global scope" allows standalone files to mix together at runtime, importing a module into another allows runtime interoperation between them.
|
||||
|
||||
Regardless of which code organization pattern (and loading mechanism) is used for a file (standalone or module), you should still think of each file as its own (mini) program, which may then cooperate with other (mini) programs to perform the functions of your overall application.
|
||||
|
||||
@ -70,7 +70,9 @@ Assuming this program has already defined a variable `firstName` with the string
|
||||
The back-tick `` ` ``-delimited string can be used without including interpolated expressions, but that defeats the whole purpose of that alternate string literal syntax:
|
||||
|
||||
```js
|
||||
console.log(`Am I confusing you by omitting interpolation?`);
|
||||
console.log(
|
||||
`Am I confusing you by omitting interpolation?`
|
||||
);
|
||||
// Am I confusing you by omitting interpolation?
|
||||
```
|
||||
|
||||
@ -167,10 +169,10 @@ typeof 42; // "number"
|
||||
typeof "abc"; // "string"
|
||||
typeof true; // "boolean"
|
||||
typeof undefined; // "undefined"
|
||||
typeof null; // "object" -- oops, JS bug!
|
||||
typeof null; // "object" -- oops, bug!
|
||||
typeof { "a": 1 }; // "object"
|
||||
typeof [1,2,3]; // "object"
|
||||
typeof function Hello(){}; // "function"
|
||||
typeof function hello(){}; // "function"
|
||||
```
|
||||
|
||||
| WARNING: |
|
||||
@ -252,7 +254,9 @@ The `myBirthday` constant is not allowed to be re-assigned.
|
||||
`const` declared variables are not "unchangeable", they just cannot be re-assigned. It's ill-advised to use `const` with object values, because those values can still be changed even though the variable can't be re-assigned. This leads to potential confusion down the line, so I think it's wise to avoid situations like:
|
||||
|
||||
```js
|
||||
const actors = [ "Morgan Freeman", "Jennifer Aniston" ];
|
||||
const actors = [
|
||||
"Morgan Freeman", "Jennifer Aniston"
|
||||
];
|
||||
|
||||
actors[2] = "Tom Cruise"; // OK :(
|
||||
|
||||
@ -620,7 +624,7 @@ class Book extends Publication {
|
||||
print() {
|
||||
super.print();
|
||||
console.log(`
|
||||
Published By: ${ this.publisher }
|
||||
Publisher: ${ this.publisher }
|
||||
ISBN: ${ this.ISBN }
|
||||
`);
|
||||
}
|
||||
@ -648,7 +652,7 @@ var YDKJS = new Book({
|
||||
title: "You Don't Know JS",
|
||||
author: "Kyle Simpson",
|
||||
publishedOn: "June 2014",
|
||||
publisher: "O'reilly",
|
||||
publisher: "O'Reilly",
|
||||
ISBN: "123456-789"
|
||||
});
|
||||
|
||||
@ -656,7 +660,7 @@ YDKJS.print();
|
||||
// Title: You Don't Know JS
|
||||
// By: Kyle Simpson
|
||||
// June 2014
|
||||
// Published By: O'reilly
|
||||
// Publisher: O'Reilly
|
||||
// ISBN: 123456-789
|
||||
|
||||
var forAgainstLet = new BlogPost(
|
||||
@ -721,7 +725,7 @@ function Book(bookDetails) {
|
||||
print() {
|
||||
pub.print();
|
||||
console.log(`
|
||||
Published By: ${ bookDetails.publisher }
|
||||
Publisher: ${ bookDetails.publisher }
|
||||
ISBN: ${ bookDetails.ISBN }
|
||||
`);
|
||||
}
|
||||
@ -759,7 +763,7 @@ var YDKJS = Book({
|
||||
title: "You Don't Know JS",
|
||||
author: "Kyle Simpson",
|
||||
publishedOn: "June 2014",
|
||||
publisher: "O'reilly",
|
||||
publisher: "O'Reilly",
|
||||
ISBN: "123456-789"
|
||||
});
|
||||
|
||||
@ -767,7 +771,7 @@ YDKJS.print();
|
||||
// Title: You Don't Know JS
|
||||
// By: Kyle Simpson
|
||||
// June 2014
|
||||
// Published By: O'reilly
|
||||
// Publisher: O'Reilly
|
||||
// ISBN: 123456-789
|
||||
|
||||
var forAgainstLet = BlogPost(
|
||||
@ -848,9 +852,9 @@ export function create(title,author,pubDate,URL) {
|
||||
And finally, to use this module, we import into another ES module like `main.js`:
|
||||
|
||||
```js
|
||||
import { create as createBlogPost } from "blogpost.js";
|
||||
import { create as newBlogPost } from "blogpost.js";
|
||||
|
||||
var forAgainstLet = createBlogPost(
|
||||
var forAgainstLet = newBlogPost(
|
||||
"For and against let",
|
||||
"Kyle Simpson",
|
||||
"October 27, 2014",
|
||||
@ -866,7 +870,7 @@ forAgainstLet.print();
|
||||
|
||||
| NOTE: |
|
||||
| :--- |
|
||||
| The `as createBlogPost` clause in the `import` statement is optional; if omitted, a top-level function just named `create(..)` would be imported. In this case, I'm renaming it for readability sake; its more generic factory name of `create(..)` becomes more semantically descriptive of its purpose as `createBlogPost(..)`. |
|
||||
| The `as newBlogPost` clause in the `import` statement is optional; if omitted, a top-level function just named `create(..)` would be imported. In this case, I'm renaming it for readability sake; its more generic factory name of `create(..)` becomes more semantically descriptive of its purpose as `newBlogPost(..)`. |
|
||||
|
||||
As shown, ES modules can use *classic modules* internally if they need to support multiple-instantiation. Alternatively, we could have exposed a `class` from our module instead of a `create(..)` factory function, with generally the same outcome. However, since you're already using ESM at that point, I'd recommend sticking with *classic modules* instead of `class`.
|
||||
|
||||
|
@ -235,12 +235,14 @@ Closure is most common when working with asynchronous code, such as with callbac
|
||||
```js
|
||||
function getSomeData(url) {
|
||||
ajax(url,function onResponse(resp){
|
||||
console.log(`Response (from ${ url }): ${ resp }`);
|
||||
console.log(
|
||||
`Response (from ${ url }): ${ resp }`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
getSomeData("https://some.url/wherever");
|
||||
// Response (from https://some.url/wherever): ..whatever..
|
||||
// Response (from https://some.url/wherever): ...
|
||||
```
|
||||
|
||||
The inner function `onResponse(..)` is closed over `url`, and thus preserves and remembers it until the Ajax call returns and executes `onResponse(..)`. Even though `getSomeData(..)` finishes right away, the `url` parameter variable is kept alive in the closure for as long as needed.
|
||||
@ -249,7 +251,7 @@ It's not necessary that the outer scope be a function—it usually is, but not a
|
||||
|
||||
```js
|
||||
for (let [idx,btn] of buttons.entries()) {
|
||||
btn.addEventListener("click",function onClick(evt){
|
||||
btn.addEventListener("click",function onClick(){
|
||||
console.log(`Clicked on button (${ idx })!`);
|
||||
});
|
||||
}
|
||||
@ -283,7 +285,7 @@ Consider:
|
||||
function classroom(teacher) {
|
||||
return function study() {
|
||||
console.log(
|
||||
`${ teacher } wants you to study ${ this.topic }`
|
||||
`${ teacher } says to study ${ this.topic }`
|
||||
);
|
||||
};
|
||||
}
|
||||
@ -297,11 +299,11 @@ The outer `classroom(..)` function makes no reference to a `this` keyword, so it
|
||||
| :--- |
|
||||
| `study()` is also closed over the `teacher` variable from its outer scope. |
|
||||
|
||||
The inner `study()` function is returned from `classroom("Kyle")` and assigned to a variable called `assignment`. So how can `assignment()` (aka `study()`) be called?
|
||||
The inner `study()` function returned by `classroom("Kyle")` is assigned to a variable called `assignment`. So how can `assignment()` (aka `study()`) be called?
|
||||
|
||||
```js
|
||||
assignment();
|
||||
// Kyle wants you to study undefined -- Oops :(
|
||||
// Kyle says to study undefined -- Oops :(
|
||||
```
|
||||
|
||||
In this snippet, we call `assignment()` as a plain, normal function, without providing it any *execution context*.
|
||||
@ -317,7 +319,7 @@ var homework = {
|
||||
};
|
||||
|
||||
homework.assignment();
|
||||
// Kyle wants you to study JS
|
||||
// Kyle says to study JS
|
||||
```
|
||||
|
||||
A copy of the `assignment` function reference is set as a property on the `homework` object, and then it's called as `homework.assignment()`. That means the `this` for that function call will be the `homework` object. Hence, `this.topic` resolves to `"JS"`.
|
||||
@ -330,10 +332,10 @@ var otherHomework = {
|
||||
};
|
||||
|
||||
assignment.call(otherHomework);
|
||||
// Kyle wants you to study Math
|
||||
// Kyle says to study Math
|
||||
```
|
||||
|
||||
A third way to invoke a function is with the `call(..)` method, which takes an object (`otherHomework` here) to use for setting the `this` reference for the function call. `this.topic` thus resolves to `"Math"`.
|
||||
A third way to invoke a function is with the `call(..)` method, which takes an object (`otherHomework` here) to use for setting the `this` reference for the function call. `this.topic` resolves to `"Math"`.
|
||||
|
||||
The same context-aware function invoked three different ways, gives different answers each time for what object `this` will reference.
|
||||
|
||||
@ -463,7 +465,7 @@ The two objects `jsHomework` and `mathHomework` each prototype link to the singl
|
||||
<br><br>
|
||||
</figure>
|
||||
|
||||
`jsHomework.study()` delegates to `homework.study()`, but its `this` (in `this.topic`) for that execution resolves to `jsHomework` because of how the function is called, so `this.topic` is `"JS"`. Similarly for `mathHomework.study()` delegating to `homework.study()` but still resolving `this` to `mathHomework`, and thus `this.topic` as `"Math"`.
|
||||
`jsHomework.study()` delegates to `homework.study()`, but its `this` (`this.topic`) for that execution resolves to `jsHomework` because of how the function is called, so `this.topic` is `"JS"`. Similarly for `mathHomework.study()` delegating to `homework.study()` but still resolving `this` to `mathHomework`, and thus `this.topic` as `"Math"`.
|
||||
|
||||
The preceding code snippet would be far less useful if `this` was resolved to `homework`. Yet, in many other languages, it would seem `this` would be `homework` because the `study()` method is indeed defined on `homework`.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user