You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: getting-started/ch2.md
+367-2
Original file line number
Diff line number
Diff line change
@@ -11,9 +11,11 @@ This chapter is not an exhaustive reference on every bit of syntax of the JS lan
11
11
12
12
Instead, we're just going to survey some of the major topic-areas of the language. Our goal is to get a better *feel* for it, so that we can move forward writing our own programs with more confidence.
13
13
14
+
However, don't expect this chapter to be a quick read. It's long and there's plenty of detail to chew on. Take your time.
15
+
14
16
| TIP: |
15
17
| :--- |
16
-
| If you're still getting familiar with JS, I suggest you take your time working through this chapter. Take each section and ponder and explore the topic for awhile. Look through existing JS programs and compare what you see in them to the code and explanations (and opinions!) presented here. You will get a lot more out of the rest of the book series with a solid foundation of JS's *nature*. |
18
+
| If you're still getting familiar with JS, I suggest you reserve plenty of extra time to work through this chapter. Take each section and ponder and explore the topic for awhile. Look through existing JS programs and compare what you see in them to the code and explanations (and opinions!) presented here. You will get a lot more out of the rest of the book and series with a solid foundation of JS's *nature*. |
17
19
18
20
## Files As Programs
19
21
@@ -574,4 +576,367 @@ Since the `Boolean(..)` function always returns a value of type boolean, the `==
574
576
575
577
You just can't get away from coercions in JS comparisons. Buckle down and learn them.
576
578
577
-
## TODO
579
+
## Code Organization
580
+
581
+
Two major patterns for organizing code (data and behavior) are used broadly across the JS ecosystem: classes and modules. These patterns are not mutually exclusive; many programs can and do use both.
582
+
583
+
In some respects, these patterns are very different. But interestingly, in other ways, they're just different sides of the same coin. Being proficient in JS requires understanding both patterns and where they are appropriate (and not!).
584
+
585
+
### Classes
586
+
587
+
The terms "object oriented", "class oriented" and "classes" are all very loaded full of detail and nuance; they're not universal in definition.
588
+
589
+
We will use a common and somewhat traditional definition here, the one most likely familiar to those with backgrounds in "object oriented" languages like C++ and Java.
590
+
591
+
A class in a program is a definition of a "type" of custom data structure that includes both data and behaviors that operate on that data. Classes define how such a data structure works, but classes are not themselves concrete values. To get a concrete value that you can use in the program, a class must be *instantiated* (with the `new` keyword) one or more times.
592
+
593
+
Consider:
594
+
595
+
```js
596
+
classPage {
597
+
constructor(text) {
598
+
this.text= text;
599
+
}
600
+
601
+
print() {
602
+
console.log(this.text);
603
+
}
604
+
}
605
+
606
+
classNotebook {
607
+
constructor() {
608
+
this.pages= [];
609
+
}
610
+
611
+
addPage(text) {
612
+
var page =newPage(text);
613
+
this.pages.push(page);
614
+
}
615
+
616
+
print() {
617
+
for (let page ofthis.pages) {
618
+
page.print();
619
+
}
620
+
}
621
+
}
622
+
623
+
var mathNotes =newNotebook();
624
+
mathNotes.addPage("Arithmetic: + - * / ...");
625
+
mathNotes.addPage("Trigonometry: sin cos tan ...");
626
+
627
+
mathNotes.print();
628
+
// ..
629
+
```
630
+
631
+
In the `Page` class, the data is a string of text stored in a `this.text` member property. The behavior is `print()`, a method that dumps the text to the console.
632
+
633
+
For the `Notebook` class, the data is an array of `Page` instances. The behavior is `addPage(..)`, a method that instantiates new `Page` pages and adds them to the list, as well as `print()` which prints out all the pages in the notebook.
634
+
635
+
The statement `mathNotes = new Notebook()` creates an instance of the `Notebook` class, and `page = new Page(text)` is where instances of the `Page` class are created.
636
+
637
+
Behavior (methods) can only be called on instances (not the classes themselves), such as `mathNotes.addPage(..)` and `page.print()`.
638
+
639
+
The `class` mechanism allows packaging data (`text` and `pages`) to be organized together with their behaviors (`addPage(..)`, `print()`). The same program could have been built without any `class` definitions, but it would likely have been much less organized, harder to read and reason about, and more susceptible to bugs and subpar maintenance.
640
+
641
+
#### Class Inheritance
642
+
643
+
Another aspect inherent to traditional "class oriented" design, though a bit less commonly used in JS, is "inheritance" (and "polymorphism"). Consider:
644
+
645
+
```js
646
+
classPublication {
647
+
constructor(title,author,pubDate) {
648
+
this.title= title;
649
+
this.author= author;
650
+
this.pubDate= pubDate;
651
+
}
652
+
653
+
print() {
654
+
console.log(`
655
+
Title: ${this.title}
656
+
By: ${this.author}
657
+
${this.pubDate}
658
+
`);
659
+
}
660
+
}
661
+
```
662
+
663
+
This `Publication` class defines a set of common behavior that any publication might need.
664
+
665
+
Now let's consider more specific types of publication, like `Book` and `BlogPost`:
666
+
667
+
```js
668
+
classBookextendsPublication {
669
+
constructor(bookDetails) {
670
+
super(
671
+
bookDetails.title,
672
+
bookDetails.author,
673
+
bookDetails.publishedOn
674
+
);
675
+
this.publisher=bookDetails.publisher;
676
+
this.ISBN=bookDetails.ISBN;
677
+
}
678
+
679
+
print() {
680
+
super.print();
681
+
console.log(`
682
+
Published By: ${this.publisher}
683
+
ISBN: ${this.ISBN}
684
+
`);
685
+
}
686
+
}
687
+
688
+
classBlogPostextendsPublication {
689
+
constructor(title,author,pubDate,URL) {
690
+
super(title,author,pubDate);
691
+
this.URL=URL;
692
+
}
693
+
694
+
print() {
695
+
super.print();
696
+
console.log(this.URL);
697
+
}
698
+
}
699
+
```
700
+
701
+
Both `Book` and `BlogPost` use the `extends` clause to *extend* the general definition of `Publication` to include additional behavior. The `super(..)` call in each constructor delegates to the parent `Publication` class's constructor for its initialization work, and then they do more specific things according to their respective publication type (aka, "sub-class" or "child class").
702
+
703
+
Now consider using these child classes:
704
+
705
+
```js
706
+
varYDKJS=newBook({
707
+
title:"You Don't Know JS",
708
+
author:"Kyle Simpson",
709
+
publishedOn:"June 2014",
710
+
publisher:"O'reilly",
711
+
ISBN:"123456-789"
712
+
});
713
+
714
+
YDKJS.print();
715
+
// Title: You Don't Know JS
716
+
// By: Kyle Simpson
717
+
// June 2014
718
+
// Published By: O'reilly
719
+
// ISBN: 123456-789
720
+
721
+
var forAgainstLet =newBlogPost(
722
+
"For and against let",
723
+
"Kyle Simpson",
724
+
"October 27, 2014",
725
+
"https://davidwalsh.name/for-and-against-let"
726
+
);
727
+
728
+
forAgainstLet.print();
729
+
// Title: For and against let
730
+
// By: Kyle Simpson
731
+
// October 27, 2014
732
+
// https://davidwalsh.name/for-and-against-let
733
+
```
734
+
735
+
Notice that both child class instances have a `print()` method, which was an override of the *inherited*`print()` method from the parent `Publication` class. Each of those overridden child class `print()` methods call `super.print()` to invoke the inherited version of the `print()` method.
736
+
737
+
The fact that both the inherited and overriden methods can have the same name and co-exist is called *polymorphism*.
738
+
739
+
Inheritance is a powerful tool for organizing data/behavior in separate logical units (classes), but allowing the child class to cooperate with the parent by accessing/using its behavior and data.
740
+
741
+
### Modules
742
+
743
+
The module pattern has essentially the same goal as the class pattern, which is to group data and behavior together into logical units. Also like classes, modules can "include" or "access" the data and behaviors of other modules, for cooperation sake.
744
+
745
+
But modules have some important differences from classes. Most notably, the syntax is entirely different.
746
+
747
+
#### Classic Modules
748
+
749
+
ES6 added a module syntax form, which we'll look at in a moment. But from the early days of JS, modules was an important and common pattern that was leveraged in countless JS programs, even without a dedicated syntax.
750
+
751
+
The key hallmarks of a *classic module* are an outer function (that runs at least once), which returns an "instance" of the module with one or more functions exposed that can operate on the module instance's internal (hidden) data.
752
+
753
+
Because a module of this form is *just a function*, and calling it produces an "instance" of the module, another description for these functions is "module factories".
754
+
755
+
Consider the classic module form of the earlier `Publication`, `Book`, and `BlogPost` classes:
756
+
757
+
```js
758
+
functionPublication(title,author,pubDate) {
759
+
var publicAPI = {
760
+
print() {
761
+
console.log(`
762
+
Title: ${title}
763
+
By: ${author}
764
+
${pubDate}
765
+
`);
766
+
}
767
+
};
768
+
769
+
return publicAPI;
770
+
}
771
+
772
+
functionBook(bookDetails) {
773
+
var pub =Publication(
774
+
bookDetails.title,
775
+
bookDetails.author,
776
+
bookDetails.publishedOn
777
+
);
778
+
779
+
var publicAPI = {
780
+
print() {
781
+
pub.print();
782
+
console.log(`
783
+
Published By: ${bookDetails.publisher}
784
+
ISBN: ${bookDetails.ISBN}
785
+
`);
786
+
}
787
+
};
788
+
789
+
return publicAPI;
790
+
}
791
+
792
+
functionBlogPost(title,author,pubDate,URL) {
793
+
var pub =Publication(title,author,pubDate);
794
+
795
+
var publicAPI = {
796
+
print() {
797
+
pub.print();
798
+
console.log(URL);
799
+
}
800
+
};
801
+
802
+
return publicAPI;
803
+
}
804
+
```
805
+
806
+
Comparing these forms to the `class` forms, there are more similarities than differences.
807
+
808
+
The `class` form stores methods and data on an object instance, which much be accessed with the `this.` prefix. With modules, the methods and data are accessed as identifier variables in scope, without any `this.` prefix.
809
+
810
+
With `class`, the "API" of an instance is implicit in the class definition -- also, all data and methods are public. With the module factory function, you explicitly create and return an object with any publicly exposed methods, and any data or other unreferenced methods remain private inside the factory function.
811
+
812
+
| NOTE: |
813
+
| :--- |
814
+
| There are other variations to this factory function form that are quite common across JS, even in 2019; you may run across these forms in different JS programs: AMD ("Asynchronous Module Definition"), UMD ("Universal Module Definition"), and CommonJS (classic Node.js style modules). The variations are minor, though. All of these forms rely on the same basic principles. |
815
+
816
+
Consider also the usage (aka, "instantiation") of these module factory functions:
817
+
818
+
```js
819
+
varYDKJS=Book({
820
+
title:"You Don't Know JS",
821
+
author:"Kyle Simpson",
822
+
publishedOn:"June 2014",
823
+
publisher:"O'reilly",
824
+
ISBN:"123456-789"
825
+
});
826
+
827
+
YDKJS.print();
828
+
// Title: You Don't Know JS
829
+
// By: Kyle Simpson
830
+
// June 2014
831
+
// Published By: O'reilly
832
+
// ISBN: 123456-789
833
+
834
+
var forAgainstLet =BlogPost(
835
+
"For and against let",
836
+
"Kyle Simpson",
837
+
"October 27, 2014",
838
+
"https://davidwalsh.name/for-and-against-let"
839
+
);
840
+
841
+
forAgainstLet.print();
842
+
// Title: For and against let
843
+
// By: Kyle Simpson
844
+
// October 27, 2014
845
+
// https://davidwalsh.name/for-and-against-let
846
+
```
847
+
848
+
The only observable difference here is the lack of using `new`, calling the module factories as normal functions.
849
+
850
+
#### ES6 Modules
851
+
852
+
ES6 modules are meant to serve much the same spirit and purpose as the existing *classic modules* just described, especially taking into account important variations and use-cases from AMD, UMD, and CommonJS.
853
+
854
+
The implementation approach does however differ significantly.
855
+
856
+
First, there's no wrapping function to *define* a module. The wrapping context is a file. ES6 modules are always file-based; one file, one module.
857
+
858
+
Second, you don't interact with a module's "API" explicitly, but rather use the `export` keyword to add a variable or method to its public API definition. If something is defined in a module but not `export`ed, then it stays hidden (just as with *classic modules*).
859
+
860
+
Third, and maybe most noticeably different from previously discussed patterns, you don't "instantiate" an ES6 module, you just `import` it to use its single instance. ES6 modules are, in effect, "singletons", in that there's only one instance ever created, at first `import` in your program, and all other `import`s just receive a reference to that same single instance. If your module needs to support multiple instantiations, you have to provide a *classic module* style factory function on your ES6 module definition for that purpose.
861
+
862
+
In our running example, we do assume multiple-instantation, so these following snippets will mix both ES6 modules and *classic modules*:.
863
+
864
+
Consider the file `publication.js`:
865
+
866
+
```js
867
+
functionprintDetails(title,author,pubDate) {
868
+
console.log(`
869
+
Title: ${title}
870
+
By: ${author}
871
+
${pubDate}
872
+
`);
873
+
}
874
+
875
+
exportfunctioncreate(title,author,pubDate) {
876
+
var publicAPI = {
877
+
print() {
878
+
printDetails(title,author,pubDate);
879
+
}
880
+
};
881
+
882
+
return publicAPI;
883
+
}
884
+
```
885
+
886
+
To import and use this module, from another ES6 module like `blogpost.js`:
| The `as createBlogPost` clause in the `import` statement above 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(..)`. |
933
+
934
+
As shown, ES6 modules can utilize *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 ES6 modules at that point, I'd recommend sticking with *classic modules* instead of `class`.
935
+
936
+
If your module only needs a single instance, you can skip the extra layers of complexity: `export` its public methods directly.
937
+
938
+
## Before You Go On
939
+
940
+
As promised at the top of this chapter, we just glanced over a wide surface area of the main parts of the JS language. Even with this "brief" survey of JS, there's a ton of details herein you should carefully consider and ensure you are comfortable with. I suggest re-reading this chapter, maybe a few times.
941
+
942
+
In the next chapter, we're going to dive deeper into some important aspects of how JS works. Make sure you take your time with this chapter's material before you proceed.
0 commit comments