-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathCheatSheet.org
More file actions
2155 lines (1720 loc) · 75 KB
/
Copy pathCheatSheet.org
File metadata and controls
2155 lines (1720 loc) · 75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#+TITLE: JavaScript CheatSheet
# +SUBTITLE: ---Reference Sheet for “What I'm Currently Learning”---
#+MACRO: blurb Quick reference to the tremendously accessible high-level language of the web ^_^
#+AUTHOR: [[http://www.cas.mcmaster.ca/~alhassm/][Musa Al-hassy]]
#+EMAIL: alhassy@gmail.com
#+OPTIONS: toc:nil
#+INCLUDE: CheatSheet/CheatSheetSetup.org
#+PROPERTY: header-args:js :results output :prologue const assert = require('assert');
#+TODO: Todo | spacing LaTeX
:Setup:
#+begin_src emacs-lisp
(use-package ob-js
:ensure t
:config
(add-to-list 'org-babel-load-languages '(js . t))
(org-babel-do-load-languages 'org-babel-load-languages org-babel-load-languages)
(add-to-list 'org-babel-tangle-lang-exts '("js" . "js"))
(system-packages-ensure "node"))
;; use “:results output” for js blocks!
;; Seamless use of babel: No confirmation upon execution.
;; Downside: Could accidentally evaluate harmful code.
(setq org-confirm-babel-evaluate nil)
(load-file (let ((coding-system-for-read 'utf-8))
(shell-command-to-string "/usr/local/bin/agda-mode locate")))
;; Unicode Input via Agda Input:2 ends here
;; [[file:~/.emacs.d/init.org::*Unicode Input via Agda Input][Unicode Input via Agda Input:3]]
(use-package agda-input
:ensure nil ;; I have it locally.
:demand t
:hook ((text-mode prog-mode) . (lambda () (set-input-method "Agda")))
:custom (default-input-method "Agda"))
;; Now C-\ or M-x toggle-input-method turn it on and offers
;; Unicode Input via Agda Input:3 ends here
#+end_src
Test:
#+BEGIN_SRC js
console.log('ola');
#+END_SRC
#+RESULTS:
: ola
:End:
* LaTeX Extra, Local, Setup :ignore:
# Empty by default.
#+LATEX_HEADER: \def\cheatsheeturl{https://github.com/alhassy/JavaScriptCheatSheet}
# The following are the defaults & may be omitted.
#+LATEX_HEADER: \def\cheatsheetcols{2}
#+LATEX_HEADER: \landscapetrue
#+LATEX_HEADER: \def\cheatsheetitemsep{-0.5em}
# Example unicode declarations; see section “unicode” below.
#+LATEX_HEADER: \newunicodechar{𝑻}{\ensuremath{T}}
#+LATEX_HEADER: \newunicodechar{⊕}{\ensuremath{\oplus}}
#+LATEX_HEADER: \newunicodechar{≈}{\ensuremath{\approx}}
#+LATEX_HEADER: \newunicodechar{𝓍}{\ensuremath{x}}
#+LATEX_HEADER: \newunicodechar{𝓎}{\ensuremath{y}}
#+LATEX_HEADER: \newunicodechar{𝓈}{\ensuremath{s}}
#+LATEX_HEADER: \newunicodechar{𝓌}{\ensuremath{w}}
#+LATEX_HEADER: \newunicodechar{𝓋}{\ensuremath{v}}
#+LATEX_HEADER: \newunicodechar{ʸ}{\ensuremath{^y}}
#+LATEX_HEADER: \newunicodechar{₋}{\ensuremath{_{-}}}
#+LATEX_HEADER: \newunicodechar{₁}{\ensuremath{_1}}
* Contents :TOC_2:QUOTE:ignore:
#+BEGIN_QUOTE
- [[#extra-local-setup][Extra, Local, Setup]]
- [[#intro][Intro]]
- [[#negative-space][Negative space]]
- [[#types][Types]]
- [[#negative-space-1][Negative space]]
- [[#variable-bindings][Variable Bindings]]
- [[#scope-and-statements][Scope and Statements]]
- [[#negative-space-2][Negative space]]
- [[#arithmetic][Arithmetic]]
- [[#booleans][Booleans]]
- [[#empty-values][Empty Values]]
- [[#strings][Strings]]
- [[#string-methods][String Methods]]
- [[#method-names-shared-with-arrays][Method Names Shared with Arrays]]
- [[#treating-strings-as-arrays][Treating Strings as Arrays]]
- [[#arrays][Arrays]]
- [[#array-methods][Array methods]]
- [[#functions][Functions]]
- [[#intro-1][Intro]]
- [[#default-values][Default Values]]
- [[#rest-parameters][Rest Parameters]]
- [[#higher-order-functions][Higher-order Functions]]
- [[#negative-space-3][Negative space]]
- [[#destructuring-and-the-spread-operator][Destructuring and the “spread” Operator]]
- [[#objects][Objects]]
- [[#object-creation][Object Creation]]
- [[#getters-and-setters----get-set][Getters and Setters -- ~get, set~]]
- [[#dictionries-or-maps][Dictionries or maps]]
- [[#negative-space-4][Negative space]]
- [[#the-this-keyword][The ~this~ Keyword]]
- [[#call][~Call~]]
- [[#the-this-of-parent-scopes][The ~this~ of Parent Scopes]]
- [[#object-oriented-programming][Object-Oriented Programming]]
- [[#intro-2][Intro]]
- [[#adding-new-methods-or-overriding-methods][Adding New Methods or Overriding Methods]]
- [[#inheritance-and-null-parents][Inheritance and Null Parents]]
- [[#1-prototype-example][⟨1⟩ Prototype Example]]
- [[#2-manual-constructor-functions][⟨2⟩ Manual Constructor Functions]]
- [[#3-constructor-functions-with-new][⟨3⟩ Constructor Functions with ~new~]]
- [[#4-class-notation][⟨4⟩ ~class~ Notation]]
- [[#the-iterator-interface][The Iterator Interface]]
- [[#negative-space-5][Negative space]]
- [[#javascript-and-the-browser][JavaScript and the Browser]]
- [[#large-scripts][Large Scripts]]
- [[#break][Break]]
- [[#reads][Reads]]
- [[#some-fun-stuff-_][Some Fun Stuff ^_^]]
#+END_QUOTE
* Intro :ignore:
#+latex: \vspace{1em}
JavaScript is what everyone calls the language, but that name is [[https://softwareengineering.stackexchange.com/questions/135905/legal-ramifications-of-use-of-the-javascript-trademark][trademarked]] (by
Oracle, which inherited the trademark from Sun). Therefore, the official name of
JavaScript is ECMAScript. The “ECMA” in “ECMAScript” comes from the organisation
that hosts the primary standard, the European Computer Manufacturers
Association.
# JavaScript initially did not have exceptions, and so it simply “fails silently”
# by giving error values, ~0 / 0 ≈ NaN~, rather than raising exceptions.
As the programming language of browsers, it is remarkably error-tolerant. It
simply “fails silently” by giving error values such as ~undefined~ when things
are not there or ~0 / 0 ≈ NaN~ for nonsensical numeric expressions.
By accident, there are two (mostly) /interchangeable/ values ~null~ and
~undefined~ that denote the absence of a meaningful value. Many operations that
don't produce meaningful values yield ~undefined~ simply because they have to
yield /some/ value. [[https://funcall.blogspot.com/2007/11/in-kingdom-of-nouns.html?m=1][Here]] is a neat story about null.
* COMMENT This is how we do it ---Assertions
An [[https://nodejs.org/api/assert.html#assert_strict_mode][assertion]] describes what the result of a computation is expected to look like
and throws an exception if those expectations are not met.
#+BEGIN_SRC js
const assert = require('assert');
assert.ok(!null)
// Printing a value to standard out (another method call)
console.log('Hello!');
#+END_SRC
#+RESULTS:
: Hello!
* Negative space :ignore:
#+latex: \vspace{-1em}
* Types
JavaScript considers types only when actually running the program, and even
there often tries to implicitly convert values to the type it expects.
+ ~typeof~ gives a string value naming the type of its argument.
+ The functions ~Number, String, Boolean~ try to convert values into those
types.
#+BEGIN_SRC js
console.log(typeof 4.5, typeof '4.5', typeof true)
// ⇒ number string boolean
console.log(8 * null // Multiplication needs numbers so null ↦ 0
, 'five' * 2 // 'five' is not a number, so 'five' ↦ NaN
, '5' - 1 // Subtraction needs numbers so '5' ↦ 5
, '5' + 1) // The first is a string,
// so “+” denotes catenation, so 1 ↦ '1'
console.log(Number('2.3') // ⇒ 2.3
,Number('five') // ⇒ NaN
,Boolean('five') // ⇒ true
,Boolean('') // ⇒ false
,String(NaN) // ⇒ 'NaN'
,String(null)) // ⇒ 'null'
#+end_src
#+RESULTS:
: 2.3 NaN true false NaN null
* Negative space :ignore:
#+latex: \vspace{-1em}
* Variable Bindings
~let x₀ = v₀, …, xₙ = vₙ;~ introduces 𝓃-new names ~xᵢ~ each having value ~vᵢ~.
- The ~vᵢ~ are optional, defaulting to ~undefined~.
- The program crashes if any ~xᵢ~ is already declared.
- Later we use ~xᵢ = wᵢ;~ to update the name ~xᵢ~ to refer to a new value
~wᵢ~.
* Augmented updates: ~x ⊕= y ≡ x = x ⊕ y~
* Increment: ~x-- ≡ x += 1~
* Decrement: ~y-- ≡ x -= 1~
#+BEGIN_SRC js
let x, y = 1, z;
console.log(x, y, z); // ⇒ undefined 1 undefined
#+END_SRC
- In the same way, for the same purpose, we may use ~var~ but it has undesirable
properties; e.g., its declarations are in the global scope and no error is
raised using ~var x = ⋯~ if ~x~ is already declared.
- In the same way, we may use ~const~ to introduce names that are constant: Any
attempt to change their values crashes the program.
- A binding name may include dollar signs ($) or underscores (_) but no other
punctuation or special characters.
* COMMENT Negative space :ignore:
#+latex: \vspace{-1em}
* Scope and Statements
Each binding has a scope, which is the part of the program in which
the binding is visible. For bindings defined outside of any function
or block, the scope is the whole program—you can refer to such
bindings wherever you want. These are called global.
# #
#+begin_parallel org
#+BEGIN_SRC js
let x = 10;
{ // new local scope
let y = 20;
var z = 30;
console.log(x + y + z); // ⇒ 60
}
// y is not visible here
// console.log(y)
// But z is!
console.log(x + z); // ⇒ 40
#+END_SRC
#+latex: \columnbreak
#+latex: \vspace{2em}
⊙ /global bindings/ are defined outside of any block and can be referenced anywhere.
#+latex: \vspace{1em}
⊙ /local bindings/ are defined within a block and can only be referenced in it.
#+latex: \vspace{1em}
⊙ =let, const= declare local bindings; =var= always makes global ones!
#+end_parallel
Besides the assignment statement, we also have the following statements:
+ Conditionals:
~if (condition) A else B~
+ Blocks:
If ~Sᵢ~ are statements, then ~{S₀; …; Sₙ;}~ is a statement.
+ The ~for/of~ syntax applies to arrays, strings, and other iterable structures
---we will define our own later.
#+BEGIN_SRC js
// Print all the elements in the given list.
for (let x of ['a', 1, 2.3]) {
console.log(`x ≈ ${x}`);
}
#+END_SRC
JavaScript is whitespace insensitive.
* Negative space :ignore:
#+latex: \vspace{-1em}
* [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number][Arithmetic]]
In addition to the standard arithmetic operations, we have ~Math.max(x₀, …, xₙ)~
that takes any number of numbers and gives the largest; likewise ~Math.min(⋯)~.
Other common functions include ~Math.sqrt, Math.ceil, Math.round, Math.abs,~ and
~Math.random()~ which returns a random number between 0 and 1.
Also, use ~%~ for remainder after division.
#+BEGIN_SRC js
// Scientific notation: 𝓍e𝓎 ≈ 𝓍 × 10ʸ
console.log(1, 2.998e8, 100 + 4 * 11)
// Special numbers so that division “never crashes”.
console.log(1/0, -1/0, Infinity - 10) // ⇒ Infinity -Infinity Infinity
console.log(Infinity - Infinity, 0/0) // ⇒ NaN NaN
// Random number in range min...Max
Math.floor(Math.random() * (max - min) + min)
#+END_SRC
:Hide:
#+BEGIN_SRC js
// Random number in 0..9
console.log(Math.floor(Math.random() * 10));
let max = 100, min = 30
let x = Math.floor(Math.random() * (max - min) + min);
console.log(x)
#+END_SRC
:End:
~NaN~ stands for “not a number”, it is what you get when a numeric expression
has no meaningful value.
# It is what we get when “things go wrong”, like ~1 / 0~.
+ Any ~NaN~ in an arithmetic expressions swallows the whole expression into a ~NaN~.
+ ~Number.isNaN(x)~ is true iff ~x~ is ~NaN~.
Everything is equal to itself, except ~NaN~. Why? ~NaN~ denotes the result of
nonsensical computations, and so is not equal to the result of any other
nonsensical computation.
#+BEGIN_SRC js
console.log(NaN == NaN) // ⇒ false
#+END_SRC
#+RESULTS:
: false
* [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean][Booleans]]
The empty string =''=, list =[]=, and ~0, NaN~ are falsey ---all else is truthy.
+ Note: ~(p < q < r) ≈ (p < q) < r~, it is not conjunctive!
#+BEGIN_SRC js
console.log(true, false, 3 > 2, 1 < 2, 1 != 2, 4 <= 2 < 3)
// Upper case letters come first, then lower case ones.
console.log('abc' < 'def', 'Z' < 'a')
// Equality with coercions, and without.
console.log(1.23 == '1.23', 1.23 === '1.23')
#+END_SRC
#+RESULTS:
: true false true true true true false
: true true
: true false
+ /Precise Equality/ ~===~ is equality with no type coercions.
# - ~x == y~ ≡ ~coe x === coe y~
+ Applying the “not” =!= operator will convert a value to Boolean type before negating
it.
+ Precedence: Relationals like ~==~ and ~>~ are first, then “and” ~&&~, then “or” ~||~.
+ The ternary operator: =condition ? if_true : if_false=
# && and || are lazy.
** Empty Values
#+BEGIN_SRC js
console.log(null == undefined) // ⇒ true
#+END_SRC
#+RESULTS:
: true
Only the empty values are coerced into being equal, no other value
is equal to an empty value. As such, ~x != null~ means that ~x~ is not an
empty value, and is in fact a real meaningful value.
Since ~&&~ and ~||~ are lazy, ~x || y~ means return ~x~ if ~x != false~
and otherwise return ~y~; i.e., /give me x if it's non-empty, else y/.
Likewise, ~x && y~ means /give me y, if x is nonempty, else give me the particular
empty value x/.
#+begin_src js
console.log( 4 == 3 && 4 // 3 is truthy
,'' == '' && 4 // '' is falsey
,'H' == 'H' && 4 // 'H' is truthy
, 0 == 0 && 4 // 0 is falsey
, 4 == 0 || 4 // 0 is falsey
)
#+end_src
#+RESULTS:
* [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String][Strings]]
Any pair of matching single-quotes, backticks, or double-quotes will produce a
string literal. However, backticks come with extra support: They can span
multiple lines and produce /formatted strings/, where an expression can be
evaluated if it is enclosed in ~${⋯}~.
:Hide:
#+BEGIN_SRC js
console.log('this', `and`, "this")
console.log('single: \', backtick: `, and double: "')
// ⇒ single: ', backtick: `, and double: "
#+END_SRC
:End:
#+BEGIN_SRC js
console.log(`half of 100 is ${100 / 2}`) // ⇒ half of 100 is 50
#+END_SRC
** String Methods
+ ~s.repeat(𝓃)~ ≈ Get a new string by gluing 𝓃-copies of the string 𝓈.
+ Trim removes spaces, newlines, tabs, and other whitespace from the start and end of a string.
#+BEGIN_SRC js
console.log(" okay \n ".trim()); // ⇒ okay
#+END_SRC
+ ~s.toUpperCase()~ and ~s.toLowerCase()~ to change case.
+ ~𝓈.padStart(𝓁, 𝓅)~ ≈ Ensure 𝓈 is of length ≥ 𝓁 by padding it with 𝓅 at the start.
#+BEGIN_SRC js
console.log(String(6).padStart(3, "0")); // ⇒ 006
#+END_SRC
+ ~𝓈.replace(/./g, c => p(c) ? f(c) : '')~ ≈ Keep only the characters that
satisfy predicate ~p~, then transform them via ~f~.
#+BEGIN_SRC js
let s = 'abcde'.replace(/./g, c => 'ace'.includes(c) ? c.toUpperCase() : '')
console.log(s); // ⇒ ACE
#+END_SRC
** Method Names Shared with Arrays
The following methods also apply to arrays.
:Justification:
#+BEGIN_SRC js
console.log(Object.getOwnPropertyNames(String.prototype)
.filter(x => Object.getOwnPropertyNames(Array.prototype).includes(x)))
#+END_SRC
:End:
+ ~s.length~ ⇒ Length of string
+ ~s[𝒾]~ ⇒ Get the 𝒾-th character from the start
- Unless ~0 ≤ 𝒾 < s.length~, we have ~s[𝒾] = undefined~.
+ ~s.concat(t)~ ⇒ Glue together two strings into one longer string; i.e., ~s + t~.
#+BEGIN_SRC js
console.log(('cat' + 'enation').toUpperCase()) // ⇒ CATENATION
#+END_SRC
+ ~s.includes(t)~ ⇒ Does ~s~ contain ~t~ as a substring?
+ ~s.indexOf(t)~ ⇒ Where does substring ~t~ start in ~s~, or -1 if it's not in ~s~.
- To search from the end instead of the start, use ~lastIndexOf~.
# Both indexOf and lastIndexOf take an optional second argument that indicates where to start searching.
+ ~s.slice(𝓂,𝓃)~ ⇒ Get the substring between indices 𝓂 (inclusive) and 𝓃 (exclusive).
- 𝓃 is optional, defaulting to ~s.length~.
- If 𝓃 is negative, it means start from the end: ~s.slice(-𝓃) ≈ s.slice(s.length - 𝓃)~.
- ~s.slice()~ ⇒ Gives a copy of ~s~.
** Treating Strings as Arrays
- There is no character type, instead characters are just strings of length 1.
- You can “split” a string on every occurrence of another string to get a list
of words, and which you can “join” to get a new sentence. ~𝓈.split(𝒹).join(𝒹) ≈ 𝓈~.
- To treat a string as an array of characters, so we can apply array only methods such
as ~f = reverse~, we can use split and join:
| ~𝓈.split('').f().join('')~ |
- Keeping certain characters is best done with regular expressions.
** COMMENT Warning: Care needs to be taken when working with strings of Unicode symbols.
#+BEGIN_SRC js
console.log("🐴👟".length); // ⇒ 4!
console.log("🐴👟"[1]); // Invalid half-characters!
for (let it of "🐴👟") console.log(it); // Better!
#+END_SRC
UTF-16, the format used by JavaScript strings, describes most common characters
using a single 16-bit code unit but uses a pair of two such units for others.
UTF-16 is generally considered a bad idea today.
* [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array][Arrays]]
# arrays are objects!
Array indexing, ~arr[𝒾]~, yields the 𝒾-th element from the start; i.e., the
number of items to skip; whence ~arr[0]~ is the first element.
# #
#+begin_parallel org
#+BEGIN_SRC js
let numbers = [];
numbers.push(2);
numbers.push(5);
numbers.push(7);
// or
numbers = [2, 5, 7]
console.log(numbers[2]); // ⇒ 7
let last = numbers.pop() // ⇒ 7
console.log(numbers) // ⇒ [ 2, 5 ]
// ⇒ undefined
console.log(numbers[2]);
// Is an element in the array? No.
console.log(numbers.includes(7))
numbers = numbers.concat('ola')
console.log(numbers)
// ⇒ [ 2, 5, 'ola' ]
console.log(numbers.reverse())
// ⇒ [ 'ola', 5, 2 ]
#+END_SRC
#+RESULTS:
: 7
: [ 2, 5 ]
: undefined
: false
: [ 2, 5, 'ola' ]
: [ 'ola', 5, 2 ]
#+end_parallel
# // Arrays can invert the order of their elements
#+latex: \vspace{-1em}
(*Stack*) The push method adds values to the end of an array, and the pop method
does the opposite, removing the last value in the array and returning it.
(*Queue*) The corresponding methods for adding and removing things at the start
of an array are called ~unshift~ and ~shift~, respectively.
Arrays have the following methods, which behave similar to the string ones from earlier.
| ~length~ | ~concat~ | ~includes~ | ~indexOf~ | ~lastIndexOf~ | ~slice~ |
One difference is that unlike string's ~indexOf~, which searches for substrings, array's
~indexOf~ searches for a specific value, a single element.
#+BEGIN_SRC js
console.log([1, 2, 3, 2, 1].indexOf(2)); // ⇒ 1
console.log([1, 2, 3, 2, 1].lastIndexOf(2)); // ⇒ 3
#+END_SRC
The ~concat~ method can be used to glue arrays together to create a new array,
similar to what the ~+~ operator does for strings.
- If you pass ~concat~ an argument that is not an array, that value will be added
to the new array as if it were a one-element array. This is a ~push~.
~Array(𝓃).fill(𝓍)~ ≈ Get a new array of 𝓃-copies of element 𝓍.
:Hide:
#+BEGIN_SRC js
console.log(Array(10).fill(3));
#+END_SRC
:End:
** Array methods
#+latex: \vspace{1em}
# Arrays provide a number of useful higher-order methods.
+ ~xs.forEach(a)~ to loop over the elements in an array and perform action ~a~.
# - It provides something like a ~for-of~ loop as a higher-order function.
+ ~xs.filter(p)~ returns a new array containing only the elements that pass the predicate ~p~.
+ ~xs.map(f)~ transforms an array by putting each element through the function ~f~.
+ ~xs.reduce(f, e)~ combines all the elements in an array into a single value.
- We can omit the starting value ~e~ if the array ~xs~ is non-empty,
in which case ~e~ is taken to be the first element ~xs[0]~.
+ ~xs.some(p)~ tests whether any element matches a given predicate function ~p~.
- ~xs.every(p)~ tests if every element of ~xs~ satisfies ~p~.
# - xs.includes(x) ≡ xs.some(e => e == x)
+ ~xs.findIndex(p)~ finds the position of the first element that matches the predicate ~p~.
With the exception of ~forEach~, the above functions do not modify the array
they are given.
#+BEGIN_SRC js
// Print the elements of the given array
[`a`, `b`, `c`].forEach(l => console.log(l));
// ∃/∀
console.log([1, 2, 3].some(e => e == 2)) // true
console.log([1, 2, 3].every(e => e == 2)) // false
// Sum the elements of an array
console.log([1, 2, 3, 4].reduce((soFar, current) => soFar + current)); // ⇒ 10
// flatten an array of arrays
let flatten = (xss) => xss.reduce((sofar, xs) => sofar.concat(xs), [])
let arrays = [[1, 2, 3], [4, 5], [6]];
console.log(flatten(arrays)) // ⇒ [ 1, 2, 3, 4, 5, 6 ]
#+End_src
Higher-order functions start to shine when you need to compose operations.
* COMMENT Control Flow
+ Braces form blocks:
If ~Sᵢ~ are statements, then ~{S₀; …; Sₙ;}~ is a statement.
+ Conditional statement:
~if (condition) S else S′~
+ Loop: ~while (condition) S~.
+
#+begin_src js
let i = 0;
while (i < 10) console.log(Math.pow(2, i++))
console.log('-------')
for(let i = 0; i < 10; i++) console.log(Math.pow(2, i))
#+end_src
+ ~break;~ ⇒ Exit the current loop.
+ ~continue;~ ⇒ Jump out of the body and contiue with the next iteration of the loop.
+ ~for~ rewrites to a ~while~ loop: ~for(init; cond; change) body ≈ init;
while(cond){body; change;}~. As such, all three pieces of ~for~ are optional.
+ Dispatching on a value with switch
#+begin_src js
switch (x){
case v₀: S₀
⋮
case vₙ₋₁: Sₙ₋₁
default: Sₙ
}
#+end_src
Find the /first/ 𝒾 with ~x == vᵢ~, then execute
~{Sᵢ; ⋯; Sₘ;}~, if there is no such 𝒾, execute the
default statement ~Sₙ~. Where ~Sₘ~ is the first
statement after ~Sᵢ~ that ends with ~break;~.
E.g., ~case v: S; case w: S′; break~
means do ~S;S′~ if we see ~v~
but we do ~S′~
when seeing both ~v~ and ~w~.
#+begin_src js
switch (2){
case 0: console.log(0)
case 1: console.log(1)
case 2: console.log(2)
default: console.log(-1)
}
#+end_src
#+RESULTS:
: 2
: -1
* [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function][Functions]]
** Intro :ignore:
Function values can do all the things that other values can do; i.e., they can
be used in arbitrary expressions; e.g., a binding that holds a function is still
just a regular binding and can, if not constant, be assigned a new value.
+ A ~function~ definition is a regular binding where the value of the binding is a
function.
Functions declared using the top-level ~function~ keyword may be used before
their declarations.
# Conceptually, they are moved to the top of their scope.
# #
#+begin_parallel org
#+BEGIN_SRC js
const square = function(x) {
return x * x;
};
console.log(square(12)); // ⇒ 144
#+END_SRC
#+BEGIN_SRC js
// Shorter way to define functions
console.log(square2(12));
function square2(x) {
return x * x;
}
#+END_SRC
#+end_parallel
#+latex: \vspace{-0.5em}
+ A ~return~ keyword without an expression after it will cause the
function to return ~undefined~.
+ Functions that don’t have a ~return~ statement at all, similarly return
~undefined~.
+ Declaring ~function f (⋯) {⋯}~ will not raise a warning if the name ~f~
is already in use ---similar to ~var~.
+ One may also define functions using “arrow” notation: ~(x₀, …, xₙ) => ⋯~.
- When there is only one parameter name, you can omit the parentheses around
the parameter list.
- If the body is a single expression, rather than a (multi-line) block in
braces, that expression will be returned from the function.
So, these two definitions of square do the same thing:
#+BEGIN_SRC js
const square1 = (x) => { return x * x; };
const square2 = x => x * x;
#+END_SRC
#+latex: \vspace{-0.5em}
As will be seen, arrow functions are [[https://stackoverflow.com/a/34361380/3550444][not exactly]] the same as declared functions.
*JavaScript is extremely fault-tolerant:* If we give a function more arguments
than it needs, the extra arguments are just ignored.
If we give it too few arguments, the missing arguments are assigned ~undefined~.
#+BEGIN_SRC js
// Extra arguments are ignored
console.log(square(4, true, "hedgehog")); // ⇒ 16
// No longer a function!
square = 'g'
#+END_SRC
** Default Values
(*Default Values*)
If you write an = operator after a parameter, followed by an expression, the
value of that expression will replace the argument when it is not given.
#+BEGIN_SRC js
let square = (x = 1) => x * x;
console.log(square(3)); // ⇒ 9
console.log(square()); // ⇒ 1
#+END_SRC
# “let clauses”!
** Rest Parameters
(*Rest Parameters*)
It can be useful for a function to accept any number of arguments. For example,
~Math.max~ computes the maximum of all the arguments it is given. To write such
a function, you put three dots before the function’s last parameter, which is called
“the rest parameter” and it is treated as an array containing all further arguments.
# #
#+begin_parallel org
#+BEGIN_SRC js
function max(...numbers) {
let result = -Infinity;
for (let number of numbers) {
if (number > result)
result = number;
}
return result;
}
console.log(max(4, 1, 9, -2)); // ⇒ 9
#+END_SRC
#+latex: \columnbreak
You can use a similar three-dot notation to call a function with an array of arguments.
#+latex: \vspace{1em}
#+BEGIN_SRC js
let numbers = [5, 1, 7];
console.log(max(...numbers));
// ⇒ 7
#+END_SRC
# #
# Lisp's “apply” and Python's “unpacking *” operation!
#+end_parallel
This “spreads” out the array into the function call, passing its elements as
separate arguments. It is possible to include an array like that along with
other arguments, as in ~max(9, ...numbers, 2)~.
:Hide:
Square bracket array notation similarly allows the triple-dot operator to spread
another array into the new array.
#+BEGIN_SRC js
let words = ["never", "fully"];
console.log(["will", ...words, "understand"]);
// ⇒ ["will", "never", "fully", "understand"]
#+END_SRC
:End:
** Higher-order Functions
# + Functions are first-class citizens! They can be passed around like other values.
# + Multiline arrows need to be enclosed in {/}.
*Higher-order functions* allow us to abstract over actions, not just values.
They come in several forms.
For example, we can have functions that create new functions.
#+BEGIN_SRC js
let greaterThan = n => (m => m > n);
let greaterThan10 = greaterThan(10);
console.log(greaterThan10(11)); // ⇒ true
#+END_SRC
And we can have functions that change other functions. (*Decorators*)
#+BEGIN_SRC js
function noisy(f) {
return (...args) => {
let result = f(...args);
console.log(`Called: ${f.name}(${args}) ≈ ${result}`);
return result;
};
}
noisy(Math.min)(3, 2, 1); // Called: min(3,2,1) ≈ 1
#+END_SRC
:FunctionProperties:
#+BEGIN_SRC js
console.log(Object.getOwnPropertyNames(Function.prototype))
#+END_SRC
:End:
We can even write functions that provide new types of control flow.
# #
#+begin_parallel org
#+BEGIN_SRC js
function unless(test, then) {
if (!test) then();
}
#+END_SRC
#+latex: \columnbreak
#+BEGIN_SRC js
let n = 8;
unless(n % 2 == 1, () => {
console.log(n, "is even");
});
// ⇒ 8 is even
#+END_SRC
#+end_parallel
* Negative space :ignore:
#+latex: \vspace{-2em}
* Destructuring and the “spread” Operator
If you know the value you are binding is an array/object, you can use []/{} brackets
to “look inside” of the value, binding its contents.
# - A similar trick works for objects, using braces instead of square brackets.
#+latex: \vspace{-0.3em}
One of the reasons the ~doit~ function below is awkward to read is that we have
a binding pointing at our array, but we’d much prefer to have bindings for the
elements of the array, whence the second definition of ~doit~.
#+latex: \vspace{-0.3em}
#+BEGIN_SRC js
let xs = [9, 11, 22, 666, 999];
// The following are the same.
function doit(xs){ return xs[0] + xs[1] + xs[2]; }
function doit([x, y, z]) {return x + y + z; }
//
// Only first three items accessed in “doit”; extra args are ignored as usual.
console.log(doit(xs))
// Destructuring to get first three elements and remaining
let x = xs[0], y = xs[1], z = xs[2], ws = xs.slice(3);
console.log(x, y, z, ws) // ⇒ 9 11 22 [ 666, 999 ]
// Nice! Same thing.
let [a, b, c, ...ds] = xs
console.log(a, b, c, ds) // ⇒ 9 11 22 [ 666, 999 ]
// Destructuring to get first and remaining elements
let [head, ...tail] = xs
console.log(head, tail) // ⇒ 9 [ 11, 22, 666, 999 ]
// Destructuring on an object to get two properties and the remaining subobject
let {name, age, ...more} = {name: "Musa", age: 72, x: 1, y: 2}
console.log(name, age, more) // ⇒ Musa 72 { x: 1, y: 2 }
// Destructuring: Simultaneous assignment!
var p = 1, q = 2 // ⇒ 1, 2
var [p, q] = [q, p] // swap them
console.log(p, q) // ⇒ 2, 1
// Unpacking: f(...[x₀, …, xₙ]) ≈ f(x₀, …, xₙ)
console.log(Math.min(...xs)) // ⇒ 9
// Unpacking: Merging arrays/objects
let ys = [1, ...xs, 2, 3] // ⇒ 1, 9, 11, 22, 666, 999, 2, 3
let zs = {w: 0, ...more, z: 3} // ⇒ { w: 0, x: 1, y: 2, z: 3 }
// Updating a property, a key-value pair
zs = {...zs, w: -1} // ⇒ { w: -1, x: 1, y: 2, z: 3 }
#+END_SRC
# objArray.map(obj => ({ ...obj, keyToChange: 667, key: 42 }))
Note that if you try to destructure ~null~ or ~undefined~, you get an error,
much as you would if you directly try to access a property of those values.
:Hide:
| | ~let {x₀, …, xₙ, ...𝓌} = 𝓋~ |
| ≡ | ~let x₀ = 𝓋.x₀, …, xₙ = 𝓋.xₙ; 𝓌 = 𝓋; delete 𝓌.x₀, …, delete 𝓌.xₙ~ |
:End:
#+BEGIN_SRC js
let {x₀, …, xₙ, ...𝓌} = 𝓋
≡ let x₀ = 𝓋.x₀, …, xₙ = 𝓋.xₙ; 𝓌 = 𝓋; delete 𝓌.x₀, …, delete 𝓌.xₙ
#+END_SRC
As usual, in arrow functions, we may destructure according to the shape
of the elements of the array; e.g., if they are lists of at least length 2
we use ~(soFar, [x, y]) => ⋯~. This may be useful in higher order functions
such as ~map, filter, reduce~.
* [[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object][Objects]]
# Numbers, Booleans, and strings are the atoms that data structures are built
# from. Many types of information require more than one atom, though. Objects
# allow us to group values—including other objects—to build more complex
# structures.
Objects and arrays (which are a specific kind of object) provide ways to group
several values into a single value. Conceptually, this allows us to put a bunch
of related things in a bag and run around with the bag, instead of wrapping our
arms around all of the individual things and trying to hold on to them
separately. These “things” are called /properties/.
Arrays are just a kind of object specialised for storing sequences of things.
** Object Creation
Values of the type /object/ are arbitrary collections of properties. One way to
create an object is by using braces as an expression that lists properties as /“name:value”/
pairs.
:MergingObjects:
#+BEGIN_SRC js
// JavaScript does pushouts-ish: There’s an Object.assign function that copies
// all properties from one object into another:
// Object.assign(x, y) ≡ x = x ∪ y
let objectA = {a: 1, b: 2};
Object.assign(objectA, {b: 3, c: 4});
console.log(objectA);
// ⇒ {a: 1, b: 3, c: 4}
#+END_SRC
:End:
:the_delete_keyword:
The difference between setting a property to undefined and actually deleting it
is that, in the first case, the object still has the property (it just doesn’t
have a very interesting value), whereas in the second case the property is no
longer present and in will return false.
:End:
1. Almost all JavaScript /values/ have properties. The exceptions are ~null~ and
~undefined~. If you try to access a property on one of these nonvalues, you get
an error. Properties are accessed using ~value.prop~ or ~value["prop"]~.
2. Whereas ~value.x~ fetches the property of value named /x/, ~value[e]~ tries to
evaluate the expression ~e~ and uses the result, converted to a string, as the
property name.
3. The dot notation only works with properties whose names look like valid
(variable) binding names. So if you want to access a property named ~2~ or ~John
Doe~, you must use square brackets: ~value[2]~ or ~value["John Doe"]~.
# E.g., rather than ~array["length"]~ we typically write ~array.length~ since it's
# shorter.
# Properties that contain functions are generally called methods of the value they
# belong to, as in “toUpperCase is a method of a string”.
4. Unless ~value~ contains a property ~x~, we have ~value.x ≈ undefined~.
- Hence, out of bounds indexing results in ~undefined~.
# E.g., if ~xs ≈ [1, 2]~ then ~xs[66]~ is undefined.
# !('x' in o) ⇒ o.x ≈ undefined; the converse is not true.
5. Notice that the ~this~ keyword allows us to refer to other parts of /this/
object literal. Above, ~info~ used the ~person~ object's information, whereas
~speak~ did not. The ~“this”~ keyword is covered in more detail below.
6. Variables names in an object literal, like ~languages~, denote a shorthand
for a property with the same and value, but otherwise is no longer related to
that binding.
This is useful if we want multiple objects to have the same binding; e.g.,
with ~let x = ⋯, a = {name: 'a', x}, b = {name: 'b', x}~, both objects have
a ~x~ property: ~a.x~ and ~b.x~.
:Example:
#+BEGIN_SRC js
let x = 3, a = {name: 'a', x}, b = {name: 'b', x};
console.log(a.x)
#+END_SRC
:End:
7. We cannot dynamically attach new properties to the atomic types String, Number, Boolean;
e.g., ~let x = 2; x.vest = 'purple'; console.log(x.vest);~ prints ~undefined~.
We can write it, but they “don't stick”.
8. Below, we could have begun with the empty object then added properties dynamically:
~let person = {}; person.name = `musa`; person.age = 29; …~.
#+latex: \columnbreak
#+BEGIN_SRC js
let languages = ['js', 'python', 'lisp']
let person = { name: 'musa'
, age: 27
, 'favourite number': 1