## More on JavaScript ![](https://i.imgur.com/bC0LH8W.png) 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}]"}
    1551 views