本博客是我學習Curran Kelleher老師數據可視化課程的筆記,感興趣的小伙伴可以點擊這里學習。
three cores of data visualization:
- analysis
- design
- construction
推薦書籍《visualization analysis & design》
使用https://vizhub.com/進行編程學習,這個網站好像是Curran Kelleher自己創建的一個教學網站。
JS知識點
該部分的學習除了上述課程以外,還參照了廖雪峰的JavaScript教程。
- JavaScript代碼可以直接嵌在網頁的任何地方,不過通常我們都把JavaScript代碼放到
<head>
中。由<script>...</script>
包含的代碼就是JavaScript代碼,它將直接被瀏覽器執行。或者代碼放在一個單獨的.js
文件中,然后在HTML中通過<script src="..."></script>
引入這個文件 - 如果在一個頁面中引入多個文件,并且在
<script></script>
中間還有js代碼,瀏覽器將將會按照順序依次執行。 - JavaScript的設計者希望用
null
表示一個空的值,而undefined
表示值未定義。事實證明,這并沒有什么卵用,區分兩者的意義不大。大多數情況下,我們都應該用null
。undefined
僅僅在判斷函數參數是否傳遞的情況下有用。 - 變量名是大小寫英文、數字、
$
和_
的組合,且不能用數字開頭。變量名也不能是JavaScript的關鍵字,如if
、while
等。申明一個變量用var
或let
或const
語句,盡量使用const
var
聲明的變量的作用域是函數。內部函數可以訪問外部函數定義的變量,并且會覆蓋重名的變量。函數在定義的時候會掃描整個函數體的語句,把所有var
變量的聲明提升到函數頂部。因此為了防止出現混亂,應該在函數頂部使用var
定義。更好的做法是使用let
定義變量,這樣的變量有塊級作用域。const
和let
都有塊級作用域,但是const
無法改變- 如果一個變量沒有通過
var
申明就被使用,那么該變量就自動被申明為全局變量。在strict模式下這種聲明方法是不允許的。啟用strict
模式的方法是在代碼的第一行寫上'use strict';
- 在``包圍的字符串中可以使用
$
加變量名來獲得變量的值并轉換成字符串,并且可以得到多行字符串。字符串是不可變的,如果對字符串的某個索引賦值,不會有任何錯誤,但是,也沒有任何效果: - 可以通過
()=>{}
的方式聲明函數,不需要關鍵字function
- 數組的長度保存在
length
屬性中。如果修改length
的值改變數組的大小。如果通過索引賦值,索引超過了范圍,同樣會引起數組大小的變化。 - 數組可以通過數組的
forEach
方法遍歷數組
array.forEach(item => {//對item的操作
});
這種方法的好處是可以可以把里面的函數變成其他的,靈活的進行遍歷
- 數組使用
map
方法可以得到另一個數組,每個數組的值是函數處理過的值
let func = item => {//對item的操作
};
let tmp = array.map(item);
- 數組可使用
filter
方法過濾元素,需要傳入的函數返回布爾值
let tmp = array.filter(item => {})
- 數組的
sort()
方法默認將數字轉化為字符串,返回仍然是當前字符串。可以通過傳入一個函數,分別返回-1,0,1來確定排序順序。 - 數組使用
join
方法可以將數組元素看成字符串拼接起來 - 使用解構賦值可以使得代碼更加簡潔。如果是數組的話使用
[]
,如果是對象的話使用{}
var person = {name: '小明',age: 20,gender: 'male',passport: 'G-12345678',school: 'No.4 middle school',address: {city: 'Beijing',street: 'No.1 Road',zipcode: '100001'}
};
var {name, address: {city, zip}} = person;
name; // '小明'
city; // 'Beijing'
zip; // undefined, 因為屬性名是zipcode而不是zip
// 注意: address不是變量,而是為了讓city和zip獲得嵌套的address對象的屬性:
address; // Uncaught ReferenceError: address is not defined//更換屬性名
var person = {name: '小明',age: 20,gender: 'male',passport: 'G-12345678',school: 'No.4 middle school'
};// 把passport屬性賦值給變量id:
let {name, passport:id} = person;
name; // '小明'
id; // 'G-12345678'
// 注意: passport不是變量,而是為了讓變量id獲得passport屬性:
passport; // Uncaught ReferenceError: passport is not defined//添加默認值
var person = {
var person = {name: '小明',age: 20,gender: 'male',passport: 'G-12345678'
};// 如果person對象沒有single屬性,默認賦值為true:
var {name, single=true} = person;
name; // '小明'
single; // true//有時候變量已經聲明,則正確的寫法也會報錯
// 聲明變量:
var x, y;
// 解構賦值:
{x, y} = { name: '小明', x: 100, y: 200};
// 語法錯誤: Uncaught SyntaxError: Unexpected token =
//正確寫法
({x, y} = { name: '小明', x: 100, y: 200});
-
JSON.stringify(array, null, 2);
將數組轉化為字符串,第二個參數一般用不上,第三個參數是添加換行的,否則擠在一起 -
JSON.parse(str)
可以將上面的字符串再轉換成數組 -
對象最后一個鍵值對不要加逗號,否則有的瀏覽器會報錯
-
訪問屬性是通過
.
操作符完成的,但這要求屬性名必須是一個有效的變量名。如果屬性名包含特殊字符,就必須用''
括起來 -
檢測對象是否擁有某一屬性可以用
in
操作符。有可能這個屬性是繼承得到的。為了判斷一個對象是否是對象本身擁有的,可以用hasOwmProperty()
方法。
var a = ['A', 'B', 'C'];
for (let i in a){console.log(i); //'0' '1' '2'console.log(a[i]); //'A' 'B' 'C'
}
var xiaohong = {name: '小紅','middle-school': 'No.1 Middle School'
};
例如上面的屬性名middle-school
就不是一個有效的變量名,只能用xiaohong['middle-school']
的方法進行訪問。
for ... in ...
可以把一個對象的所有屬性名依次循環出來,可以用hasOwnProperty()
過濾對象繼承的屬性,數組也是對象,不過屬性名為數組的下標。需要注意的是得到的是屬性名類型為String
類型- JavaScript中對象的鍵值是字符串類型的。如果想要讓鍵值為其他類型的,可以使用
Map
。可以用二維數組對Map
進行初始化,常用的方法有set get has delete
等
var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]]);
m.get('Michael'); // 95m = new Map(); // 空Map
m.set('Adam', 67); // 添加新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 刪除key 'Adam'
m.get('Adam'); // undefinedm = new Map();
m.set('Adam', 67);
m.set('Adam', 88);
m.get('Adam'); // 88
Set
和Map
相似,但是不存儲value。
var s = new Set([1, 2, 3, 3, '3']);
s; // Set {1, 2, 3, "3"}
s.add(4);
s; // Set {1, 2, 3, "3", 4}
s.add(4);
s; // 仍然是Set {1, 2, 3, "3", 4}s = new Set([1, 2, 3]);
s; // Set {1, 2, 3}
s.delete(3);
s; // Set {1, 2}
- 為了解決
Map
和Set
不能使用下標訪問的問題,我們可以使用for ... of ...
循環來遍歷,只循環集合本身的元素,后添加的元素不會循環 - 同樣的我們可以使用
forEach
方法進行循環。不過需要注意使用方法
第三個參數是調用者本身
var s = new Set(['A', 'B', 'C']);
s.forEach(function (element, sameElement, set) {console.log(element);
});var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
m.forEach(function (value, key, map) {console.log(value);
});var a = ['A', 'B', 'C'];
a.forEach(function (element) {console.log(element);
});
- 函數如果沒有
return
語句,函數執行完畢后也會返回結果,只是結果為undefined
。 - 函數有多種定義方法
function abs(x) {if (x >= 0) {return x;} else {return -x;}
}var abs = function (x) {if (x >= 0) {return x;} else {return -x;}
};//不要忘記分號var abs = x => x>0 ? x : -x;
- 由于JavaScript允許傳入任意個參數而不影響調用,因此傳入的參數比定義的參數多也沒有問題,雖然函數內部并不需要這些參數。為了避免參數不存在,可以使用
typeof
運算符對參數進行檢查。
function abs(x) {if (typeof x !== 'number') {throw 'Not a number';}if (x >= 0) {return x;} else {return -x;}
}
-
特殊參數使用:
arguments
是調用者傳入的所有參數的數組。...rest
參數可以獲得所有額外的參數。如果傳入的參數連正常定義的參數都沒有填滿rest
參數會接受一個空數組(不是undefined
)。 -
需要注意
return
后面不要隨意換行,JS會自動加分號,可能導致錯誤。 -
方法的
this
指針始終指向調用者,如果直接調用函數一般this
指向window
。在strict
模式下,直接調用函數的this
指針指向undefined
。只有對象直接調用函數的this
指針是有效的,內層嵌套的函數的this
指針是無效的,解決方法是一個臨時變量將this
保存起來。 -
對于上面的問題,還可以使用
apply
和call
方法解決。apply
還能動態改變函數的行為 -
返回閉包時牢記的一點就是:返回函數不要引用任何循環變量,或者后續會發生變化的變量。如果一定要引用循環變量怎么辦?方法是再創建一個函數,用該函數的參數綁定循環變量當前的值,無論該循環變量后續如何更改,已綁定到函數參數的值不變。
function count() {var arr = [];for (var i=1; i<=3; i++) {arr.push((function (n) {return function () {return n * n;}})(i));}return arr;
}var results = count();
var f1 = results[0];
var f2 = results[1];
var f3 = results[2];f1(); // 1
f2(); // 4
f3(); // 9
- 在沒有class機制,只有函數的語言里,借助閉包,同樣可以封裝一個私有變量。我們用JavaScript創建一個計數器
'use strict';function create_counter(initial) {var x = initial || 0; //initial如果沒有定義為假return {inc: function () {x += 1;return x;}}
}var c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3var c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13
- 閉包還可以把多參數的函數變成單參數的函數。例如,要計算xy可以用Math.pow(x, y)函數,不過考慮到經常計算x2或x3,我們可以利用閉包創建新的函數pow2和pow3:
'use strict';function make_pow(n) {return function (x) {return Math.pow(x, n);}
}
// 創建兩個新函數:
var pow2 = make_pow(2);
var pow3 = make_pow(3);console.log(pow2(5)); // 25
console.log(pow3(7)); // 343
-
如果使用箭頭函數要返回一個對象,而且對象只有一個鍵值對的時候要把對象用括號括起來
-
箭頭函數和匿名函數的區別:箭頭函數內部的
this
總是指向詞法作用域,也就是外層調用者。由于已經綁定,所以無法使用call
或者apply()
對this
進行綁定,傳入的第一個參數被忽略。 -
setTimeOut(()=>{} , x)
可以用來睡眠一樣的操作,前面傳入的是一個函數,后面是睡眠的秒數,單位是毫秒,睡眠xms后運行前面的函數
let waitSeconds = numSeconds => new Promise(resolve =>{const message = `${numSeconds} seconds have passed`;setTimeout(()=>{resolve(message)}, numSeconds*1000);
});
waitSeconds(2).then(message => console.log(message));
模仿視頻在vizhub上的實現的代碼:https://vizhub.com/Edward-Elric233/97bd1319ee774022babd69cd4cca220e
不過需要注意一點的是在這個網頁上的import
文件需要在文件名前面加上./
,Curran老師在Youtube上說明了自己不用加也可以成功運行的原因是Bug。