学习了一些基础语法。包括 map,set,原始值和引用值,字符串方法,数组方法。
没什么好看的,全是红宝书上的内容,别看了。
1. Map
ES6语法,Map 用于代替 object 来储存键值对。
1.1 基础 API
has,get,set,delete,clear。
- 其中 set,返回 map,因此可以多个 set 连在一起使用:
const m = new Map();
m.set("a",1).set("b",2).set("c",3)
- map 的键可以是任意数据结构。不像 object 有限制。
1.2 顺序
和 object 不同,map 实例会记录数据的插入顺序,map 实例会提供一个迭代器(这是 object 没有的)。
迭代器每次迭代返回 [key, value] 这样的数组。
// 使用 entries 获取迭代器
for (let pair of m.entries()) {
console.log(pair); // [key, value]
}
// entries 为默认迭代器,不加也可以
for (let pair of m) {
console.log(pair); // [key, value]
}
// 如果要专门迭代 key 或者 values,则要写
for (let key of m.keys()) {
console.log(key);
}
2. set
2.1 基本 API
add,has,delete,clear。
和 map 相比,get,set 变成了 add。
add 也是返回新实例,因此也可以连着使用。
2.2 迭代
和 map 一样的迭代方式,有 keys,values,entries,默认是 values,keys 和 values 相等。因此 entries 其实是一对重复值。
3. 数字
- NaN 指的是 not a number,是一种特殊的数字:
typeof NaN === "number"。 - 0/0 会得到 NaN,5/0,5/+0,5/-0 会得到 +/-Infinity。
- NaN 不等于自身,
NaN !== NaN。 - 将非数值转化为数值有三种方法:
- Number,能够接受的信息最多,无论什么格式都可以尝试转化。
- parseInt,只能转化字符串,且只能转化为整数,接受第二个参数表示以什么进制来解析字符串里的数字。比如 parseInt("10", 2) 和 parseInt("10", 10)。前者返回值为 2,后者为 10.
- 推荐永远写上第二个参数,即使是十进制
- parseInt("1234red", 10); // 解析为 1234
- parseInt("red1234", 10); // 解析为 NaN,因为第一个字符解析失败立刻返回 NaN
- parseFloat,和 parseInt 一样,但是可以转化小数(整数当然也可以解析)。此外,parseFloat 没有第二个参数,只能解析为十进制。
4. 字符串
- 像很多语言一样,js 中字符串也是不可变的。看着像是更改的操作在内部都是先销毁再复制。
- 其他类型转为字符串有两种方法:
- toString() 方法,几乎所有的类型都有这种方法。 null 和 undefined 没有。(NaN 是有的,因为它毕竟是数字类型,转化为字符串 "NaN")
- 在对数字使用 toString() 的时候,可以接受一个参数,表示以几进制展示该数字。
let a = 255; a.toString(2);// 输出 "11111111"
- 在对数字使用 toString() 的时候,可以接受一个参数,表示以几进制展示该数字。
- String() 函数。当不确定是否要转化 null,undefined 的时候,用这个函数。当为 null 和 undefined 的时候,返回 "null" 和 "undefined"。其他时候会调用 toString() 方法。
- toString() 方法,几乎所有的类型都有这种方法。 null 和 undefined 没有。(NaN 是有的,因为它毕竟是数字类型,转化为字符串 "NaN")
- 模板字符串 ``
- 原始字符串: 模板字符串依然会对 \n 等解释为换行符。如果想纯粹使用字符串。则需要用到原始字符串:
String.raw`first\nsecond`,输出"first\nsecond",而不是换行。
5. 操作符
-
- 号会对数据进行转化,将其转化为数字,转化方式和 Number 一样。
-
- 和 + 一样,只是会在转换后添加负号
5.1 位操作符
js 里的二进制为 32 位,10010 实际上是 0000 0000 0000 0000 0000 0000 0001 0010。其中第 32 位(最左边的),表示符号。0 为 正,1 为 负。
- 按位非:
~,对单个数使用,二进制的每一位取反。由于第三十二位符号位也会取反,因此按位非之后不仅数值变化,符号也会变化。 - 按位与:
&,对两个数使用,顾名思义 - 按位或:
|,对两个数使用,顾名思义 - (注意)按位异或:
^,当两边只有 1 个为 1 的时候返回 1,不要和 R 里的 n 次方搞混了,在 js 里这个表示的是异或。 - 左移:
<<,对单个数使用。如10010 << 5 === 1001000000。左移会保留符号,空位(右边)补 0. - 有符号右移:
>>,对单个数使用。如1010010 >> 5 === 101。空位(左边,符号位之后)补 0。 - 无符号右移:
>>>,对于单个数使用。对于正数,表现和有符号一样。对于负数,由于负数是以二补数的形式表现,得到的结果将会是一个非常大的正数。
5.2 指数操作符
即 **,这个是ES7的语法。代替 Math.pow()。
5.3 相等操作符
== 判断相等前会做类型转换,因此类型不一样也会返回 true。
=== 全等,数据类型不同会返回 false。
6. 标签语句
与 break,continue 一起使用,可以跳到特定的循环。
outermost:
for (let i in a) {
for (let j in b) {
break outermost; // 直接跳到最外层,两个循环都不再执行了。
break; // 只跳出一层循环。
}
}
7. 原始值和引用值
- 和很多语言不同,js 中的字符串是原始值,是按值访问的。
- 函数的参数全部是按值复制的,所有参数都是局部变量。
- 那么有个问题,如果都是复制,那为什么引用值的改变会影响到外部变量呢?
- 因为复制的仅仅是一个指针,指向的和外部变量的是同一个内存地址。
- 函数执行完毕后,垃圾回收掉该参数,不会对外部变量有影响。
- 那么有个问题,如果都是复制,那为什么引用值的改变会影响到外部变量呢?
8. 日期
- Date 类型的 toLocaleString,toString 显示的内容会根据浏览器而不同。如果想要保持一致,应该获取特定部分再组合。
- 获取特定部分的方法:getFullYear(),getMonth(),getDate(),getDay(),getHours(),getMinutes(),getSeconds(),getMilliSeconds()
9. 原始值包装类型
之前提到字符串是原始值,但是 String 是引用值。这个叫做原始值包装类型。类似的还有 Boolean,Number。这三种被称作原始值包装类型,都是引用值。但是其对应的布尔值,数字,字符串都是原始值。
// 这三个都是原始值
const a = 1;
const b = true;
const c = "yukari";
typeof a; // "number"
typeof b; // "boolean"
typeof c; // "string"
// 这三个是引用值
const ar = new Number(1);
const br = new Boolean(true);
const cr = new String("yukari");
typeof ar; // "object"
typeof br; // "object"
typeof cr; // "object"
每当创建原始值的时候,其实 js 内部都创建了一个对应的原始值包装类型。这样就可以使用包装类型的方法了。比如说字符串的 substring() 等等。const s1 = "hello".substring(2)
这很合理,因为原始值是不应该有方法的,但很多时候我们对原始值使用方法都没问题,这都是因为背后有着原始值包装类型在工作。
实际上后台在每次原始值调用方法的时候都会执行三步,①创建一个包装类型实例②在这个实例上调用方法③销毁该实例。
也就是说变量本身还是原始值。只是拥有引用值的调用方法行为而已。
let s1 = "color text";
s1.color = "red"; // 生成了一个包装类型,确实也给这个包装类型添加了 color 属性,但是立刻就销毁了。
console.log(s1.color); // undefined,因为上一行生成的包装类型立刻就销毁了。这一行又是新生成的一个实例,并没有 color 属性。
- 注意:Number() 和 new Number() 不一样,前者为普通函数,作用为把各种数据类型转化为数字。后者为构造函数,用于生成原始值包装类型。前者结果的 typeof 为 "number",后者为 "object"。
- 一般不推荐使用原始值包装类型,仅用于后台。
10. String 方法
当谈到字符串方法的时候,实际上是在说 String 包装类型的方法。
10.1 拼接:concat()
拼接字符串,大部分时候 + 更方便。
"yukari".concat(" cannot", " beat ", "murasaki"); // "yukari cannot beat murasaki"
10.2 子字符串:slice(), substring(), substr() 方法以及区别
都是剪切出子字符串的方法。
- slice, substring 使用方法相同,第一个参数表示开始,第二参数表示结束(不包括结束)。
- substring 当第二个参数比第一个小的时候,会调换位置
- slice 则会返回一个空字符串
- substr,第二个参数表示子字符串的长度。
- 这些方法都不会有 index 错误,比如
"asd".substr(1,2000)会返回"sd"。- 嘛,毕竟本来
"asd"[2000]也只是返回 undefined。
- 嘛,毕竟本来
- 省略第二个参数都意味着选取后面的所有。
- 当参数有负值的时候,表现又有不一样了。
- substring 会把所有负值变成 0
- substr 会把第一个参数的负值变成字符串长度加上负值,第二个参数变为0(别忘了这个是长度为 0 的意思,也就是说返回一个空字符串)
- slice 会把所有负值都变成字符串长度加上该负值
- 字符串长度加上负值,其实就是倒着数的意思
- NaN 都会被转成 0
10.3 寻找位置 indexOf(),indexLastOf()
寻找某个子字符串的位置
一个从前面开始找,一个从后面开始找。
为什么要分成两个方向呢,因为还能接受第二个参数,表示开始找的位置。一个向前找,一个向后找。这样区别就大了。
10.4 包含
这是 Es6 新增的方法。返回的都是布尔值。
- startsWith() 和 endsWith(),顾名思义,字符串是否以参数为开头或者结尾。
- includes(),是否包含子字符串。
- startsWith() 和 includes() 可以接受第二个参数,表示从哪里开始往后找。
- endsWith() 也接受第二个参数,不过表示的是从哪里开始往前找(换句话说,以什么位置作为结尾,前面的 startsWith 也可以换句话说以什么位置作为开始)。
10.5 trim()
新生成一个字符串,去掉原始字符串头尾的空格符。
还有顾名思义的 trimLeft() 和 trimRight()
10.6 repeat()
将字符串复制n遍。参数为复制多少遍。
const a = "Aqua";
a.repeat(3); // "AquaAquaAqua"
10.7 padStart, padEnd
接受一个数字参数,当字符串长度不够参数的时候,往前或者往后添加空格直到该长度。
也可接受第二个参数,表示当你不想用空格的时候,可以自定义填充字符。
10.8 迭代
字符串可以迭代
10.9 大小写转换
- toLowerCase() 和 toUpperCase()。顾名思义。
11. eval()
和 python 里的一样,接受一个字符串,把这个字符串当做代码来执行。
eval 里的变量和函数不会被提升。
严格模式下,eval 内部定义的函数和变量,外部是无法访问的。
12. Array
12.1 Array.from 和 Array.of
这两个是 ES6 新增的语法。
12.1.1 Array.from
第一个参数是任何一个可迭代的对象,将其转化为数组。像 map 的键值对就会被转成 [[key1,value1], [key2, value2], [key3, value3]] 的形式。如果参数是数组,则会进行浅复制。
第二个参数是一个函数,作用和 Array.map 一样,直接改变新数组的值。
12.1.2 Array.of
把一组参数转化成数组。
Array.of(1,2,3,4); // [1,2,3,4]
12.2 数组空位
比如 [1,,,,3],但是不同方法对空位的处理不一样,为了保持一致性,可以把每个空位显式赋值为 undefined。
12.3 length
数组的 length 不是只读的,可以通过更改 length 来从末尾添加或删除元素。
如果改变 length 为更小的值,则从末尾删除元素。
如果改变 length 为更大的值,则末尾为空值。
let a = [1,2,3,4];
a.length = 3; // a === [1,2,3]
a.length = 4; // a === [1,2,3,empty]
12.4 Array.isArray()
判断一个变量是否为数组
let a = [];
Array.isArray(a); // true
12.5 迭代器方法
Es6 中 Array 暴露了 keys,values,entries 方法。
keys 为索引,values 为值,entries 为 [index, value]
for (const [index, value] of [1,2,3]) {
console.log(index);
console.log(value);
}
12.6 填充方法 fill(), copyWithin()
首先注意这两个方法都不会改变数组的长度。
- fill() 顾名思义给数组填充。接受3个参数。
第一个参数为要填充的值,必填。
第二个参数为填充开始的索引。
第三个参数为填充结束的索引。
如果不声明后两个参数则为填充所有。
不声明第三个则为填充从开始索引到数组最后。 - copyWithin() 也是填充,但是是复制数组某个范围的值来填充。
第一个参数为要填充的开始索引,必填。
第二个参数为复制的数组的开始索引。
第三个参数为复制的数组的结束索引。
如果不声明后面两个参数则为默认第一个参数为0。 如果不声明第三个参数则为从开始索引直到数组最后。
12.7 数组转换方法
-
toString():把所有元素变成字符串,再用逗号拼接(注意没有空格)。
[1, 2, 3, 4].toString(); // "1,2,3,4" -
valueOf():返回数组自身
-
join():使用指定参数字符串拼接数组中的元素。
[1,2,3,4].join(","); // 相当于 toString()
12.8 栈方法
push() 和 pop(),会在数组末尾添加和删除元素。返回值都为添加/删除的那个值。
12.9 队列方法
push() 和 shift(),push() 不仅是栈方法也是队列方法,因为两者都是从末尾添加元素。区别在于取出元素,队列是从头部取数据。因此使用的是 shift() 方法。
shift() 方法会删除数组头部的元素,返回该值。
还有一个 unshift() 方法,可以在数组开头添加数据。
12.10 排序方法
12.10.1 数组反向 reverse()
顾名思义,数组反向排列即可。
[2,34,1,34].reverse(); // "34,1,24,2",只是单纯反向。
12.10.2 排序 sort()
将数组排序。
注意:sort() 会在排序前将元素转化为字符串,因此对于数字排序是有问题的。需要靠接受的函数参数来解决
// 由于转成字符串,排序数字会有问题
[1,2,3,4,5,10,20].sort(); // [1,10,2,20,3,4,5] ???????????????????????????
// 因此需要参数函数来帮助
[1,10,2,20,3,4,5].sort( (a,b) => a-b ); // 数字从小到大排序,改成 b-a 就是从大到小排序
该函数参数被称为比较函数,接受两个数值。如果要升序排序,则应该当第一个参数小于第二个参数,函数返回负值,大于的时候返回正值,相等的时候返回 0。比如:
.sort( (a,b) => a<b ? -1 : a>b ? 1 : 0 ); // 适用于多种情况,不只是数值
.sort( (a,b) => a<b ? 1 : a>b ? -1 : 0 ); // 降序版
不过如果是数值,可以就用上上段代码,返回 a-b 即可。
12.11 操作方法
- concat(),该方法可以接受多个参数,当参数为数组的时候,把这个数组摊开再添加到原数组上,当参数不是数组的时候,直接把这个元素加到原数组后面。返回值为新数组,原数组不会改变。
const a = [1,2,3,4];
const b = a.concat(5,"6",[{7:8}, 9]);
console.log(b); // [1,2,3,4,5,"6",{7:8},9]
可以用符号阻止数组被摊开:
const a = [1,2,3];
const b = [4,5];
b[Symbol.isConcatSpreadable] = false;
a.concat(b); // [1,2,3,[4,5]]
-
slice() 和字符串的行为类似,只是元素变成了数组元素。 返回值为新数组。
-
splice() 更强大的数组方法,有多种使用方法:
-
删除:传两个参数,第一个参数表示要删除的元素的位置,第二个参数表示要删除多少个元素。
-
替换:传三个或以上参数,上面的基础上,传第三个参数,表示在删除的部分插入的元素,还可以第四个,第五个等等,注意这里的参数和 concat 不一样的是,这里如果传数组不会被摊开。
-
插入:如果第二个参数设置为 0,其实就是相当于插入了。 和前面两个方法不同,splice 是直接改变原数组而不是创建一个新数组。
splice 的返回值是被删除的元素组成的数组。
12.12 搜索方法
- indexOf,lastIndexOf,includes 和字符串一样
- find,findIndex 方法:这两个方法分别返回第一个匹配参数函数的元素和该元素的索引。
- 接受的第一个函数被称作断言函数,只有匹配该函数的元素会被返回。
- 而 find 和 findIndex 就会返回第一个匹配该断言函数的元素/索引(或者说第一个在断言函数里返回 true 的元素/索引)
- 断言函数接受三个参数,分别为
(当前元素,当前元素的索引,数组本身)/(item, index, array)const a = [1,2,3,4,5,6]; a.find((element,index,array) => { return (element === 4) }); // 返回 4 - find 和 findIndex 除了第一个参数(断言函数)之外,还可以接受第二个参数,可以指定断言函数内部的 this 的值。
12.13 迭代方法
- React 里经常会用到的 map,就属于迭代方法的一种。
迭代方法接受两个参数,第一个参数为函数,对数组里的每个元素运行,第二个参数为该函数里的 this 值。
数组有 5 个迭代方法:
- every,如果数组每一项都返回 true,则整体返回 true
- filter,只有返回 true 的元素会在最后组成数组被返回
- forEach,仅仅只是对每一项运行函数,相当于遍历数组
- map,把每一项的返回值组成数组
- some,和 every 对应,只要有一项返回 true,则整体返回 true
迭代方法接受的参数为 (item, index, array)
12.14 归并方法
reduce() 和 reduceRight(),前者从头到尾,后者从尾到头。
归并指的是,遍历每一项,但是和迭代方法不一样,只返回一个综合的值。
接受的参数为 (prev, cur, index, array),其中 prev 为上次遍历的返回值。cur 为这次遍历的元素。
[1,2,3,4].reduce( (prev, cur) => {
return (prev + cur);
}) // 数组里所有的元素加和。此例返回 10