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=
```