本博客是我学习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。