## More on JavaScript

Spring 2019 。 Ric Huang
---
### Disclaimers
* These lecture notes have by no means any intention to be a complete tutorial of JavaScript.
* I assume you should have some basic knowledge/experience on one of the most commonly learnt programming languages: C/C++, Java, Python
* Therefore, I will cover only those grammers/syntax/Constructs that are special in JavaScript. To have a better learning, you are strongly recommended to go through a JavaScript tutorial on your own.
---
### More on Variables and Objects
* Recall: Using "var" to declare a variable
* If a variable is declared outside any function, it is a global variable and is visible in the entire program.
* If a variable is declared inside a function, it is visible only in that function.
* If a function is defined inside another function, the inner function can see the variables in the outer function, but not the other way around.
----
### Using "let" and "const"
* **"let"** defines a block-scoped local variable
```javascript
if (true) {
let y = 5;
}
console.log(y); // ReferenceError: y is not defined
```
* **"const"** defines "read-only" variables. However, the properties inside a const object are NOT constrained
```javascript
const obj = { aa: 10, bb: "Hello" };
obj = 30; // ERROR
obj.aa = 30; // This is OK!!
```
----
### Built-in **"Math"** Object [[link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Numbers_and_dates#Math_object)]
* The built-in **Math** object provides many usful methods:
```javascript
// Some useful examples
Math.PI; // 3.14159...
Math.sin(radian); // e.g. Math.sin(Math.PI/4) = 0.7071...
Math.pow(2, 5); // 32
Math.floor(22/3); // 7
Math.round(-3.49); // -3
Math.min(3, 5, 7); // 7
Math.random(); // Between [0, 1)
Math.sqrt(179); // ~13.38
```
----
### Built-in **"Date"** Object [[link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Numbers_and_dates#Date_object)]
* Create Date objects
```javascript
var now = new Date(); // current local time (up to second)
var XMasDinner = new Date("December 25, 2019 18:30:00");
var XMasDinner = new Date(2019, 11, 25, 18, 30, 0)
var myBirthday = new Date("December 02, 2001");
var myBirthday = new Date(2001, 11, 02); // 11 is December
// Output: "Sun Dec 02 2001 00:00:00 GMT+0800 (台北標準時間)"
```
----
* Built-in set and get mthods for **Date** object:
```javascript
// set<FullYear,Date,Hours,Minutes,Seconds,Milliseconds>
// get<FullYear,Date,Day,Hours,Minutes,Seconds,Milliseconds>
// There are also set/getUTCxxx versions
var now = new Date();
now.setFullYear(2001, 11, 02); // change to 2001/12/02
now.setDate(28); // change to 2001/12/28
now.setMinutes(13); // change minute to 13
now.getFullYear(); // 2001
now.getDate(); // 28
now.getDay(); // 5; because 2001/12/28 is Friday
// toXXXString
now.toDateString(); // "Fri Dec 28 2001"
now.toLocaleDateString(); // "2001/12/28"
now.toTimeString(); // "HH:MM:SS GMT+0800 (台北標準時間)"
now.now.toLocaleString(); // "2001/12/28 下午H:MM:SS"
```
----
* **setTime()** method sets the time represented by a number of milliseconds since 1970/01/01.
* **getTime()** method returns the number of milliseconds since 1970/01/01 for a Date object.
```javascript
// Try this...
(function () {
var time = new Date();
var hour = time.getHours();
var minute = time.getMinutes();
var second = time.getSeconds();
var temp = '' + ((hour > 12) ? hour - 12 : hour);
if (hour == 0)
temp = '12';
temp += ((minute < 10) ? ':0' : ':') + minute;
temp += ((second < 10) ? ':0' : ':') + second;
temp += (hour >= 12) ? ' P.M.' : ' A.M.';
return temp;
})();
```
---
### More on Control Statements
----
### 各種 "for"
* 你知道的 for
```javascript
for ([initialExpression]; [condition]; [incrementExpression])
statement
```
* for (... in ...)
// loop through property names
```javascript
for (propertyName in object)
statement
```
* for (... of ...)
// loop through property values on **iterable** object
```javascript
for (propertyValue of object)
statement
```
----
### "for(... in ...)" vs. for (... of ...)
```javascript
var arr = [3, 5, 7];
arr.foo = 'hello';
for (var i in arr) {
console.log(i); // logs "0", "1", "2", "foo"
}
for (var i of arr) {
console.log(i); // logs 3, 5, 7; NO "hello"
}
```
----
### Exception Handling
* 許多時候,當你的 code 有錯誤,你只會得到一個白螢幕,或者是許多的小菜包
* 當然,console.log() 是你 debug 的好朋友
* 但往往要不是發現的時候太晚了,就是還要開 console 才能看得到
* 用 Exception Handling 來第一時間抓到錯誤!
----
#### Exception Handling
```javascript
try {
const a = 10;
doSomething(a);
} catch(e) {
alert(e);
}
function dosomething(a) { a++; }
```
語法:
```javascript
try {
doSomething();
} catch(e) { // e 可能為系統的錯誤訊息,或者是 throw 出來的 exp
takeSomeAction(e);
} finally { // 不管有沒有 error 都會被 call
takeCareTheFinalStep();
}
function doSomething() {
// if something wrong, throw an error
throw errorExpression;
}
```
---
### More on Operators
----
* The **"in"** operator returns true if the specified property is in the specified object
```javascript
// Array
var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple'];
0 in trees; 3 in trees; // returns true
6 in trees; 'bay' in trees; // returns false
'length' in trees; // returns true
// built-in objects
'PI' in Math; // returns true
var myString = new String('coral');
'length' in myString; // returns true
// Custom objects
var mycar = { make: 'Honda', model: 'Accord', year: 1998 };
'make' in mycar; // returns true
'model' in mycar; // returns true
```
----
* The **"instanceof"** operator returns true if the specified object is of the specified object type
```javascript
var theDay = new Date(1995, 12, 17);
if (theDay instanceof Date) {
// statements to execute
}
```
----
* The **"typeof"** operator returns a string indicating the type of the unevaluated operand
```javascript
var myFun = new Function('5 + 2');
var shape = 'round';
var size = 1;
var foo = ['Apple', 'Mango', 'Orange'];
var today = new Date();
typeof myFun; // returns "function"
typeof shape; // returns "string"
typeof size; // returns "number"
typeof foo; // returns "object"
typeof today; // returns "object"
typeof doesntExist; // returns "undefined"
```
----
### Shift Operators
* **"<<"** performs signed left-shifting operation
```javascript
9 << 2; /* 36 */ -9 << 2; /* -36 */
```
* **">>"** performs signed right-shifting operation
```javascript
// Thinking in 2's complement, with (+/-) sign preserved
9 >> 2; /* 2 */ -9 >> 2; /* -3 */
```
* **">>>"** performs zero-filled right-shifting operation
```javascript
// Thinking in 2's complement, with 0's filled from left
9 >>> 2; /* 2 */ -9 >>> 2; /* 1073741821 */
```
----
### Spread (...) Operator
* Spread (...) operator allows array, string, or function to **expand** the argument specification in place for 0 or more operands
```javascript
// for function calls
myFunction(...iterableObj);
// for array or string
[...iterableObj, '4', 'five', 6];
// for object literals (new in ES2018)
let objClone = { ...obj };
// Example:
function myFunction(v, w, x, y, z) { }
var args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);
```
----
### Rest (...) Operator
* **Rest** syntax looks exactly like **spread** syntax but is used for **destructuring** arrays and objects, while "spread" is used for **expanding** arguments
```javascript
// Syntax
function f(a, b, ...restArgs)... // restArgs is an array
// Examples
function myFun(a, b, ...manyMoreArgs) {
console.log("a", a); console.log("b", b);
console.log("manyMoreArgs", manyMoreArgs);
}
myFun("one", "two", "three", "four", "five", "six");
function f(...[a, b, c]) { return a + b + c;}
f(1) // NaN (b and c are undefined)
f(1, 2, 3) // 6
f(1, 2, 3, 4) // 6 (the fourth parameter is not destructured)
```
----
### Destructing Assignment \[[link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)\]
* The destructuring assignment is to unpack values from arrays, or properties from objects, into distinct variables.
```javascript
var a, b, rest;
[a, b] = [10, 20];
console.log(a); // expected output: 10
console.log(b); // expected output: 20
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(rest); // expected output: [30,40,50]
```
----
### Template Literals (Strings)
* 有的時候我們用字串變數來 access 一些物件、Array 內的 properties/values, 在程式中需要動態的決定字串的值,像是 [ "bg-green", "bg-red", "bg-yellow"], 我們希望傳進 function 的參數可以是:
* someFunction("bg-dynamicallyDecided"), 其中 "dynamicallyDecided" 希望可以動態決定
* Template literals 的語法是:
```javascript
`string text ${expression} string text`
```
* 其中 expression 可以是一個變數,甚是是一個算式
----
### Template Literal Examples
```javascript
assets["bird"]
= ["blue", "red", "yellow"].map(
color => ["upflap", "midflap", "downflap"].map(
flap => loadImage(`${color}bird-${flap}.png`)
)
);
```
---
### More on Functions
----
### Nested function and closure
* Recall: there can be functions in a function
* <em> "...the inner function can see the variables in the outer function, but not the other way around." </em>
* The inner function can be accessed only from statements in the outer function.
* Therefore, the inner function forms a **closure (閉包)**
* The inner function inherits the arguments and variables from the outer function, and the outer function can call the inner function only through its arguments
* **Encapsulation** for the vars of the inner function.
----
### Examples of "Closure"
```javascript
function addSquares(a, b) {
function square(x) {
return x * x;
}
return square(a) + square(b);
}
a = addSquares(2, 3); // returns 13
b = addSquares(3, 4); // returns 25
c = addSquares(4, 5); // returns 41
```
----
### Return the closure inner function
```javascript
function addN(x) {
function increment(y) {
return x + y;
}
return increment;
}
var add3 = addN(3); // give me a function that adds 3
var a = add3(5); // returns 8
var b = addN(3)(5); // returns 8
```
* The function name "increment" is kind of redundant in this example. Using "arrow function" makes it clearer.
```javascript
function addN(x) { return (y => x + y); }
```
----
### Issue about "this"
* **"this"** in function refers to the caller object
```javascript
function Student(id) {
this.id = id;
this.toSchoolID = function () {
this.id = "b07901" + ((id < 100)? ("0" + id): id);
}
}
var ric = new Student(57);
ric.toSchoolID(); // { id: "b07901057" }
```
----
* How about this:
```javascript
function Student(i) {
this.id = i;
this.toSchoolID = function () {
this.id = "b07901" + ((id < 100)? ("0" + id): id);
}
}
// Uncaught ReferenceError: id is not defined
```
* and this?
```javascript
function Student(id) {
this.id = id;
this.toSchoolID = function () {
id = "b07901" + ((id < 100)? ("0" + id): id);
}
}
var ric = new Student(57);
ric.toSchoolID(); // { id: 57 }
```
----
* Actually, try this:
```javascript
function Student(id) {
this.id = id;
this.toSchoolID = function () {
idd = "b07901" + ((id < 100)? ("0" + id): id);
}
}
var ric = new Student(57);
ric.toSchoolID(); // { id: 57}
console.log(idd); // "b07901057"
```
==> undeclared variable in a function refers to the global scope!
----
### Be careful about "this"
* We expect "p.age" to grow up every second
```javascript
function Person() {
this.age = 0;
setInterval(function growUp() {
this.age++;
}, 1000);
}
var p = new Person();
// Note: "setInterval(f, ms)" calls f() per ms milliseconds
```
* But, "p.age" stays at '0'.... WHY??
----
* Try this...
```javascript
function Person() {
this.age = 0;
setInterval(function growUp() {
age++;
}, 1000);
}
var p = new Person();
```
[Beware] You got an error "Uncaught ReferenceError: age is not defined at growUp" **every second**!!
----
* One way to fix it...
```javascript
function Person() {
var that = this;
that.age = 0;
setInterval(function growUp() {
that.age++;
}, 1000);
}
var p = new Person();
```
==> console.log\(p\), and you will see "p.age" grows every second!
==> But this is kind of stupid. How can we fix it?
----
### Using "bind()"
* "Function.prototype.bind(thisArg[, ...otherArgs])" creates a new function that use "thisArg" as "this" for the bound function
```javascript
function Person() {
this.age = 0;
function growUp() { this.age++; }
setInterval(growUp.bind(this), 1000);
}
var p = new Person();
```
----
### Or, using "arrow function"
* Note: An arrow function does not have its own this; the this value of the **enclosing execution context** is used.
```javascript
function Person() {
this.age = 0;
setInterval(() => { this.age++; }, 1000);
}
var p = new Person();
```
----
### More on "Arrow Functions"
* Syntax
```javascript
([parameter, ...moreParameters]) => { statements }
or
([parameter, ...moreParameters]) => expression
// the above is the same as { return expression; }
or
([parameter, ...moreParameters]) => ({ objectDefinition })
or
// when there is only one parameter, "()" can be omitted
parameter => { statements }
```
----
#### A "Hello World" Example of "Arrow Functions"
```javascript
() => { console.log("Hello"); }
// However, these are WRONG!!
() => ({ console.log("Hello"); }) // NOT a valid object def
() => ( console.log("Hello"); ) // Not a valid expression
// These are OK!
() => ( console.log("Hello") )
() => console.log("Hello"); // But, be careful about ';'
() => console.log("Hello")
```
----
### But, arrow function is always anonuymous. How do we call it?
----
### You must call it on the spot!
### Or, pass it as a parameter to another function!
----
### Calling an Arrow Function
```javascript
// These are OK!
(() => { console.log("Hello"); })();
(() => console.log("Hello"))();
// But this is wrong!
(() => ( console.log("Hello"); )(); // NO ';' for expression!
```
----
### A more common usage is to pass an arrow function as a function parameter
```javascript
function Person() {
this.age = 0;
setInterval(() => { this.age++; }, 1000);
}
var elements = ['Hydrogen', 'Helium', 'Lithium', 'Beryllium'];
elements.map(elem => elem.length); // [8, 6, 7, 9]
```
* The second example uses arrow function as a "callback function"
----
### Callback Function
* A callback functin f() is passed as a functional object to another function g(args), like g(**f**, ...args).
* Note that callbacks are often used to continue code execution after an asynchronous operation has completed — these are called **asynchronous callbacks**
```javascript
function getImg(imgGot, url) {
var httpState = someAsyncFunToGetImg(url);
imgGot(httpState); // callback function to info the caller
}
```
---
### More on Array and Array Methods
* Array can be constructed in 3 different ways
```javascript
// These are the same
var arr = [ 0, 1, 2, 3 ];
var arr = new Array(0, 1, 2, 3);
var arr = Array(0, 1, 2, 3);
```
----
* Element or length?
```javascript
var arr = [ 42 ]; // arr[0] = 42
var arr = [ "42" ]; // arr[0] = "42"
var arr = [ 1, "42" ]; // arr[0] = 1, arr[1] = "42"
var arr = Array(42); // arr.length = 42, arr[0] = undefined
// The above is the same as:
var arr = [];
arr.length = 42;
```
----
### Array.length
```javascript
var arr = ['one', 'two', 'three'];
arr[2]; // three
arr['length']; // 3; same as "arr.length"
arr['foo']; // undefined
arr.foo = "bar"; // As an object, arr can have properties
arr['foo']; // "bar"
```
```javascript
var cats = [];
cats[30] = ['Dusty'];
console.log(cats.length); // 31
```
----
### Iterate through an Array
```javascript
var array = ['first', 'second', , 'fourth'];
for (var i = 0; i < array.length; i++)
console.log(array[i]);
// Output: first, second, undefined, fourth
```
* Or using forEach() with arrow function
* Will skip undefined element!!
```javascript
var array = ['first', 'second', , 'fourth'];
array.forEach(i => console.log(i));
// Output: first, second, fourth
```
----
### Useful Array Methods ([link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections#Array_methods))
```javascript
var newArr = arr.concat(elements);
var newArr = arr.join(char); // join elements with 'char'
arr.push(elements); // to the end
var element = arr.pop(); // remove and return the last
var firstElem = arr.shift(); // remove and return the first
var length = arr.unshift(elements); // to the front
// "slice" extracts a section of an array and returns a new array
var newArray = arr.slice(startIdx, uptoIdx);
arr.reverse();
arr.sort();
```
----
### Other useful Array methods with "callbacks"
* Syntax: arr.method(callback[, thisObj])
```javascript
arr.forEach(elem => console.log(elem));
arr.map(elem => elem*2); // return newArr
arr.filer(elem => elem.length < 10); // return newArr
arr.every(elem => elem.length < 10); // return true/false
arr.some(elem => elem.length < 10); // return true/false
arr.reduce((e1, e2) => e1 + e2); // reduce to one element
```
---
## That's it for now!
{"metaMigratedAt":"2023-06-14T20:32:05.795Z","metaMigratedFrom":"YAML","title":"More on JavaScript (03/13)","breaks":true,"slideOptions":"{\"theme\":\"beige\",\"transition\":\"fade\",\"slidenumber\":true}","contributors":"[{\"id\":\"752a44cb-2596-4186-8de2-038ab32eec6b\",\"add\":22356,\"del\":4778}]"}