# JavaScript | Notes * Basic Concept * **JavaScript Types are Dynamic** This means that the same variable can be used to hold different data types `var x; (undefined) x = 5; (Number) x = "abc" (String);` * `<script></script>`Placing scripts at the bottom of the <body> element improves the display speed, because script interpretation slows down the display. * Using document.write() after an HTML document is loaded, will delete all existing HTML. (should only be used for testing) * window keyword is optional (wondow.alert() => alert()) * print() : literally call a printer to print * avoid code lines longer than **80** characters. * encode : Unicode * var xxx; 還沒給值,此時type為undefined * re-declare a JavaScript variable, it will not lose its value. `var x = 123; var x; // x still 123` but var x = 456 becomes 456 * 左往右讀,碰到字串後就都轉成字串 `console.log("5" + 2 + 3) // 523` `console.log(2 + 3 + "5") // 55` * (" ' ' " , ' " " ')可以(" " " " , ' ' ' ')不行 * **`undefined` and `null` are equal in value but different in type** : **null === undefined // false (===代表value跟type一樣才為真 : identity)** **null == undefined // true (==代表值value一樣就為真 : equality)** * objectName.methodname (without parentheses) return function defnition function f(a){return 123}; f(a) return Number, f return function * 字串很長可以用 \ 換行( some browsers do not allow spaces after \\ ) 但還是應該用 + ``` var x = "hello \ world" console.log)(x); // hello world ``` * String can be Objects, but don't create strings as objects. It slows down execution speed. ``` var x = "Francis"; // return String var y = new String("Francis"); // return Object var z = new String("Francis"); // return Object ``` **Comparing two JavaScript objects will **always** return `false`.** ``` (y == z) // false, because they are different objects(not the same location in memory) (y === z) // false, because they are different objects(not the same location in memory) // Array, Function, Date, Regx 也是比較記憶體位置 ``` * string.indexOf("xx", 1) : 可以有第二個參數當作開始位置,但不能用正則。 string.search("xx") : 只可以有一個參數,但可以用正則。 string.slice(2, 5) : 擷取字串的片段,負數倒者取(右到左),return String * string.substring(2, 5) : 不能用負數的slice() [位置2到位置5] * string.substr(2, 5) : 可以負數[位置2往後算5個] "i like xx and xx".replace("xx", "yy") : * 不會改掉原字串而是return new String * 只改掉第一個match (i like yy and xx) * case sensitive, use regx with an /i flage to insensitive * use regx with an /g to replace all matches(global match) 用regx第一個參數不用引號 * Strings are immutable : All string methods return a new string. * string.trim() : removes whitespace from both sides of a string. * var str = "HELLO WORLD"; str[0]; // returns H str[100]; // returns `undefined` it's not a array * string.split() : 如果參數為空,回傳一個長度1的字串陣列 如果參數為"",回傳一個長度1的字串陣列 * `var x = 10; var y = 20;` `"result : " + x + y; // result : 1020` * var x = "100"; var y = "10"; var z = "apple"; x / y // 10 x * y // 1000 x - y // 90 x + y; // "10010" y / - * z // NaN y + z // "10apple" * **(typeof NaN, Infinity) // returns number** * num.toString() : 參數可放2, 8, 16等等進位法 * num.toFixed(x) : x為小數點後幾位,回傳string,算錢用2 * num.toPrecision(x) : x為去掉小數點後的所有位數,回傳string * var x = 6; var y = x.MAX_VALUE;   // y becomes undefined * Array.forEach() ``` var fruits, text; fruits = ["Banana", "Orange", "Apple", "Mango"]; text = "<ul>"; fruits.forEach(myFunction); text += "</ul>"; function myFunction(value) { text += "<li>" + value + "</li>"; } ``` * You should use **objects** when you want the element names to be **strings (text)**. * You should use **arrays** when you want the element names to be **numbers**. * **Use `[]` instead.** * new Array(40,100) // [40, 100] 2 elements * new Array(40) // an array with 40 undefined elements * Array.isArray(arr); // don't use typeof because it always returns object * array.join("") // 所有元素以*號分開全部串起來 * array.pop() // 移除最後一個元素,也可以回傳被移除的該元素 * array.push() // 增加一個元素在最後一個元素後,也可以回傳被加入的該元素 * array.shift() // 移除第一個元素,也可以回傳被增加的該元素 * array.unshift() // 增加一個元素在第一個元素前,**回傳陣列長度** * delete arr[0]; // arr[0] becomes **undefined** * array.splice ``` var fruits = ["Banana", "Orange", "Apple", "Mango"]; fruits.splice(2, 0, "Lemon", "Kiwi"); // 第2個開始(orange),移除0個再開始加 "Lemon", "Kiwi" fruits.splice(0, 2); // 從第0開始移除到2(two elements) ``` * array.concat(可放陣列或value) : 原本陣列沒變,回傳新陣列。 * array.slice(i) : 移除i位置之前的元素(右往左),return the rest of the array。 * array.slice(i, j) : 保留i到j位置之間的元素(右往左),return the rest of the array。 * array.sort() // asc, 會用字串比較, numbers要另外寫 ``` var points = [40, 100, 1, 5, 25, 10]; points.sort(function(a, b){return a - b}); // numbers asc ``` * array.reverse() // desc * Math.max.apply(null, [1, 2, 3]) is equivalent to Math.max(1, 2, 3). * When comparing a string with a number, JavaScript will convert the string to a number. An empty string converts to 0. A non-numeric string converts to `NaN` which is always `false` * '2' < '12' 這種要先強制轉型Number(string) 不然會相反結果 [相反是因為"2"直接轉成的number會比"1"("12"的第一個位數)直接轉成的number大] * comparison : * typeof 回傳出來的都是字串 * typeof null // Object, also Date and Array * typeof NaN // Number * 沒給type、有給type但沒給value : 都是undefined (待確定) * type conversion : * "5" + null // returns "5null" * String(null) // "null" * `this` : * 在method中代表該物件 (method是在建構子裡面的function) * 單獨出現代表global object [object window] * 在function中代表global object [object window] * Why `var self = this;` ? **因為在js中this不是變數是關鍵字,而且會分不清this是local的還是global的** ``` // this變成person2 var person1 = { fullName: function() { return this.firstName + " " + this.lastName; } } var person2 = { firstName:"John", lastName: "Doe", } person1.fullName.call(person2);  // Will return "John Doe" ``` * let * global用let,block用var會有error(already been declared) * let變數只存在block scope或loop中 * Global variables defined with the `let` keyword do not belong to the window object : let x = "a"; // window.x will be `undefined` * 沒有hoistitng特性 ``` var x = 10; // Here x is 10 { var x = 2; // Here x is 2 } // Here x is 2 ``` ``` var x = 10; // Here x is 10 { let x = 2; // Here x is 2 } // Here x is 10 ``` ``` let i = 5; for (let i = 0; i < 10; i++) { // some statements } // Here i is 5 ``` * const * 宣告就要有值 * `const PI = 3.14159` // primitive type不可再被assign * 物件的資料可被重新reassign `const car{color:"white"}; car.color = red;` is available * 但不可以被reassign物件,直接ERROR `const car{color:"white"}; car = {color:"red"};` is unavailable * 沒有hoistitng特性 * hoistitng (提升) ``` num = 6; num + 7; var num; /* 只要 num 有被宣告,就不會有錯誤 */ /* 因為 var num; 被hoist到 num = 6上面 */ ``` **JavaScript 僅提升宣告的部分,而不是初始化。** **如果在使用該變數後才宣告和初始化,那麼該值將是 undefined。** ``` var x = 1; // 初始化 x console.log(x + " " + y); // '1 undefined' var y = 2; var x = 1; // 初始化 x var y; // 宣告 y console.log(x + " " + y); // '1 undefined' y = 2; // 初始化 y ``` | global | block | return | |:------ | ----- | ---------:| | var | var | block | | var | let | global | | var | const | global | | let | var | **ERROR** | | let | let | global | | let | const | global | | const | var | **ERROR** | | const | let | global | | const | const | global | ## JavaScript Objects Definitions * except primitives, all values are object * A JavaScript object is a collection of **named values** * JavaScript objects are containers properties and methods. * how to create an object : * Object Literal : `var person = {age:50, color:"blue"}`; * new : var person = new Object(); person.age = 50; person.color = "blue" (不推薦) * Constructor : Properties * `for(x in person) {txt += person[x];}` // 印出所有values * `person.name = "xx"` // 沒有的properties可直接加 * `delete person.name` // 刪除指定property (both property and property's attribute aka name and value) * JS物件繼承了他們prototype的properties。 * `delete` 無法刪除prototype property,若刪除,繼承prototype的物件全部都會被影響 Methods * Methods are functions stored as object properties. * Adding a Method to an Object `person.name = function () {"new method"};` Display * `person.name` * `for (x in person) {}` * `Object.values(person);` // 只會回傳放value的陣列 * `JSON.stringify(person);` // 會回傳含name value的字串。且methods不顯示,可用`person.method = person.method.toString();` Accessors(getter and setter) * getter and setter ``` var person = { language : "cc" get getlang() { return this.language.toUpperCase(); } set setlang(lang) { this.language.toUpperCase() = lang; } } person.getlang(); person.setlang() = "xxx"; ``` * Object.defineProperty() ``` // Define object var obj = {counter : 0}; // Define setters Object.defineProperty(obj, "reset", {   get : function () {this.counter = 0;} }); Object.defineProperty(obj, "increment", {   get : function () {this.counter++;} }); Object.defineProperty(obj, "decrement", {   get : function () {this.counter--;} }); Object.defineProperty(obj, "add", {   set : function (value) {this.counter += value;} }); Object.defineProperty(obj, "subtract", {   set : function (value) {this.counter -= value;} }); // Play with the counter: obj.reset; obj.add = 5; obj.subtract = 1; obj.increment; obj.decrement; ``` Constructor * constructor and object ``` function Person(first, last, age, eye) { this.firstName = first; this.lastName = last; this.age = age; this.eyeColor = eye; } var myFather = new Person("John", "Doe", 50, "blue"); var myMother = new Person("Sally", "Rally", 48, "green"); myFather.nationality = "English"; // adding property to an Object myFather.name = function () { return this.firstName + " " + this.lastName; }; // adding method to an Object ``` * adding property to a constuctor ``` function Person(first, last, age, eyecolor) { this.firstName = first; this.lastName = last; this.age = age; this.eyeColor = eyecolor; this.nationality = "English"; } // 只能從建構子加,且可以有初始值 ``` * adding method to a constuctor ``` function Person(firstName, lastName, age, eyeColor) { this.firstName = firstName;  this.lastName = lastName; this.age = age; this.eyeColor = eyeColor; this.changeName = function (name) { this.lastName = name;    }; // 同上,只能改建構子 } myMother.changeName("Doe"); ``` Prototype :+1::+1::+1::+1::+1: * All JavaScript objects inherit properties and methods from a prototype. * `prototype` property allows you to add new properties and methods to object constructors: ``` function Person(first, last, age, eyecolor) { this.firstName = first; this.lastName = last; this.age = age; this.eyeColor = eyecolor; } // add new property Person.prototype.nationality = "English"; // add new method Person.prototype.printName = function() { return this.firstName + " " + this.lastName; }; // p是一個*Function instance* var p = new ("1", "3", 34, "red"); // p.printName()找不到,因為p裡面沒有 // 所以js會自動往p.prototype搜尋 // 如果又沒有,則搜尋p.prototype.prototype也就是Object.prototype // 如果又沒有,則搜尋p.prototype.prototype.prototype也就是null // 以上這串就叫做原型鏈 // js的null沒有prototype,所以停止搜尋回傳undefined,而這個null就是原型鏈的終點 // 結論 : 有找到就return,找到底沒找到就return undefined ``` [prototype 原始思想](http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html) * 通常method都寫在prototype不是建構子。因為如果寫在建構子,new 1000個實例就會產生1000個method,這太浪費資源,所以會寫在建構子的prototype,如此該方法只會建立1次而不是1000次。 * `instance.__protyo__ === Constructor.prototype //true` ## JavaScript Functions Definitions * Function Declarations ``` function functionName(parameters) { // code to be executed } // 宣告而已,被invoked才會跑 ``` * Function Expressions ``` // 可以做變數 var x = function (a, b) {return a * b}; var z = x(4, 3); // 這個例子叫做anonymous function(without function name) // 函式存變數的時候可以不用函式名字 ``` * Function Parameters * Parameters and Arguments * parameters是names,arguments是values * Parameters Rules * function definitions don't specify data types for parameter * functions do not perform type checking on the passed arguments * functions do not check the number of arguments received * Parameters Defaults * missing arguments ``` function myFunction(x, y) { if (y === undefined) { y = 10; } return x * y; } // y becomes undefined myFunction(4); // return 40 function (a=1, b=1) { // allows default parameters } ``` * The Arguments Object * arguments 可以被包成 object ``` x = findMax(1, 123, 500, 115, 44, 88); function findMax() { var i; var max = -Infinity; for (i = 0; i < arguments.length; i++) { if (arguments[i] > max) { max = arguments[i]; } } return max; } ``` * If a function is called with **too many arguments** (more than declared), these arguments can be reached using **the arguments object**.(看無) * Arguments are Passed by Value The function only gets to know the values, not the argument's locations. **Changes to arguments are not visible (reflected) outside the function.(看無)** * Objects are Passed by Reference (看無) In JavaScript, object references are values. Because of this, objects will behave like they are passed by **reference:** If a function changes an object property, it changes the original value. **Changes to object properties are visible (reflected) outside the function.** * Function Invocation * myFunction belongs to HTML page ``` function myFunction(a, b) { return a * b; } myFunction(10, 2); // Will return 20 // myFunction(10, 2) and window.myFunction(10, 2) are same ``` * `this` In JavaScript, the thing called `this`, is the object that "owns" the current code. The value of `this`, when used in a function, is the object that "owns" the function. **通常開發會用`var self = this; // global scope` 為了區分local的this跟global的this** * Global Object **我們應該盡量避免使用window object** ``` var x = myFunction(); // x will be the window object function myFunction() { return this; } var x = a;  // x will be undefined var a = function myFunction() {   return this;   } ``` * Invoking a Function as a Method The **fullName** method is a function. The function belongs to the object. **myObject** is the owner of the function. The thing called `this`, is the object that "owns" the JavaScript code. In this case the value of `this` is **myObject**. ``` var myObject = { firstName:"John", lastName: "Doe", fullName: function () { return this.firstName + " " + this.lastName; // return "John Doe" return this; // return [object Object](owner object) } } myObject.fullName(); ``` * Invoking a Function with a Function Constructor If a function invocation is preceded with the `new` keyword, it is a constructor invocation. It looks like you create a new function, but since JavaScript functions are objects you actually create a new object The `this` keyword in the constructor does not have a value. The value of `this` will be the new object created when the function is invoked. ``` // This is a function constructor: function myFunction(arg1, arg2) { this.firstName = arg1; this.lastName  = arg2; } // This creates a new object var x = new myFunction("John", "Doe"); x.firstName;    // Will return "John" ``` * Function Call With the `call()` method, you can write a method that can be used on different objects. * All Functions are Methods If a function is not a method of a JavaScript object, it is a function of the global object. * `this` ``` var person = { firstName:"John", lastName: "Doe", fullName: function () { return this.firstName + " " + this.lastName; } } person.fullName(); // "this" is the person object that "owns" the fullName function ``` * The JavaScript call() Method With `call()`, an object can use a method belonging to another object. ``` var person = { **fullName**: function() { return this.firstName + " " + this.lastName; } } var person1 = { firstName:"John", lastName: "Doe" } var person2 = { firstName:"Mary", lastName: "Doe" } person.fullName.call(**person1**);  // Will return "John Doe" ``` * The JavaScript call() Method ``` var person = { fullName: function(city, country) { return this.firstName + this.lastName + city + country; } } var person1 = { firstName:"John", lastName: "Doe" } person.fullName.call(person1, "Oslo", "Norway"); // 方法叫另一變數且加參數 ``` * Function Apply * apply() method The `apply()` method is similar to the `call()` method. In this example the **fullName** method of **person** is **applied** on **person1** ``` var person = { fullName: function() { return this.firstName + " " + this.lastName; } } var person1 = { firstName: "Mary", lastName: "Doe" } person.fullName.apply(person1);  // Will return "Mary Doe" ``` * The Difference Between call() and apply() The `call()` method takes arguments **separately**. The `apply()` method takes arguments as an **array**. The apply() method is very handy if you want to use an array instead of an argument list. * The apply() Method with Arguments ``` var person = { fullName: function(city, country) { return this.firstName + this.lastName + city + country; } } var person1 = { firstName:"John", lastName: "Doe" } person.fullName.call(person1, "Oslo", "Norway"); // apply 可以用陣列比較方便 person.fullName.apply(person1, ["Oslo", "Norway"]); ``` * Simulate a Max Method on Arrays **arrays** do not have a max() method, you can apply the `Math.max()` method instead. ``` Math.max(1,2,3);  // Will return 3 Math.max.apply(null, [1,2,3]); // Will also return 3 //first argument could be Math, " ", 0 ``` In strict mode, first argument of the apply() is not an object, it becomes the object of the invoked function In "non-strict" mode, it becomes the global object. * JavaScript Closures * Global Variables In a web page, global variables belong to the window object. Global and local variables with the same name are different variables. Modifying one, does not modify the other. Variables created **without** a declaration keyword (`var`, `let`, or `const`) are always global, even if they are created inside a function. * Variable Lifetime Global variables live until the page is discarded, like when you navigate to another page or close the window. Local variables have short lives. They are created when the function is invoked, and deleted when the function is finished. * Nested Functions ``` var add = (function () { var counter = 0; return function () {counter += 1; return counter} })(); add(); // counter = 1 add(); // counter = 2 add(); // counter = 3 ``` * Closure A closure is a function having access to the parent scope, even after the parent function has closed. ``` function doSome() { var x = 10; function f(y) { return x + y; } return f; } var foo = doSome(); foo(20); // 30 foo(30); // 40 ``` 單看f(y)的話, x 好像沒有被定義。 但是f建立了closure,將外部的x **關進(close)** 自己的scope。 若closure型式的函式物件持續存活,那麼x也會繼續存活,可以說延續了x的生命週期。 注意!閉包關閉的是變數,而不是變數所參考的值。 ``` function doOther() { var x = 10; function f(y) { return x + y; } x = 100 return f; } var foo = doOther(); // 建立閉包時,綁定了x變數,而不是數值10(x變數的值) foo(20); // 120 foo(30); // 130 function doOther() { var x = 10; function f(y) { x--; return x; } return f; } var f1 = doOther(); var f2 = doOther(); // 第一次生成,建立值為10的變數x,然後變數x被close起來 // 第二次生成,建立值為10的變數x,然後變數x被close起來 // 所以兩個都return 9 f1(); // 9 f2(); // 9 ``` * typeof and instanceof ### JavaScript ES6 之後才有Class ``` var p = new Person('Justin', 35); 等同於 var p = {}; Person.call(p, 'Justin', 35); ------------------------------------------------------ function Nobody() { } function Person(name, age) { return []; } var n = new Nobody(); var p = new Person(); console.log(n instanceof Nobody); // true console.log(p instanceof Person); // 因為p是[],[]不是Person,所以false console.log(p instanceof Array); // 因為p是[],所以true ``` ```` typeof : Returns the type of a variable ex : String, object, Array , etc... typeof 100; //number typeof (1==1); //boolean typeof 'onepixel'; //string typeof onepixel; // undefined typeof parseInt; // function typeof {} ; //object typeof new Date; //object typeof null;//object typeof undefined; // undefined ```` ```` instanceof : Returns true if an object is an instance of an object type 檢查object是否為某object type的實例或建構式 (a instanceof b a是否為b的實例) ex : var ClassFirst = function(){}; var instance = new ClassFirst(); instance instanceof Object; // true - {} instanceof Object; // true ```` ## AJAX * ajax 支援的回傳格式(dataType) 有 xml, json , script 或是 htm * [ajaxform vs ajaxsubmit](https://www.teakki.com/p/57e10206dafc6eb617b26e8e) * [handle-file-download-from-ajax-post stackoverflow](https://stackoverflow.com/questions/16086162/handle-file-download-from-ajax-post) * [https://stackoverflow.com/questions/1106377/detect-when-browser-receives-file-download](https://stackoverflow.com/questions/1106377/detect-when-browser-receives-file-download) ## javascript工具包 ###### tags: `Front-End` `JavaScript`