JS-2 === ## 函數 > - 定義函數: `function 函數名() { 函數體 }` > - 調用函數: `函數名();` `vi main.js` ```javascript= /* 如果沒有用函數封裝起來, * 而每次想要求1-10的和時, 都要寫一次下面那串code * 那如果要修改成1-20時, 每個都要改 * * 調用函數時, 只要改函數內容就好了 var sum = 0; for (var i=1; i<=10; i++) { sum += i; } console.log(sum); */ /* 定義函數: function 函數名() { 函數體 } */ function sumFn() { var sum = 0; for (var i=1; i<=10; i++) { sum += i; } console.log(sum); } /* 調用函數: 函數名() */ // sumFn() ``` `main.html` ```htmlmixed= <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <!-- 引入js, 類似python的 import --> <script src='main.js'></script> <script> // 調用函數 // 愛怎麼調, 就怎麼調 sumFn(); // 55 sumFn(); // 55 sumFn(); // 55 </script> </head> <body> </body> </html> ``` ### 參數 > - 形參: 沒有具體的值, 僅用來佔位用, 類似`var 變量;` > - 實參: 對形參附值 > - 跟Python不一樣的地方在: JS沒有限定幾個形參就要幾個實参 ```javascript= /* 語法: function 函數名(形參1, 形參2, ...) { // 函數體 } function(實參1, 實參2,...) */ /* 把形參看成變量, 把實參看成對變量附值即可 */ function getSum(num1, num2) { console.log(num1+num2) } getSum(1,2) // 3 /* 變量視角 var num1; var num2; num1=1; num2=2 */ getSum(100,200) // 300 // 傳變量 var x = 10; var y = 11; getSum(x,y); // 函數內部修改值, 不會影響外面實參的值, // 因為調用的時候是實參複製一份給形參 /* 類似於 a = 100 // 100 b = a // 100 b = 200 // 200 a // 100 > 沒變 */ function getNum(a, b) { a = 100; b = 200; console.log(a,b); } var a = 1; var b = 2; getNum(a,b) // 100 200 console.log(a,b)// 1 2 // Q. 求圓面積 pi * r * r function circus(r) { var pi = 3.1415926; console.log(pi*r*r); } circus(3); // Q. 求三個數中的最大值 // 解一 function getMax(num1, num2, num3) { if (num1>num2) { if (num1>num3) { console.log('MaxNum = ' + num1); } else { console.log('MaxNum = ' + num3); } } else { if (num2>num3) { console.log('MaxNum = ' + num2); } else { console.log('MaxNum = ' + num3); } } } // 解二 function getMax(num1, num2, num3) { if (num1>num2 && num1>num3) { console.log('MaxNum = ' + num1); } else if (num1>num2 && num1<num3) { console.log('MaxNum = ' + num3); } else if (num1<num2 && num2>num3) { console.log('MaxNum = ' + num2); } } getMax(2,1,4); // MaxNum = 2 getMax(10,20,5); // MaxNum = 20 getMax(30,20,5); // MaxNum = 30 // Q. 求n個數的最大值 function getMax(arr) { var max = arr[0] // 假設第一個是最大的 for (i=1; i<arr.length; i++) { if (max < arr[i]) { max = arr[i] } } console.log('MaxNum = ' + max); } arr = [123,1,56,23,55]; getMax(arr); // Q. 判斷一個數是否為質數 function judgePrime(num1) { var isPrime = true; // 假設每個數都是質數 for (i=2; i<num1; i++) { if (num1%i === 0) { // 如果沒有餘數, 表示可以被整除而不是質數 isPrime = false; // 改為不是質數 break; // 既然確定不是質數, 就不用再繼續判斷了 } } if (isPrime) { // 輸出 console.log('質數') } else { console.log('不是質數') } } judgePrime(4); // 不是質數 judgePrime(5); // 質數 judgePrime(6); // 不是質數 judgePrime(7); // 質數 // 沒有限定幾個形參就要幾個實參 /* Python嚴格限定 * 實參 > 形參 In [16]: def test(): ...: print('haha') In [17]: test(1,2,3) # test() takes 0 positional arguments but 3 were given * 形參 > 實參 In [18]: def test(a,b,c): ...: print('haha') In [19]: test() # test() missing 3 required positional arguments: 'a', 'b', and 'c' */ function test(a,b,c) { console.log(a,b,c); } test() // undefined undefined undefined : 實參不足就都補undefined test(1) // 1 undefined undefined test(1,2,3,4) // 1 2 3: 實參超過也沒差 ``` ### 函數返回值 > - 函數沒有返回值時, 會返回undefined > - return 後面可以不寫東西, 返回 undefined > - return 後面跟的東西就是返回值 > - return 會結束函數執行 ```javascript= /* 語法: return function 函數名(形參..) { // 函數體 return 返回值; } */ // Q. 求一組數的最大值 function getMax(arr) { var max = arr[0]; for (i=1; i<arr.length; i++) { if (max < arr[i]) { max = arr[i]; } } return max; // 返回 } var arr = [32,55,23,4,11,2]; var max = getMax(arr); console.log(max); // 55 // Q. 求一組數的最小值 function getMin(arr) { var min = arr[0]; for (i=1; i<arr.length; i++) { if (min > arr[i]) { min = arr[i]; } } return min; // 返回 } var arr = [32,55,23,4,11,2]; var min = getMin(arr); console.log(min); // 2 // Q. 求階乘 5! = 5*4*3*2*1 = 120 function getFactorial(num) { var multiply = 1; for (i=1 ; i<=num; i++) { multiply *= i; } return multiply; } var multiply = getFactorial(5); console.log(multiply); // 120 // Q. 求 1!+2!+3!+..n! // !! 發現一個問題, 如果我兩個 i 都沒有用var 定義時, 答案會是127, 為何? function getFactorial(num) { var multiply = 1; for (var i=1 ; i<=num; i++) { multiply *= i; } return multiply; } function getSum(n) { var sum = 0; for (var i=1; i<=n; i++) { /* 這樣調用了兩次, 效率不高, 拿個東西把getFactorial存起來 sum += getFactorial(i); console.log(i, getFactorial(i)) */ multiply = getFactorial(i); sum += multiply; console.log(i, multiply); } return sum; } var resault = getSum(5); console.log(resault); // 153 // 函數沒有返回值時, 會返回undefined /* Python 是返回None In [14]: def test(): ...: print('haha') In [15]: print(test()) haha None */ function test(a,b) { console.log(a,b); // 5 6 } var r = test(5,6); console.log(r); // undefined // return 會結束函數執行 // return 後面可以不寫東西, 返回 undefined /* python是返回None In [12]: def test(): ...: return In [13]: print(test()) None */ function test(a,b) { return; console.log(a,b); // 沒有執行 } var r = test(5,6); console.log(r); // undefined ``` ### arguments > - JS 所有函數都內建一個 arguments 屬性 > - arguments 對象存儲了所有傳進來的實參 > - arguments 可以進行遍歷 > - 看起來類似 python 的 *args 的作用, 用在參數數量不固定時 ```javascript= /* 複習python *args, **kwargs * python參數有兩種: * - 位置參數(position argument) * - 關鍵字參數(keyword argument) * * - *args 傳遞無名可變長參數列表(位置參數), 本質上就是 tuple * - **kwargs 傳遞關鍵字可變長參數列表(關鍵字參數), 本質上就是 dict * * - 如果兩個同時使用時, 由於位置參數需要位置對應, 故*args 應該在**kwargs前面 * def fn(*args, **kwargs): pass fn(*args, **kwargs) */ // arguments 對象存儲了所有傳進來的實參 function test() { console.log(arguments); } test(1,2,3) // Arguments(3) [1, 2, 3, ...] // 0: 1 > 有各種位置 // 1: 2 // 2: 3 // length: 3 > 也有長度屬性 // arguments 可以進行遍歷 // Q. 求任意個數的最大值 function maxNum() { var max = arguments[0] for (var i=1; i<arguments.length; i++) { if (max < arguments[i]) { max = arguments[i]; } } console.log(max); } maxNum(44,33,52,1,43,12) // 52 // Q. 求任意個數的和 function getSum() { var sum = 0; for (i=0; i<arguments[i]; i++) { sum += arguments[i]; } console.log(sum); } getSum(1,2,3,4,5); // 15 ``` ### 函數封裝練習 ```javascript= // Q. 求 Fibonacci 數列中第n個數是多少? (1,1,2,3,5,8,...) function getFib(n){ var firstFib = 1; var secondFib = 1; var fib ; for (var i=3; i<=n; i++) { fib = firstFib + secondFib; firstFib = secondFib; secondFib = fib; } console.log(fib); } getFib(3); // 2 getFib(4); // 3 getFib(5); // 5 getFib(6); // 8 // Q. 返回一個反轉陣列 [1,2,3,4,5] -> [5,4,3,2,1] function reverseArr(arr) { var newArr=[]; for (i=arr.length-1; i>=0; i--) { newArr[newArr.length] = arr[i] } return newArr } var arr = [1,2,3,4,5]; console.log(reverseArr(arr)); var arr2 = ['toyz', '刷漢堡', '包鬼', '光頭哥哥', 'RIP']; console.log(reverseArr(arr2)); // Q. 冒泡排序, 由小至大 function bubbleSort(arr) { for (i=1; i<arr.length; i++) { var isSort = true; for (j=0; j<=arr.length-i; j++) { if (arr[j] > arr[j+1]) { var temp = arr[j+1]; arr[j+1] = arr[j]; arr[j] = temp; isSort = false; } } if (isSort) { break; } } return arr; } var arr = [54,26,93,17,77,31,44,55,20]; console.log(bubbleSort(arr)); // Q. 判斷某年份是否為閏年(能被4整除且不能被100整除, 或能被400整除) function judgeLeapYear(n) { if (n%4 === 0 && n%100 !== 0 || n%400 === 0) { console.log('閏年'); } else { console.log('不是閏年'); } } judgeLeapYear(400); // 閏年 judgeLeapYear(88); // 閏年 judgeLeapYear(200); // 不是閏年 // Q. 輸入年月日, 計算天數 // 必須判斷是否為閏年 function judgeLeapYear(n) { var resault = false; if (n%4 === 0 && n%100 !== 0 || n%400 === 0) { resault = true; } return resault; } // 累加天數 function getDays(year, month, day) { var days = day; // 先把當月的天數加上去 for (var i=1; i<month; i++){ /* 用if的話太麻煩了, * 這邊全部都是判斷 ===, 所以改用 switch 會簡潔很多 if (i===1) { days += 31; }else if (i===3) { days += 31; } */ switch (i) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: days += 31; break; case 4: case 6: case 9: case 11: days += 30; break; case 2: if (judgeLeapYear(year)){ days+=29; } else { days+=28; } break; } // console.log(days); } return days; } getDays(2019,11,19) // 323 ``` ### 匿名函數 > - 語法: `function() {函數體}` > - 匿名函數不能單獨存在 > - 可以賦值給一個變量(函數表達式) > - 可以匿名函數自調用(自調用函數) ```javascript= // 匿名函數 /* JS 語法 function() { // 函數體 } */ /* 函數表達式 var fn = function() { # 後面是匿名函數 // 函數體 } fn() */ var fn = function() { console.log('haha'); } fn(); // haha /* Python匿名函數 lambda 參數: 式子 a = lambda x,y: x+y # 匿名表達式 a(1,2) # 3 */ /* 自調用函數 * 匿名函數用小括號刮起來, 變成一個表達式, 後面再加一個調用的小括號 (function() { // 函數體 })() */ (function() { console.log('haha'); })() // haha ``` ### 函數是一種數據類型 > - 可作為參數 > - 可作為返回值 ```javascript= var fn = function() {} console.log(typeof fn); // function // 函數作為參數 var fn = function() { console.log('haha') } var fn2 = function(fn) { fn(); } var a = 100 fn2(fn); // haha fn2(a); // 傳遞非函數進去 > fn is not a function // 函數作為返回值 /* python In [33]: def fn(a): ...: b = 100 ...: def fn2(): ...: print(a+b) ...: return fn2 # 函數當成返回值 In [34]: fn(5) Out[34]: <function __main__.fn.<locals>.fn2()> In [35]: a = fn(5) In [36]: a() 105 * Python的閉包(帶有數據的大功能編寫)常用這個技巧 */ function test(a) { var b = 10; return function() { console.log(a+b); } } var fn = test(5); console.log(typeof fn); // function : 返回了一個函數 fn(); // 15 : 調用返回的函數 ``` - 函數中如果需要 window ,要用傳的 - 避免如果壓縮後,window 這個單詞背壓縮成某個字,造成不是指向 window 對象 - 減少查找,提高效率 ``` ;(function (window) { window.xxx = ooo })(window); ``` - 立即執行函數最好接收一個真實的 undefined - 因為舊的瀏覽器 undefined 是可以被修改的,規範上他不是保留字 - 為了避免 undefined 被人改過而產生問題,接收一個沒有傳參的形參就能保證作用域內的 undefined 是真正的 undefiend ``` ;(function (undefined) { })(); ``` ## JS 基本規範 ```javascript= // 命名規範 // - 命名要有意義 // - 變量常用名詞(代表一個東西) // - 函數常用動詞(做一件事情) // 變量規範 // - 操作符號前後要有空格 // var a = 1000; // 註釋規範 // - 註釋後面空一格 // xxxx // 空格規範, 換行規範 var name = 'xx'; if (true) { } for (var i = i; i <= 10; i++) { } function fn() { } ``` ## 作用域 > - 作用域: 變量或函數起作用的地方 > - ECMAScript中, 作用域有兩種 > - 全局作用域 > - 全局作用域定義的『全局變量』, 任何地方都能訪問 > - 關閉網站後, 全局變量所佔的內存會被回收 > - 局部作用域 > - 任何函數內部都有一個局部作用域, > - 在局部作用域中定義的局部變量, 只有該函數內可用 > - 函數執行完後,局部變量所佔內存會被回收 > - 塊級作用域 > - 塊級: 任何一個()或{} 包起來的區域都屬於一個塊 > - 在塊級內定義的東西, 塊級外不可以直接用 > - ==**ES5 之前,都沒有塊級作用域**== > - 作用域鏈 > - 全局作用域> 0級鏈 > - 函數內定義的東西 > 1級鏈 > - 函數內定義函數, 內圈函數裡定義的東西 > 2級鏈 ```javascript= // 全局變量 // 一般寫法 var a = 10; // 全局變量 function test() { console.log(a); } test(); // 10 // 特殊寫法 function test() { a = 10; // 沒有var, 執行後也是丟到全局變量, 不過很少人這樣寫 } test(); console.log(a); // 10 // 局部變量 function test() { var a = 10; console.log(a); // 10 } test(); console.log(a); // a is not defined // 塊級作用域 { var a = 10; console.log(a); // 10 } console.log(a); // 10 > 現在還沒有塊級作用域 // 作用域鏈 function test() { var a = 10; function test2() { var b = 10; } } var c = 10; // 0級鏈: test, c // 1級鏈: a test2 // 2級鏈: b print(a) a =10 ``` ## 預解析 > - Js是由Js解析器執行, Js解析器執行code會有兩個步驟: > 1. 預解析 > 2. code執行 > - 預解析 > - 把變量聲明提升到最上面, 只提升聲明, 不提升附值, 聲明時的值為 undefined > - 把函數聲明提升到最上面, 只提升聲明, 不提升調用 > - 先提升var, 再提升func > - 預解析的過程中, 如果函數和變量同名, 函數優先 > - 提升完後再 `放置` 可執行代碼 ```javascript= // 神奇的問題 console.log(a); // undefined > 沒有炸裂!因為預解析把聲明丟到前面了 var a = 10; // 預解析後 var a; console.log(a); a = 10; // 神奇的問題 a(); // 10 > 也沒有炸裂! 因為預解析把聲明函數往上丟了 function a() { console.log(10); } // 預解析後 function a() { console.log(10); } a(); // 神奇的問題 var a = 25; function abc() { console.log(a); var a = 10; } abc(); // // 預解析 var a; function abc() { // 局部作用域 預解析 var a; console.log(a); a = 10; } a = 25; abc(); // undefined // 神奇的問題: 函數優先 console.log(a); // a() { console.log('aaaaa'); } function a() { console.log('aaaaa'); } var a = 1; console.log(a); // 1 // 預解析 var a; // var 高於 function function a() { console.log('aaaaa'); } console.log(a); // a() { console.log('aaaaa'); } a = 1; console.log(a); // 1 // Q var a = 18; f1(); function f1() { var b = 9; console.log(a); // undefined console.log(b); // 9 var a = '123'; } // 預解析 var a; function f1() { var b; var a; b = 9; console.log(a); // undefined console.log(b); // 9 a = '123'; } a = 18; f1(); // Q 重要~ f1(); console.log(c); console.log(b); console.log(a); function f1() { var a = b = c = 9; console.log(a); console.log(b); console.log(c); } // 預解析 function f1() { // var a = b = c = 9; var a; // 局部變量 a = 9; // 沒有var, 全局變量! b = 9; c = 9; console.log(a); console.log(b); console.log(c); } f1(); // 9 9 9 console.log(c); // 9 console.log(b); // 9 console.log(a); // a is not defined ``` ## 對象 ### 對象是什麼 > - 對象是一個「具體」的事務, 具有特徵與行為 > - 「具體」事務: > - 手機 > 不是對象, 是一個類型(包含世界上所有的手機) > - 我手上這支手機 > 對象 > - 特徵與行為 > - 特徵: > - 名詞, 描述這個對象 > - Note7, 32G, 有裂痕, 原廠手機殼 > - 行為 > - 動詞, 表示對象可以幹嘛 > - 上網, 打電話, 丟炸彈遊戲 ### JS中的對象 > - JS對象是無序屬性 (類似dict> key:value,) > - 由屬性和方法組成, 每個屬性方法用 `,` 隔開 > - 屬性 = 特徵 > - 方法 = 行為 > - 對象與函數的區別 > - 對象: 封裝屬性方法 > - 函數: 封裝一串code > - 對象內方法調用對象的屬性值: `this`, > - 類似 python 實例類裡的實例方法要調用實例屬性用 self ```javascript= // 創建對象 var myPhone = { // 對象: 我的手機 label: 'Samsung', // 屬性(特徵): 三星Note7 model: 'Note7', callMom: function () { // 方法(行為): 打給老媽 console.log(0987654321); } } // 調用方法 myPhone.callMom(); // 987654321 > 0 怎麼不見了=.= // 獲取屬性 console.log(myPhone.label); // "Samsung" console.log(myPhone.model); // "Note7" // this 在方法中代表當前對象 // 類似python的 self var bigBro = { name: 'machi', speak: function () { console.log(this.name + '底加'); } } console.log(bigBro.name) // machi bigBro.speak(); // machi底加 /* python: class class BigBro(object): def __init__(self): self.name = 'machi' def speak(self): print(self.name + '底加') print(BigBro().name) # machi BigBro().speak() # machi底加 */ // 另外一種調用屬性的方式, 其實就是dict取值的方式 array['key'] console.log(bigBro['name']); // machi console.log(bigBro['speak']); // f bigBro['speak']() // machi底加 // Q. 創造李星 var leeSin = { name: 'LeeSin', health: 575, healthRegen: 7.5, armor: 33, magicResist: 32.1, energy: 200, energyRegen: 50, attackDamage: 70, attackSpeed: 0.651, critDamage: 2, range: 125, rangeType: 'Melee', moveSpeed: 345, levelUp: function() { this.health += 85; this.healthRegen += 0.7; this.armor += 3.7; this.magicResist += 1.25; this.attackDamage += 3.2; }, Q: function() { // 略, 打個名字意思意思就好了 console.log('虎嘯龍吟/震驚百里'); }, W: function() { console.log('鐵璧金身/易筋洗髓'); }, E: function() { console.log('狂風暴雨/分筋錯骨'); }, R: function() { console.log('神龍擺尾'); } } console.log(leeSin.name); // LeeSin console.log(leeSin.health); // 575 leeSin.levelUp(); console.log(leeSin.health); // 660 leeSin.Q(); // 虎嘯龍吟/震驚百里 ``` ### 對象創建 > #### 字面量 > - 11, '11', [], {}, true, ... ```javascript= var dog = { name: '小黑', bark: function () { console.log(this.name + 'wonwon'); } } dog.bark(); // 小黑wonwon ``` > #### new Object() > - `Object` 是一個構造函數 > - 就是 python 類的父層 > - 通過 `new` 的方式來調用 `Object` > - `new` 看起來是創建實例的意思, > - 也就是 python 的 \_\_new__方法 > - \_\_new__是在創建類實例對象的過程中最先調用的方法, 並返回類實例對象 > - `new Object()` 創建一個空的對象 > - 透過動態添加來添加屬性方法 ```javascript= var dog = new Object(); // 創造一個對象 console.log(dog.name); // undefined: 對象裡面是空的, 當然也沒有name // 動態添加屬性 dog.name = '小黑'; console.log(dog.name); // 小黑 // 動態添加方法 dog.bark = function() { console.log(this.name + 'wonwon'); } dog.bark(); // 小黑wonwon ``` > #### 函數創建對象 > - 用一個函數放一些形參 > - 函數裡面創造一個空對象 > - 接著運用動態添加把東西丟進去 > - 最後返回對象 ```javascript= // 問題: // 當我想要創建多隻狗時, 一個一個創會有很多重複的內容 var dog1 = { name: '小黑', bark: function () { console.log(this.name + 'wonwon') } } var dog2 = { name: '小黃', bark: function () { console.log(this.name + 'wonwon') }, } ``` ```javascript= // 解決: 函數創建對象 function creatDog(name, age) { var dog = new Object(); dog.name = name; dog.age = age; dog.bark = function () { console.log(this.name + 'wonwon'); } return dog; } var yellowDog = creatDog('小黃', 18); var blackDog = creatDog('小黑', 20); yellowDog.name // "小黃" yellowDog.age // 18 yellowDog.bark() // 小黃wonwon blackDog.name // "小黑" blackDog.age // 20 blackDog.bark() // 小黑wonwon /* python 寫寫看 In [1]: def createDog(name, age): ...: class Dog(object): ...: def __init__(self, name, age): ...: self.name = name ...: self.age = age ...: def bark(self): ...: print(self.name + 'wonwon') ...: dog = Dog(name, age) ...: return dog In [2]: yellowDog = createDog('小黃', 18) In [3]: yellowDog.name # '小黃' In [4]: yellowDog.age # 18 In [5]: yellowDog.bark() # 小黃wonwon */ ``` > #### 自定義構造函數 > - 命名首字母要大寫(Pascal Case) > - 就是python的類 ```javascript= function CreateDog(name, age) { this.name = name; this.age = age; this.bark = function () { console.log(this.name + 'wonwon'); } } var yellowDog = new CreateDog('小黃', 18); var blackDog = new CreateDog('小黑', 20); // 就是調用 python 的 __new__ // 返回一個實例對象 yellowDog.name // "小黃" yellowDog.age // 18 yellowDog.bark() // 小黃wonwon blackDog.name // "小黑" /* python 對象 In [1]: class Test(object): ...: def __init__(self, name, age): ...: self.name = name # 類屬性 ...: self.age = age ...: def bark(self): # 類方法 ...: print(self.name + 'wonwon') In [2]: a = Test('小黃', 18) # 實例屬性 In [3]: a.name # '小黃' In [4]: a.age # 18 In [5]: a.bark() # 小黃wonwon In [6]: b = Test('小黑', 20) In [7]: b.name # '小黑' In [8]: b.age # 20 In [9]: b.bark() # 小黑wonwon */ ``` ### new > - new 做了什麼事 > - 創建一個新對象 > - 讓this指向該對象 > - 執行構造函數(添加屬性方法等) > - 返回對象指向 ```python # python的 __new__ 函數 def __new__(cls, clsName, clsBases, clsDict): return type(clsName, clsBases, clsDict) ``` ### this ```javascript= // 函數中的 this 指向 Window function fn() { console.log(this) } fn() // Window // 方法中的 this 指向對象方法所屬的對象 var obj = { a: 100, fn: function () { console.log(this); } } obj.fn() // {a: 100, fn: ƒ} // 構造函數中的 this 指向構造函數所創建的對象 function Fn() { a = 10; this.b = 100; console.log(this); } var cls = new Fn(); // Fn {b: 100} ``` ### 遍歷屬性 ```javascript= /* python fn = {'name': 'Toyz', 'age': 25, 'isBoy': True} In [4]: fn = {'name': 'Toyz', 'age': 25, 'isBoy': True} ...: for i in fn: ...: print(i + ': ' + str(fn[i])) name: Toyz age: 25 isBoy: True */ // 這裡有一個對象, 要怎麼遍歷屬性? var fn = { name: 'Toyz', age: 25, isBoy: true } // for in 會遍歷 key 出來 for (var key in fn) { console.log(key); // name age isBoy } // 獲取值的方法 // - fn.key > 只能指定固定的某個屬性 for (var key in fn){ console.log(key + ': ' + fn.key) } // name: undefined // age: undefined // isBoy: undefined // fn.key > 指定 key屬性的值, 可是沒有 key 屬性, 所以找無 // - fn['key'] > 以 str 的形式帶入變量去 dict 內查找 for (var key in fn){ console.log(key + ': ' + fn[key]) } // name: Toyz > 'key' + ': ' + fn['key'] // age: 25 // isBoy: true // 循環附值 var fn = {}; for (i=0; i<=10; i++) { fn['第' + i + '個'] = i; } console.log(fn); // {第0個: 0, 第1個: 1, 第2個: 2, 第3個: 3, 第4個: 4, …} for (var key in fn) { console.log(key + ': ' + fn[key]); } ``` ### 刪除屬性 `delete function['key']` ```javascript= /* python In [13]: b = {'A': 1, 'B': 2} In [15]: b['A'] # 1 In [17]: del b['A'] In [18]: b # {'B': 2} */ // 刪除屬性 var fn = { name: 'Toyz', age: 25, isBoy: true } fn['name'] // "Toyz" delete fn['name'] // true fn // {age: 25, isBoy: true} delete fn.age // true fn // {isBoy: true} ``` ## 簡單數據類型與複雜數據類型區別 > - 簡單數據類型(值類型) string, number, boolean, undefined, null > - 存儲在棧中 > - 變量存儲的是值本身, 所以又稱值類型 > - 複雜數據類型(引用類型) object > - 存儲在堆中 > - 變量存儲的是引用, 所以又稱引用類型 > - ps. JS沒有棧堆的概念 ```javascript= // 簡單數據類型的存儲方法 var a = 10; var b = a; a = 20; 棧 棧 _______ _______ | | | | | | | | | | | | b | 10 | b | 10 | a | 10 | -> a | 20 | |_____| |_____| ``` ```javascript= // 複雜數據類型的存儲方法 var fn = function (name, age) { this.name = name; this.age = age; return this; } var p = fn('Toyz', 25); var p2 = p; 棧 堆 ____________ ____________ | | | | | | | | | | | | | | | | p2 | 0x112233 | -------> 0x112233 | name age | p | 0x112233 | ---------^ | | |__________| |__________| p.name // 'Toyz' p.age // 25 p2.name // 'Toyz' p2.name = 'GodJJ' p2.name // 'GodJJ' p.name // 'GodJJ' ``` ```javascript= // 簡單類型作為參數 function fn(x,y) { x+=1; y+=1; console.log(x, y); } var x = 10; var y = 20; fn(x,y); console.log(x, y); // 預解析 var x; // x | undefined | var y; // y | undefined | function fn(x,y) { // fn | 0x11aa33 | x+=1; // 0x11aa33 | x+=1 | y+=1; // | y+=1 | console.log(x,y) } x = 10; // x | 10 | y = 20; // y | 20 | fn(x,y); // > 0x11aa44 // 0x11aa44 | x = 10; y = 10; | // | x+=1; y+=1; | > x = 11; y = 21; console.log(x,y) // 10 20 棧 堆 ____________ __________________ | | | | | | | | | | | ______________ | fn | 0x11aa33 | -------> 0x11aa33 | |x=undefined; | | y | undefined| | |y=undefined; | | x | undefined| | |x+=1; | | | | | |y+=1; | | | | | |_____________| | |__________| |_________________| x = 10, y = 20 棧 y | 10 | x | 20 | fn(10, 20) // > 0x11aa44 堆 | ______________ | 0x11aa33 | |x=undefined; | | | |y=undefined; | | | |x+=1; | | | |y+=1; | | | |_____________| | | ______________ | 0x11aa44 | |x=10; | | > console.log(x, y); // 11 21 | |y=20; | | // 執行完後, 由於沒有變量指向這個地址, | |x+=1; /*11*/ | | // 所以釋放記憶體 | |y+=1; /*21*/ | | | |_____________| | ``` ```javascript= // 複雜數據類型作為參數 function Person(name, age) { this.name = name; this.age = age; } var p = Person('Toyz', 28); function fn(person) { person.name = 'GodJJ'; } fn(p) console.log(p.name); // 預解析 var p; // p | undefined | function Person(name, age) { // Person | 0x001122 | // 0x001122 | name = undefined; | // 0x001122 | age = undefined; | this.name = name; // 0x001122 | this.name = name; | this.age = age; // 0x001122 | this.age = age; | } function fn(person) { // fn | 0x001133 | // 0x001133 | person = undefined; | person.name = 'GodJJ'; // 0x001133 | person.name = 'GodJJ' | } p = new Person('Toyz', 28); // p | 0x001144 | // 0x001144 | p.name = 'Toyz' | // 0x001144 | p.age = 28 | fn(p); console.log(p.name); 棧 堆 ____________ __________________________ | | | ______________________ | fn | 0x001133 |-----> 0x001133 | |person = undefined; | | | | | |person.name = 'GodJJ'| | p | undefined | | |_____________________| | | | | ______________________ | Person | 0x001122 |-----> 0x001122 | |name = undefined; | | | | | |age = undefined; | | | | | |this.name = name; | | | | | |this.age = age; | | | | | |_____________________| | |__________ | |_________________________| | var p = new Person() | v v ____________ __________________________ | | | ______________________ | fn | 0x001133 |-----> 0x001133 | |person = undefined; | | | | | |person.name = 'GodJJ'| | | | | |_____________________| | | | | ______________________ | Person | 0x001122 |-----> 0x001122 | |name = undefined; | | | | | |age = undefined; | | | | | |this.name = name; | | | | | |this.age = age; | | | | | |_____________________| | | | | ______________________ | | | | |name = 'Toyz'; | | | | | |age = 28; | | p | 0x001144 |-----> 0x001144 | |this.name = name; | | | | | | /* Toyz */ | | | | | |this.age = age; | | | | | | /* 28 */ | | | | | |_____________________| | |__________ | |_________________________| | fn(0x001144) | v V | | | ______________________ | | | | |name = 'GodJJ'; | | | | | |age = 28; | | person | 0x001144 |-----> 0x001144 | |this.name = name; | | | | | | /* GodJJ */ | | | | | |this.age = age; | | | | | | /* 28 */ | | | | | |_____________________| | console.log(p.name) // console.log(0x001144.name) > 'GodJJ' // 往下延伸 var p; function Person(name, age) { this.name = name; this.age = age; } function fn(person) { person.name = 'GodJJ'; person = new Person('Gear', 30); // 加這兩行的結果? console.log(person.name) } p = new Person('Toyz', 28); fn(p); console.log(p.name); fn(0x001144) | | | ______________________ | | | | |name = 'GodJJ'; | | | | | |age = 28; | | person | 0x001144 |-----> 0x001144 | |this.name = name; | | | | | | /* GodJJ */ | | | | | |this.age = age; | | | | | | /* 28 */ | | | | | |_____________________| | | person = new Person('Gear', 30); | v v | | | ______________________ | | | | |name = 'Gear'; | | | | | |age = 30; | | person | 0x001155 |-----> 0x001155 | |this.name = name; | | | | | | /* Gear */ | | | | | |this.age = age; | | | | | | /* 30 */ | | | | | |_____________________| | console.log(person.name) // console.log(0x001155.name) > 'Gear' console.log(p.name); // console.log(0x001144.name) > 'GodJJ' // 陣列 var i = [1,2,3]; function fn(array) { array[0] = 6; } fn(i); console.log(i); // [6, 2, 3] __________ __________________ i | 0x0011aa | 0x0011aa | 0x0011aa[0] = 1 | | | | 0x0011aa[1] = 2 | | | | 0x0011aa[2] = 3 | | | fn | 0x0011bb | 0x0011bb | array = undefined | | | | array[0] = 6 | |__________| |___________________| fn(i) // fn(0x0011aa) > 0x0011cc i | 0x0011aa | 0x0011cc | array = 0x0011aa | fn | 0x0011bb | | 0x0011aa[0] = 6 | // 0x0011cc 釋放 array | 0x0011aa | 0x0011aa | 0x0011aa[0] = 6 | | 0x0011aa[1] = 2 | | 0x0011aa[2] = 3 | console.log(i); // console.log(0x0011aa) > [6,2,3] // 冒泡排序 function bubbleSort(array) { for (i=1; i<arr.length; i++) { var isSort = true; for (j=0; j<=arr.length-i; j++) { if (arr[j] > arr[j+1]) { var temp = arr[j+1]; arr[j+1] = arr[j]; arr[j] = temp; isSort = false; } } if (isSort) { break; } } } var arr = [54,26,93,17,77,31,44,55,20]; console.log(bubbleSort(arr)); // undefined 沒有返回值 console.log(arr) // [17, 20, 26, 31, 44, 54, 55, 77, 93] // 流程 // 創建 _____棧_____ _________堆_________ bubbleSort | 0x0011aa | 0x0011aa | array = undefined | | xxx | arr | 0x0011bb | 0x0011bb | [,,] | // 運行函數 bubbleSort(arr) // bubbleSort(0x0011bb) > 0x0011cc ________堆_________________ 0x0011cc | array = 0x0011bb | | /*直接對0x0011bb修改排序*/ | // 0x0011cc 沒有人指向 > 釋放 ___棧______ bubbleSort | 0x0011aa | arr | 0x0011bb | array | 0x0011bb | ``` ## 內置對象 > - JS 組成: ECMAScript BOM DOM > - ECMAScript: > - 變量, 註釋, 數據類型, 操作符, > 流程控制語句 (判斷與循環), 數組, 對象, 構造函數, 內置對象 > - JS 對象種類: 自定義對象, 內置對象, 瀏覽器對象 > - ECMAScript: 自定義對象, 內置對象 > - 內置對象: Math, Array, Date, ... > - 這東西太多了, 需要的時候可以去 [MDN](https://wiki.developer.mozilla.org/zh-CN/) 找 ## Math > - Math不是一個構造函數 > - 使用上就是 `Math.xxx` > - 複習: 構造函數調用 `var xx = new Xoo()` ```javascript= var MyMath = { Pi: 3.14, max: function () { pass } } MyMath.PI MyMath.max() ``` ### Math.PI ```javascript= // 圓面積 function circleArea(radius) { return 2 * Math.PI * radius } circleArea(3); ``` ### Math.random() > 隨機產生 \[0~1\) 的 float > - `[`: 包含等於 > - `)`: 不包含等於 > - `[0~1)`: 0 >= x > 1 ```javascript= // 求 [10~100) 隨機float function getRandomFNumber(min, max) { return Math.random() * (max-min) + min } getRandomFNumber(10, 100); // 求 [10~100) 隨機整數 function getRandomINumber(min, max) { return parseInt(Math.random() * (max-min) + min) } getRandomINumber(10, 100); // 求 [10~100] 隨機整數 function getRandomInt(min, max) { return Math.floor(Math.random() * (max-min+1) + min) } getRandomInt(10, 100) // Math.random() * (91) + 10 // 0 + 10 // 99.9999 * 91 + 10 = 100.99909000000001 // Math.floor(100.99909000000001) = 100 // 隨機生成RGB [0~255] // rgb(num, num, num) function getRandomRGB() { var r = Math.floor(Math.random() * 256) // (255-0+1) + 0 var g = Math.floor(Math.random() * 256) var b = Math.floor(Math.random() * 256) var rgb = 'rgb: (' + r + ' ,' + g + ' ,' + b + ')' return rgb } ``` ### Math.floor() / Math.ceil() / Math.round() > - `Math.floor(Num)`: 向下取整 > - `Math.ceil(Num)`: 向上取整 > - `Math.round()`: 四捨五入取整 ```javascript= Math.floor(10.4) // 10 Math.floor(7.8) // 7 Math.ceil(10.4) // 11 Math.ceil(7.8) // 8 Math.round(7.8) // 8 Math.round(10.4) // 10 ``` ### Math.abs() > `Math.abs(Num)`: 絕對值 ```javascript= Math.abs(10.4) // 10.4 Math.abs(-10.4) // 10.4 ``` ### Math.max() / Math.min() > - `Math.max([value1[, value2[, ...]]])` > - `Math.min([value1[, value2[, ...]]])` > - 中括號表示可選參數 ```javascript= // 沒有參數 Math.max() // -Infinity Math.min() // Infinity // 不能轉換成 num 得值 Math.max('google') // NaN Math.max(23,41,5) // 41 Math.max(23,'41',5) // 41 // Q. 模擬Math.max() / Math.min() // 19.11.22 第一版 var MyMath = { max: function (array) { var typeOfArr = typeof array if (typeOfArr == 'undefined') { var max = -Infinity return max } if (typeOfArr == 'number') { var max = arguments[0] for (i=1; i<arguments.length; i++) { if (max < arguments[i]) { max = arguments[i]; } } return max } if (typeOfArr == 'object') { var max = array[0] for (i=1; i<array.length; i++) { if (max < array[i]) { max = array[i]; } } return max } }, min: function (array) { var typeOfArr = typeof array; if (typeOfArr == 'undefined') { var min = Infinity; return min } if (typeOfArr == 'number') { var min = arguments[0]; for (i=1; i<arguments.length; i++) { if (min > arguments[i]) { min = arguments[i]; } } return min } if (typeOfArr == 'object') { var min = array[0]; for (i=1; i<array.length; i++) { if (min > array[i]) { min = array[i]; } } return min } }, } // 19.11.23 第二版 var MyMath = { max: function () { var arr = []; var max; // 判斷有沒有東西, 沒有就-無限大 if (arguments.length === 0) { return -Infinity } // 把 arguments 不是數字可是可以轉的東西都轉出來塞到arr裡 for (i=0; i<arguments.length; i++) { //console.log(Number(arguments[i])); if (isNaN(Number(arguments[i])) === true) { return NaN } else { arr[i] = Number(arguments[i]); } //console.log(arr); } // 開始比大小 max = arr[0]; for (i=1; i<arr.length; i++) { if (max < arr[i]) { max = arr[i]; } } return max }, min: function () { var arr = []; var min; // 判斷有沒有東西, 沒有就-無限大 if (arguments.length === 0) { return Infinity } // 把 arguments 不是數字可是可以轉的東西都轉出來塞到arr裡 for (i=0; i<arguments.length; i++) { //console.log(Number(arguments[i])); if (isNaN(Number(arguments[i])) === true) { return NaN } else { arr[i] = Number(arguments[i]); } //console.log(arr); } // 開始比大小 min = arr[0]; for (i=1; i<arr.length; i++) { if (min > arr[i]) { min = arr[i]; } } return min } } /* NaN == NaN // false , 被這個搞很久, 原來NaN不能比 if (Number(arguments[i]) === NaN) { return NaN } */ ``` ## Date > - 構造函數 > - 調用要先創造一個實例(對象) `var now = new Date()` > - Date 對象基於 1970年1月1日(UTC)起經過的毫秒數 > - 就是 python 的 time.time() ```javascript= // 空構造函數, 獲取「當下」時間 // new Date(); var now = new Date(); now // Sat Nov 23 2019 09:47:21 GMT+0800 (Taipei Standard Time) // 轉換格式 // 傳入毫秒值 // new Date(value); var time = new Date(1574473641456); time // Sat Nov 23 2019 09:47:21 GMT+0800 (Taipei Standard Time) // 傳入日期形式的string // new Date(dateString); new Date(dateString); var time = new Date('1993-1-1'); time // Fri Jan 01 1993 00:00:00 GMT+0800 (Taipei Standard Time) var time = new Date('1993-1-1 1:1:1'); time // Fri Jan 01 1993 01:01:01 GMT+0800 (Taipei Standard Time) // new Date( // year, monthIndex // [, day [, hours [, minutes [, seconds [, milliseconds]]]]] // ); var time = new Date(1993,1,1) time // Mon Feb 01 1993 00:00:00 GMT+0800 (Taipei Standard Time) var time = new Date(1993,1,1,1,1,1) time // Mon Feb 01 1993 01:01:01 GMT+0800 (Taipei Standard Time) // 獲取當前時間毫秒值 // valueOf() // 這一般是JS內部在調用的, 很少在code上面寫 // This method is usually called internally by JavaScript // and not explicitly in code. var now = new Date(); now.valueOf(); // 1574476478855 // getTime() // 跟 valueOf 一樣, 只是這是寫 code 在用的 // This method is functionally equivalent to the valueOf() method. now.getTime() // 1574476478855 // now() // 兼容性問題, HTML5 才能用 // 靜態方法 var num = Date.now() // 靜態方法 vs 實例方法 new Date().getTime() num // 1574476885575 // Number 轉換 var now = new Date(); now // Sat Nov 23 2019 10:45:33 GMT+0800 (Taipei Standard Time) // 這東西可以轉換 Number(now); // 1574477133395 // 簡寫 // + 取正 // 其實內部調用了 valueOf() + now; // 1574477133395 // 整理 var now = Number(new Date()); var now = + new Date(); // 轉換日期顯示格式 var now = new Date() // 以下這些方法可能因瀏覽器不同而不同 // 導致輸出不穩定 now.toString() // "Sat Nov 23 2019 11:20:27 GMT+0800 (Taipei Standard Time)" now.toDateString() // "Sat Nov 23 2019" now.toTimeString() // "11:20:27 GMT+0800 (Taipei Standard Time)" // toLocale 系列是根據使用的 os 來顯示當地的格式 now.toLocaleString() // "11/23/2019, 11:20:27 AM" now.toLocaleDateString() // "11/23/2019" now.toLocaleTimeString() // "11:20:27 AM" // get系列 var now = new Date() now // Sat Nov 23 2019 11:31:05 GMT+0800 (Taipei Standard Time) now.getFullYear() // 2019 now.getMonth() // 10 [0-11] *** 注意: 月份從 0 開始歐!!! now.getDate() // 23 [1-28/29/30/31] now.getHours() // 11 [0-23] now.getMinutes() // 31 [0-59] now.getSeconds() // 5 [0-59] now.getTime() // 1574479865782 now.getDay() // 6 now.getMilliseconds() // 782 // Q. 自己寫一個獲取現在時間的類跟方法, 格式 xx/xx/xx xx:xx:xx /* 第一版 function MyDate() { this.now = function () { var getNow = new Date(); console.log(getNow); var month = getNow.getMonth() + 1 // getMonth 從 0 開始, 所以要 +1 var day = getNow.getFullYear() + '/' + month + '/' + getNow.getDate() var time = getNow.getHours() + ':' + getNow.getMinutes() + ':' + getNow.getSeconds() return day + ' ' + time; } } var test = new MyDate() test.now() // "2019/11/23 12:3:43" */ // 第二版 function MyDate() { this.now = function () { if (arguments.length !== 0) { console.error('不要亂塞東西好嗎'); // console.error > 罵人用的 = .= } var now = new Date(), year = now.getFullYear(), month = now.getMonth() + 1, day = now.getDate(), hour = now.getHours(), minute = now.getMinutes(), second = now.getSeconds(), today = year + '/' + month + '/' + day, nowTime = hour + ':' + minute + ':' + second; return today + ' ' + nowTime } } var test = new MyDate() test.now() // "2019/11/23 12:21:48" // 第三版 function MyDate() { this.toTaiwanString = function () { var now = new Date(), year = now.getFullYear(), month = now.getMonth()+1, day = now.getDate(), hour = now.getHours(), minute = now.getMinutes(), second = now.getSeconds(), today, nowTime; // 個位數補零 /* 註 if (Number(month) < 10) { month = '0' + month } */ month = month < 10 ? '0' + month: month; day = day < 10 ? '0' + day: day; hour = hour < 10 ? '0' + hour: hour; minute = minute < 10 ? '0' + minute: minute; second = second < 10 ? '0' + second: second; today = year + '/' + month + '/' + day; nowTime = hour + ':' + minute + ':' + second; return today + ' ' + nowTime } } // 註 // str 會自動經過轉換後才比較, 不用自己轉 '10' < 11 // true '10' < 9 //false 'a' < 1 // false 'a' > 1 // false isNaN('a') // true isNaN('1') // false // ? 三元運算符 // 判斷式 ? 表達式1: 表達式2 // 如果判斷試為 true, 執行表達式 1, 否則執行表達式 2 if (3>1) { console.log('haha') } else { console.log('wowo') } // haha 3>1 ? console.log('haha') : console.log('woow') // haha // 看到一個案例的寫法 function fn(a) { // 判斷參數是不是Date的實例對象 // 變量 instanceof 構造函數 > 變量是否為構造函數的實例對象 // console.log(a instanceof Date); > true || false if (!(a instanceof Date)) { // 不是D ate 的對象 console.error('請塞Date的實例對象'); // 罵人 return // 閃人 } var year = a.getFullYear(), month = a.getMonth()+1, day = a.getDate(), hour = a.getHours(), minute = a.getMinutes(), second = a.getSeconds(), today, nowTime; month = month < 10 ? '0' + month: month; day = day < 10 ? '0' + day: day; hour = hour < 10 ? '0' + hour: hour; minute = minute < 10 ? '0' + minute: minute; second = second < 10 ? '0' + second: second; today = year + '/' + month + '/' + day; nowTime = hour + ':' + minute + ':' + second; return today + ' ' + nowTime } var a = new Date() fn(a) // "2019/11/23 14:06:37" var b = [1,2,4] fn(b) // 請塞Date對象 // Q. 計算時間差, 返回相差 天/時/秒 function fn(start, end) { var subTime = end-start; subTime /= 1000; var day, hour, minute, second; // 註 second = subTime % 60; minute = subTime / 60 % 60; hour = subTime / 60 / 60 % 24; day = subTime / 60 / 60 / 24; return day + '天/' + hour + '時/' + minute + '分/' + second + '秒' } var dayOne = new Date(); var dayTwo = new Date(2020,0,1); fn(dayOne, dayTwo); // 跨年倒數 // 註: 推導公式的過程 // 150顆水果, 一袋裝4個, 一箱裝7袋, 可以裝成幾箱幾袋剩幾個 // 150 / 4 = 37袋2個 // 37 / 7 = 5箱2袋 // 所以150顆水果可以裝5箱2袋2個 // 150 % 4 = 2 個 // 150 / 4 % 7 = 2 袋 // 150 / 4 / 7 = 5 箱 // 秒 % 60 = 秒 // 秒 / 60 % 60 = 分 // 秒 / 60 / 60 % 24 = 時 // 秒 / 60 / 60 / 24 = 天 ``` ## Array > - 棧操作 > - push() > - pop() > - 隊列操作 > - push() > - shift() > - unshift() > - 排序 > - reverse() > - sort() > - 操作 > - concat() > - splice() > - slice() > - 位置 > - indexOf() > - lastIndexOf() > - 迭代 > - every()、filter()、forEach()、map()、some() > - 連接 > - toString() > - join() ```javascript= // 陣列字面量創建陣列 var arr = [] arr.length // 0 arr[0] = 100 arr.length // 1 arr // [100] var arr2 = [1,2,3] arr2.length // 3 // 陣列的構造函數創列陣列 var array = new Array() array.length // 0 array[0] = 100 // 100 array.length // 1 array // [100] var array2 = new Array(1,2,3) array2 // [1, 2, 3] // 判斷是否為陣列 // 方法1: 變量 instanceof 構造函數 var a = [1,2,3]; a instanceof Array; // true var b = 1; b instanceof Array; // flase // 方法2: Array.isArray(變量) // 瀏覽器兼容問題: HTML5 才有 Array.isArray(a) // true Array.isArray(b) // false // valueOf() 返回對象本身 console.log(a.valueOf()) // (3) [1, 2, 3] // toString() 陣列轉成string, 用逗號隔開 console.log(a.toString()) // 1,2,3 ``` ```javascript= // 棧操作(先進後出) // - push() : 進 // 參數可以有多個 // 返回值為改變後陣列的長度 ++arr.length var arr = [1,2,3]; var arr2 = arr.push(4,5); console.log(arr); // (5) [1, 2, 3, 4, 5] console.log(arr2); // 5 // - pop() : 出 // 返回值為改變前陣列的長度 // arr-- var arr = [1,2,3]; var arr2 = arr.pop(); console.log(arr); // (2) [1, 2] console.log(arr2); // 3 // 隊列操作(先進先出) // - push() // - shift() // 取出陣列 [0] 的值, 返回值也是 [0] 的值 var arr = [99,55,11] var first = arr.shift(); console.log(first); // 99 console.log(arr); // (2) [55, 11] // - unshift() // 在 [0] 插入一個值, 返回值為要插入的值 var arr = [99,55,11] var tmp = arr.unshift(4); console.log(tmp); // 4 console.log(arr); // (4) [4, 99, 55, 11] // 排序 // - reverse() 翻轉 // 直接對數組本身翻轉排序 var arr = ['a','4',true]; var tmp = arr.reverse(); console.log(arr); // (3) [true, "4", "a"] console.log(tmp); // (3) [true, "4", "a"] // Q. ['Toyz', 'GodJJ', 'Gear'] -> ["Gear", "GodJJ", "Toyz"] // 解一: reverse() var arr = ['Toyz', 'GodJJ', 'Gear']; var newArr = arr.reverse(); console.log(newArr); // (3) ["Gear", "GodJJ", "Toyz"] console.log(arr); // (3) ["Gear", "GodJJ", "Toyz"] // 解二 還在想 // - sort() 排序 // arr.sort([compareFunction]) // 直接對數組本身修改排序 var arr = ['a', '4', true]; var tmp = arr.sort(); console.log(tmp); // (3) ["4", "a", true] console.log(arr); // (3) ["4", "a", true] // 默認是對 「字符的編碼」(ASCII, Unicode) // 從小到大排序 // 排序前先轉乘str, 再依據Unicode編碼來排序, // 所以 100 會排在 9 的前面 // 所以排出來的東西可能較不實用 var arr = [11,102,3,1,30]; var tmp = arr.sort(); console.log(arr); // (5) [1, 102, 11, 3, 30] // function compare(a,b) // compare(a,b) 的返回值小於0時, a會排在b前面 // compare(a,b) 的返回值大於0時, b會排在a前面 // 降序 var arr = [11,102,3,1,30]; /*function compare(a,b) { return b-a } arr.sort(compare)*/ arr.sort(function (a,b) { return b-a }) console.log(arr); // (5) [102, 30, 11, 3, 1] // 升序 var arr = [11,102,3,1,30]; /*function compare(a,b) { return a-b } arr.sort(compare)*/ arr.sort(function (a,b) { return a-b }) console.log(arr); // (5) [1, 3, 11, 30, 102] var arr = ['apple', 'amazon', 'google', 'tesla'] arr.sort(function (a,b) { return a.length-b.length }) console.log(arr); // (4) ["apple", "tesla", "amazon", "google"] // 冒泡排序 function fn(a,b) { return a-b } function bubbleSort(arr, fn) { for (i=1; i<arr.length; i++) { var isSort = true; for (j=0; j<=arr.length-i; j++) { //if (arr[j] > arr[j+1]) { // 把值丟到 fn() 裡去相減, 大於0就表示前者比後者大, // 跟上面那句一樣意思 if (fn(arr[j], arr[j+1]) > 0) { var temp = arr[j+1]; arr[j+1] = arr[j]; arr[j] = temp; isSort = false; } } if (isSort) { break; } } return arr; } var arr = [54,26,93,17,77,31,44,55,20]; console.log(bubbleSort(arr, fn)); /* python 思考 def fn(a,b): return a-b def bubbleSort(arr,fn): for i in range(1,len(arr)): for j in range(len(arr)-i): # if arr[j] > arr[j+1]: # 調用fn(), 把兩個值丟進去相減為正數的話就表示前者比後者大, # 跟原本寫的是一樣意思 if fn(arr[j], arr[j+1]) > 0: arr[j], arr[j+1] = arr[j+1], arr[j] return arr a = [100, 23, 4, 1, 2, 11] bubbleSort(a, fn) # [1, 2, 4, 11, 23, 100] */ // 清空陣列 // = [] var arr = [1,2,3]; arr = []; console.log(arr); // [] // .length = 0 var arr = [1,2,3]; arr.length = 0; console.log(arr); // [] // .splice(start[, deleteCount[, item1[, item2[, ...]]]]) // 刪除或替換當前陣列的某些項目 // 返回值: 清掉了什麼 var arr = [1,2,3]; console.log(arr.splice(0)); // (3) [1, 2, 3] console.log(arr); [] var arr = [1,2,3]; console.log(arr.splice(0,1)); [1] console.log(arr); // (2) [2, 3] // 操作方法 // concat() : 把參數拼接到陣列中 // slice(strat, end) : 從當前陣列中取得一個新陣列(不影響原陣列) // start 從 0 開始 // end 從 1 開始 // --------------------位置方法-------------------------- // indexOf(searchElement[, fromIndex]) // -1: 找不到 // 從前往後找, 找到馬上返回 // 第二個參數是從第幾個開始找 // lastIndexOf(searchElement[, fromIndex]) // -1: 找不到 // 從後往前找, 找到馬上返回 // Q. ['a', "c", "a", "z", "a", "x", "a"] 找到每個 'a' 的位置 // 解一 var arr = ['a', "c", "a", "z", "a", "x", "a"]; function fn(arr) { var index = {} for (i=0; i<arr.length; i++) { if (arr[i] === 'a') { console.log(i) } } } fn(arr) // 0 2 4 6 // 解二 // indexOf, lastIndexOf var arr = ['a', "c", "a", "z", "a", "x", "a"]; /* 測試 arr.indexOf('a') // 1 arr.lastIndexOf('a') // 5 arr.indexOf('a',2) // 3 // 從 [2] 開始找 'a' arr.indexOf('a',4) // 5 // 從 [4] 開始找 'a' */ var index = -1 do { index = arr.indexOf('a', index+1); if (index !== -1) { console.log(index); } } while (index !== -1); // 0 2 4 6 // !小發現! var arr = ['a', "c", "a", "z", "a", "x", "a"]; do { var index; console.log(index); // undefined 0 2 4 6 index = arr.indexOf('a', index+1); console.log(index); // 0 2 4 6 -1 } while (index !== -1); // undefined + 1 // NaN, // 應該是 NaN 而造成 NaN !== -1 的無限迴圈, 結果直接變成 0 , // 應該是 indexOf() 裡面有做判斷把不是數字的參數轉成 0 來從 [0] 找 // 迭代方法, HTML5 // every(), filter(), forEach(), map(), some() // 不影響原陣列 // Q. [1500, 1200, 2000, 2100, 1800] , 超過兩千的刪除 // 解一 /*function fn(a) { return a <= 2000 } console.log([1500, 1200, 2000, 2100, 1800].filter(fn)); // (4) [1500, 1200, 2000, 1800] */ console.log([1500, 1200, 2000, 2100, 1800].filter(function(a) { return a<=2000 })); // (4) [1500, 1200, 2000, 1800] // 解二 var array = [1500,1200,2000,2100,1800]; var tmp = []; for (var i = 0; i < array.length; i++) { if(array[i] <= 2000) { tmp.push(array[i]); } } console.log(tmp); // (4) [1500, 1200, 2000, 1800] // 將陣列串成一個 String // join() // 如果有給參數的話就會用參數去連接各個陣列的元素, // 如果沒給參數的話就用逗號連接各個陣列的元素 // 跟 toString 一樣 // 不影響原陣列 // Q. ['Toyz', 'GodJJ', 'Gear'] -> 'Toyz | GodJJ | Gear' var arr = ['Toyz', 'GodJJ', 'Gear']; var newArr = arr.join(' | '); console.log(newArr); // Toyz | GodJJ | Gear // 用參數連接 console.log(arr); // (3) ["Toyz", "GodJJ", "Gear"] // 不影響原陣列 var newArr2 = arr.join(); console.log(newArr2); // Toyz,GodJJ,Gear // 逗號連接 console.log(arr.toString()) // Toyz,GodJJ,Gear // Q. ['c', 'a', 'z', 'a', 'x', 'a'] 去除重複 var arr = ['c', 'a', 'z', 'a', 'x', 'a']; // 統計各個元素的數量, 超過1的要特別處理 var count = {} for (var i=0; i<arr.length; i++) { var item = arr[i]; if (count[item]) { // 測試1 count[item]++ // 測試2 } else { count[item] = 1; } } console.log(count); // {c: 1, a: 3, z: 1, x: 1} // value === 1 的直接放行加入新陣列 var newArr = []; for (key in count) { // 測試3 if (count[key] === 1) { newArr.push(key); } else { // 超過的看新陣列有沒有, 沒有就放行 if (newArr.indexOf(key) === -1) { newArr.push(key); } } } console.log(newArr); // (4) ["c", "a", "z", "x"] ``` ## 基本包裝類型 > - 為了方便操作, JS提供了三個基本包裝類型, 亦即將基本類型包裝成複雜類型 > - string > - number > - boolean ```javascript= var str = '123abc'; str.length; // 6 str // "123abc" // 問題: 他只是一個簡單數據類型(沒有屬性方法), 怎麼會有對象的方法呢 ?! // 基本包裝類型 var strTmp = new String('123abc'); console.log(strTmp); // String {"123abc"} // 0: "1" // 1: "2" // 2: "3" // 3: "a" // 4: "b" // 5: "c" // length: 6 // __proto__: String // [[PrimitiveValue]]: "123abc" // PrimitiveValue 原始值 // 在執行 str.length 時, 會創建一個臨時的對象 var tmp = new String('123abc') tmp.length; // 6 tmp = null; // 讓那個對象被釋放 // Number // 基本數據類型 12 // 轉換類型 console.log(Number('12')); // 12 // 創建 Number 實例 console.log(new Number('12')); // Number {12} // __proto__: Number // [[PrimitiveValue]]: 12 // Boolean // 基本數據類型 false // 轉換類型 console.log(Boolean('123')); // 創建 Boolean 實例 console.log(new Boolean(false)); // Boolean {false} //__proto__: Boolean //[[PrimitiveValue]]: false // 陷阱題 var b1 = new Boolean(false); var b2 = b1 && true; console.log(b2); // true // 因為 b1 並不是 false, 而是一個具體的實力對象 // 在經過 Boolean 轉換時, 因為不是 undefined 0 '' null NaN // 所以轉換後為 true // true && true 自然就是 true ``` ### new String() > #### string不可變 > - 每次改變str時, 都是在讓變量重新指向 > - 問題: 當我大量改變str的指向時, 會有效能問題(重新創建空間需要時間) ```javascript= var str = ''; for (i=0; i<1000000; i++) { str += i; } console.log(str); ``` > #### string方法 > - 如上面原理所述, str調用方法時, 一樣會自動創建一個臨時對象來調用後銷毀 > 所以不用特別創建對象來使用這些方法 > - 由於 str 是不可變的, 所以執行玩方法後都會返回一個新的 str ```javascript= // char 操作 // charAt(位置) 獲取指定位置的char var tmp = '123abc'; console.log(tmp.charAt(0)); // 1 console.log(tmp.charAt(4)); // b // charCodeAt(位置) 獲取指定位置 char 的 ASCII console.log(tmp.charCodeAt(0)); // 49 console.log(tmp.charCodeAt(4)); // 98 // str[位置] 與charAt 相同效果, 但這個有瀏覽器限制(HTML5, IE8+) console.log(tmp[0]); // 1 console.log(tmp[4]); // b // 操作 concat() // 拼接, 跟 + 有相同效果 var a = 'haha' var b = 'wowo' a.concat(b) // "hahawowo" slice() // 擷取 [start~end) 的 char substring() // 擷取 [start~end) 的 char substr(start, n) // 擷取從 start 開始的 n個字符 // Q. '5566不能亡' 擷取 '56不能亡' var str = '5566不能亡' var newStr = str.substr(1,2) + str.substr(4); console.log(newStr); // 位置 // indexOf() var a = '5566在5月6號時解散' console.log(a.indexOf('5')); // 0 console.log(a.indexOf('7')); // -1 'abcd'.indexOf('') // 0 //=> 空字串也等於 0 而非 -1 // lastIndexOf() // search() // vs. indexOf: search 支持正則 var a = '5566在5月6號時解散' console.log(a.search('5')); // 0 console.log(a.search('7')); // -1 // Q. '5566在5月6號時解散, 我流了56滴淚水' 找所有 5 的位置 var str = '5566在5月6號時解散, 我流了56滴淚水'; var index = -1; do { index = str.indexOf('5', index+1); if (index !== -1) { console.log(index); } } while (index !== -1) // 0 1 5 17 // 去除空白 trim() // 只去除str前後空白 // 測試 var str = ' 55 66 不 能 亡 '; console.log('!' + str.trim() + '!'); // !55 66 不 能 亡! // 轉換大小寫 // Locale 可省 to(Locale)UpperCase() // 轉換大寫 var a = 'haha'; var b = a.toUpperCase(); console.log(b) // HAHA to(Locale)LowerCase() // 轉換小寫 var c = b.toLowerCase; console.log(c) // haha // 其他 // replace(要替換的東西, 替換成) // 只會替換一個就返回 // 測試 var str = '5566在5月6號時解散, 我流了56滴淚水' str = str.replace('5', '6') // "6566在5月6號時解散, 我流了56滴淚水" // Q. '5566在5月6號時解散, 我流了56滴淚水' 將5換成6 var str = '5566在5月6號時解散, 我流了56滴淚水'; var index = -1; do { index = str.indexOf('5', index+1); if (index !== -1) { // str = str.replace(str[index], '6'); str = str.replace('5', '6'); } } while (index !== -1) console.log(str) // 6666在6月6號時解散, 我流了66滴淚水 // split() // 根據參數來切割成陣列 var str = '5,5,6,6'; var arr = str.split(','); console.log(arr); // (4) ["5", "5", "6", "6"] > 返回一個array的實例對象 // Q. ' 55 66 不 能 亡 ' 去除所有空格 // 解法一 replace(' ', '') var str = ' 55 66 不 能 亡 '; var index = -1; do { index = str.indexOf(' ', index+1); if (index !== -1) { str = str.replace(' ', ''); } } while (index !== -1); console.log(str); // 5566不能亡 // 解法二 var str = ' 55 66 不 能 亡 '; str = str.split(' '); console.log(str) // (9) ["", "", "55", "66", "不", "能", "亡", "", ""] str = str.join(''); console.log(str) // 5566不能亡 // Q. 'abcoefoxyozzopp' 判斷數量最多的char與各數 var str = 'abcoefoxyozzopp'; function fn(str) { var count = {}; for (var i=0; i<str.length; i++) { var item = str[i]; if (count[item]) { count[item]++; } else { count[item] = 1; } } console.log(count); // {a: 1, b: 1, c: 1, o: 4, e: 1, …} var max = 1; var char; for (key in count) { if (max < count[key]) { max = count[key]; char = key; } } console.log('最多的char: ' + char + ', 有' + max + '個'); } fn(str) // 最多的char: o, 有4個 // 'http://www.itheima.com/login?name=zs&age=18&a=1&b=2' 把參數轉成對象 // ? 後面為參數, 每個參數用 & 分隔, key=value var str = 'http://www.itheima.com/login?name=zs&age=18&a=1&b=2'; function getParam(str) { // 先把參數拿部份拿出來 var str = str.split('?'); // console.log(str); // ["http://www.itheima.com/login", "name=zs&age=18&a=1&b=2"] param = str[1]; // console.log(param); // name=zs&age=18&a=1&b=2 // 把每個參數拿出來 param = param.split('&'); // console.log(param); // (4) ["name=zs", "age=18", "a=1", "b=2"] // 個別拼成 key value var dict = {}; for (i=0; i<param.length; i++) { var tmpParam = param[i].split('='); var key = tmpParam[0]; var value = tmpParam[1] dict[key] = value; } return dict; } getParam(str); // {name: "zs", age: "18", a: "1", b: "2"} ``` ```javascript= ```