现代JavaScript教程 数据类型
发表于:2021-05-31分类:JavaScript笔记原始类型的方法
JavaScript有7种原始类型,原始类型是可以当作对象
使用内置方法的。
let str = "Hello"
console.log( str.toUpperCase() );//HELLO 转大写的方法
console.log(str);//Hello 原值没有变化
数字类型
let a = 1000000000;/* 1_000_000_000和这个一样 js运行使用下划线作为分隔符,任意阅读千位 */
let a = 1e9;//1右边9个0
let b = 0.001;
let b = 1e-3;//1左边3个0
let c=0xff; //255十六进制
let a = 0b11111111; // 二进制形式的 255
let b = 0o377; // 八进制形式的 255
//常用就上面3个其他进制使用parseInt()
/*
num.toString(base) 返回base指定的进制
*/
let num = 255;
num.toString(16);//16进制ff
num.toString(2);//2进制11111111
255..toString(16)//16进制ff
//直接在数字后面加..然后使用toString 也可以
let sum = 0.1 + 0.2;//0.30000000000000004
sum.toFixed(2);//0.30 取小数点后两位 四舍五入(注意得到的是字符串)
+sum.toFixed(2); //0.3 转数值
Math 数学函数
TODO
parseInt 和 parseFloat
//+和number()是严格转换的,原值内必须是个数值,否则报错(前后扣个可以忽略),parseInt,parseFloat就能实现
//从字符串中顺序“读取”数字,直到无法读取为止。
parseInt('100px')//100 整数
parseFloat('12.5em')//12.5 浮点数
parseInt('12.3')//12
parseFloat('12.3.4')//12.3
字符串类型
单引号、双引号脚本相同,反引号:允许换行、允许嵌入表达式${…}
`My\n`.length //3 字符串长度,注意这是属性,不是方法不用加()
let str = `Hello`;
str[0];//访问第一个字符,超出会未定义
str.charAt(0);//同上,超出返回空字符串
//注意以上方法不能修改字符串,但可以用串联的方式修改指定文字比如
str = 'h'+str[1]+...
//改变大小写
'Hello'.toUpperCase();//大小HELLO
'Hello'.toLowerCase();//小写hello
'Hello'[0].toLowerCase();//h (注意只返回了h)
//字符串还可以被遍历
for (let char of "Hello") {
console.log(char); // H,e,l,l,o(char 变为 "H",然后是 "e",然后是 "l" 等)
}
//查找字符
let str = 'Widget with id';
console.log( str.indexOf('Widget') ); // 0,因为 'Widget' 一开始就被找到
console.log( str.indexOf('widget') ); // -1,没有找到,检索是大小写敏感的
console.log( str.indexOf("id") ); // 1,"id" 在位置 1 处(……idget)
console.log( str.indexOf('id', 2) ) // 12,从第二个位置后找id
// str.lastIndexOf 这个可以从后往前找
let str = "Widget with id";//因为一开始就被找到会是0
if (str.indexOf("Widget") != -1) {//判断时只要不等于-1就说找到了
console.log("We found it"); // 现在工作了!
}
// str.includes(substr, pos) 查找是否含有,不需要位置
console.log( "Widget with id".includes("Widget") ); // true
console.log( "Hello".includes("Bye") ); // false
console.log( "Widget with id".includes("Wid",3) ); // false,从第三位开始找
console.log( "Widget".startsWith("Wid") ); // true,"Widget" 以 "Wid" 开始
console.log( "Widget".endsWith("get") ); // true,"Widget" 以 "get" 结束
//获取子字符串
let str = "stringify";
console.log( str.slice(0, 5) ); // 'strin',从 0 到 5 的子字符串(不包括 5)
console.log( str.slice(0, 1) ); // 's',从 0 到 1,但不包括 1,所以只有在 0 处的字符
console.log( str.slice(2) );//ringify 不指定结束就一直取到末尾
console.log( str.slice(-4, -1) );//gif,末尾第四个开始到末尾第一个
//str.substring 和slice几乎一样,开始和结束可以对掉,谁小谁是开头,但不允许负数,负数=0
console.log( str.substring(2, 6) ); // "ring"
console.log( str.substring(6, 2) ); // "ring"
//str.substr
console.log( str.substr(2, 4) );//ring ,从2开始 取4位
console.log( str.substr(-4, 2) );//gi,右侧第四位开始取2位
//substring和substr 不常用
数组
JavaScript中数组本质上是特殊的对象,所以他的也是通过引用来复制的,并且自带了一些属性和方法,
//创建数组
let arr = new Array();
let arr = [];
let arr = new Array("Apple", "Pear", "etc");//别用这个,可以指定长度
//访问数组
let fruits = ["Apple", "Orange", "Plum"];
console.log( fruits[0] ); // Apple
console.log( fruits[1] ); // Orange
console.log( fruits[2] ); // Plum
//修改元素
fruits[2] = 'Pear';
//增加元素
fruits[3] = 'Lemon';
//元素总数
fruits.length; //4
// 混合值
let arr = let arr = [ 'Apple', { name: 'John' }, true, function() { console.log('hello'); } ];
console.log( arr[1].name ); // John
arr[3](); // hello
//队列 队尾方式:push/pop 速度最快
let fruits = ["Apple", "Orange", "Pear"];
console.log( fruits.pop() ); //Pear, 取出最后一条
console.log( fruits ); // [ 'Apple', 'Orange' ]
console.log( fruits.push("Pear") );//3,队尾添加一条
console.log( fruits ); // Apple, Orange, Pear
//队头方式:shift/unshift 比较慢
let fruits = ["Apple", "Orange", "Pear"];
console.log( fruits.shift() ); // apple,取出第一条
console.log( fruits ); // Orange, Pear
console.log( fruits.unshift('Apple') );//3,队头增加一条
console.log( fruits ); // Apple, Orange, Pear
//可以一次性增加多个
let fruits = ["Apple"];
fruits.push("Orange", "Peach");//加队尾
fruits.unshift("Pineapple", "Lemon");//加队头
console.log( fruits );// ["Pineapple", "Lemon", "Apple", "Orange", "Peach"]
JavaScript对数组对象有特别的优化,如果需要有序集合就不要像使用对象那样使用它,否则他的速度优化会被关闭
- 添加一个非数字的属性,比如 arr.test = 5。
- 制造空洞,比如:添加 arr[0],然后添加 arr[1000] (它们中间什么都没有)。
- 以倒序填充数组,比如 arr[1000],arr[999] 等等。
循环数组
let arr = ["Apple", "Orange", "Pear"];
for (let i = 0; i < arr.length; i++) {//小于长度
console.log( arr[i] );
}
//for..of方法(缺点是无法获取索引,只能拿到值)
for (let fruit of fruits) {
console.log( fruit );
}
//对象方法,因为数组也是对象所以可以for in(使用过内置属性比如length,会都列出来,速度慢不推荐)
for (let key in arr) {
console.log( arr[key] );
}
关于 “length”
//它其实不是数组个数,而是最大索引数+1
let fruits = [];
fruits[123] = "Apple";
console.log( fruits.length ); // 124
//length还可以被指定
let arr = [1, 2, 3, 4, 5];
arr.length = 2; // 截断到只剩 2 个元素
console.log( arr ); // [1, 2]
//可以利用上面的特性来清空数组
arr.length = 0;
多维数组
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
console.log( matrix[1][1] ); // 5
toString
返回以逗号分隔的字符串
let arr = [1, 2, 3];
console.log( arr ); // 1,2,3
//数组转字符串都是toString
console.log( [] + 1 ); // "1" 空字符串
console.log( [1] + 1 ); // "11" 1
console.log( [1,2] + 1 ); // "1,21" 1,2
比较数组
因为数组是特殊的对象,除非是引用赋值,否则==永远不会相等
数组方法
splice 删除 添加 插入元素
//delete的缺点
let arr = ["I", "go", "home"];
delete arr[1]; // 删除 "go"
console.log( arr[1] ); // undefined
// now arr = ["I", , "home"];
console.log( arr.length ); // 索引还在长度没变3
//arr.splice(start[, deleteCount, elem1, ..., elemN])
//从索引 start(负数就是从后往前) 开始删除 deleteCount 个元素并在当前位置插入 elem1, ..., elemN。最后返回已被删除元素的数组。
let arr = ["I", "study", "JavaScript"];
console.log( arr.splice(1, 1) ); // [ 'study' ],从索引 1 开始删除 1 个元素
console.log( arr ); // ["I", "JavaScript"]
let arr = ["I", "study", "JavaScript", "right", "now"];
console.log( arr.splice(0, 3, "Let's", "dance") );//[ 'I', 'study', 'JavaScript' ],从第0个开始删除3个,并插入2个,012索引返回
console.log( arr ) // now ["Let's", "dance", "right", "now"]
let arr = ["I", "study", "JavaScript"];
// 从索引 2 开始
// 删除 0 个元素
// 然后插入 "complex" 和 "language"
console.log( arr.splice(2, 0, "complex", "language"));//无返回,删除0 从2插入
console.log( arr ); // "I", "study", "complex", "language", "JavaScript"
concat 合并多个数组或值成为新数组
let arr = [1, 2];//合并后原数组不变
console.log( arr.concat([3, 4]) ); //[ 1, 2, 3, 4 ]
console.log( arr.concat([3, 4], [5, 6]) ); //[ 1, 2, 3, 4, 5, 6 ]
console.log( arr.concat([3, 4], 5, 6) ); //[ 1, 2, 3, 4, 5, 6 ]
遍历数组forEach
该方法允许为每个元素允许函数
/*
arr.forEach(function(item, index, array) {
// ... do something with item
});
*/
["Bilbo", "Gandalf", "Nazgul"].forEach(alert);//每个对象都调用alert
["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => {
alert(`值:${item},索引${index} 元数组${array}`);
});
数组中搜索
- arr.indexOf(item, from) 从索引 from 开始搜索 item,如果找到则返回索引,否则返回 -1。
- arr.lastIndexOf(item, from) —— 和上面相同,只是从右向左搜索。
- arr.includes(item, from) —— 从索引 from 开始搜索 item,如果找到则返回 true(译注:如果没找到,则返回 false)。
注意以上方法是严格相等===的
find 和 findIndex
得到新数组,找值(元素)find
let result = arr.find(function(item, index, array) {
// 如果返回 true,则返回 item 并停止迭代
// 对于假值(falsy)的情况,则返回 undefined
//item 是元素(一般只用这个就够了),index 是它的索引,array 是数组本身。
});
let users = [
{id: 1, name: "John"},
{id: 2, name: "Pete"},
{id: 3, name: "Mary"}
];
let user = users.find(item => item.id == 1);//指定id=1的一条
//相当于普通写法:
let user = users.find(function(item) {
if(item.id = 1) return item.id;
});
console.log(user);//{ id: 1, name: 'John' }
找索引findIndex,用法类似,它主要返回元素索引
filter
(得到新数组)find返回第一个匹配的元素,filter可以找到多个
let results = arr.filter(function(item, index, array) {
// 如果 true item 被 push 到 results,迭代继续
// 如果什么都没找到,则返回空数组
});
let users = [
{id: 1, name: "John"},
{id: 2, name: "Pete"},
{id: 3, name: "Mary"}
];
// 返回前两个用户的数组
let someUsers = users.filter(item => item.id < 3);
//相当于普通写法:
let someUsers = users.filter(function(item){
if(item.id < 3) return item.id;
});
console.log(someUsers.length); // [ { id: 1, name: 'John' }, { id: 2, name: 'Pete' } ]
转换数组
map
得到新数组,注意forEach是直接改变数组本身的
let result = arr.map(function(item, index, array) {
// 返回新值而不是当前元素
})
let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length);//返回每个元素的长度
//相当于普通写法:
let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(function(item){
return item.length;
});
console.log(lengths); // 5,7,6
sort排序数组
重组原数组
let arr = [ 1, 2, 15 ];
// 该方法重新排列 arr 的内容(原位排序哦,不产生新内容)
arr.sort();
console.log( arr ); // 1, 15, 2 (默认按字符串排序)
//可以自定义排序方式
function compareNumeric(a, b) {
//先创建函数方法 冒泡排序
if (a > b) return 1;//正数往前
if (a == b) return 0;//不动
if (a < b) return -1;//负数往后
}
let arr = [ 1, 2, 15 ];
arr.sort(compareNumeric);
console.log(arr); // 1, 2, 15
//更短的写法
let arr = [ 1, 2, 15 ];
arr.sort(function(a, b) { return a - b; });//正数负数0
console.log(arr); // 1, 2, 15
//箭头写法
let arr = [ 1, 2, 15 ];
arr.sort((a,b)=> a - b );
console.log(arr); // 1, 2, 15
数组中可能存在很多内容,数字、对象、字符串都有可能,如何排序就需要用到自定义排序的方式
reverse颠倒数组
let arr = [1, 2, 3, 4, 5];
arr.reverse();
console.log( arr ); // 5,4,3,2,1
split 和 join
split,按指定分隔符把字符串分割成数组,join按指定分隔符将数组变成字符串
//分隔
let names = 'Bilbo,Gandalf,Nazgul';
let arr = names.split(',');//第二参数可以控制长度,多余的被忽略
console.log(arr);[ 'Bilbo', 'Gandalf', 'Nazgul' ]
//合并
let arr = ['Bilbo', 'Gandalf', 'Nazgul'];
let str = arr.join(';'); // 使用分号 ; 将数组粘合成字符串
console.log( str ); // Bilbo;Gandalf;Nazgul
reduce/reduceRight
遍历数组是forEach或for或for of
遍历元素是可以用map
reduce/reduceRight用来计算单个值,后者从右往左遍历
let value = arr.reduce(function(accumulator, item, index, array) {
// 2个参数,方法和初始值
/*
accumulator —— 是上一个函数调用的结果,第一次等于 initial(建议始终指定初始值)
item —— 当前的数组元素
index —— 当前索引
arr —— 数组本身
*/
//未指定初始值会取第一个元素作为初始值,并从第二个开始迭代,这有时会导致错误
}, [initial]);
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current) => sum + current, 1);
//相当于: sum就是上一次执行的return,如果没有就是初始值initial
let result = arr.reduce(function(sum,current){
return sum + current;
} , 0);
console.log(result); // 15
Array.isArray
数组是基于对象,所以单纯使用typeof 不能区分是数组还是对象,所以需要用到:
console.log(Array.isArray({})); // false
console.log(Array.isArray([])); // true
Iterable object(可迭代对象)
可迭代对象时数组的泛化、可以说任何对象都可以被定制为for..of的循环中使用的对象。数组可以迭代,很多内建对象也都可以迭代,比如说字符串也可以迭代。可以for..in的对象被称为可迭代的
Symbol.iterator
为对象增加可迭代方法,
- 为对象增加这个方法,这个方法必须return 一个迭代器,里面必须包含next()
- 这样该对象支持for..of
- for..of循环到时就会调用next()方法
- next()方法return的必须格式是:{done: Boolean, value: any},当done为true时表示迭代结束,否则value 就是下一个值,并继续迭代
let range = {
start: 1,
end: 5
};
// 我们希望 for..of 这样运行:指定了开始和结束
// for(let num of range) ... num=1,2,3,4,5
range[Symbol.iterator] = function() {//为数组增加可迭代方法(迭代器)
return {//该方法返回的就是迭代器对象(iterato object)
current:this.start,
end:this.end,
next(){//会在每轮迭代中被调用
if (this.current <= this.end) {//如果current 小于等于 end
return { done: false, value: this.current+2 };//不结束,返回值value,并+1
} else {//如果大于结束值 done
return { done: true };
}
}
}
}
//好了先range拥有了迭代方法,可以迭代了
for (let num of range) {
console.log(num); // 1, 然后是 2, 3, 4, 5
}
//注意range本身没有next方法,而是通过调用:range[Symbol.iterator]()创建了另一个对象,就是所谓迭代器,并由它的next方法来完成迭代。
//所以迭代器和迭代对象是分开的,但可以简化成:
let range = {
start: 1,
end: 5,
[Symbol.iterator]() {//内置定义迭代器方法
this.current = this.start;
return this;//把这个对象return返回出去(因为返回的是对象本身,注意下面的next也就被返回了)
},
next() {//对象内置迭代器next,上面return 时相当于也返回了next;(上例中是定义在迭代器中的)
if (this.current <= this.end) {//一样可以调用
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
for (let num of range) {
console.log(num); // 1, 然后是 2, 3, 4, 5
}
//这个例子是有缺点的,这样方法不能在多个对象上使用了,因为已经相当于内置与对象中了。不过这种情况很少遇到。
字符串时可迭代的
for (let char of "test") {
// 触发 4 次,每个字符一次
console.log( char ); // t, e, s, t
}
显式调用迭代器
这样可以清楚的看到调用过程,拥有更多控制权
let str = "Hello";
// 和 for..of 做相同的事
// for (let char of str) alert(char);
let iterator = str[Symbol.iterator]();//创建可迭代方法
while (true) {
let result = iterator.next();//调用迭代方法
if (result.done) break;//done为真时跳出,也就是结束了
console.log(result.value); // 一个接一个地输出字符
}
可迭代(iterable)和类数组(array-like)
iterable可迭代,就是实现了symbol.iterable方法的对象
Array-like类数组,时具有索引和length的属性对象,看起来像数组,但不是。
比如:字符串,可以迭代、又是类数组(有索引,有length属性),但是可迭代对象也许不是类数组,反之亦然,类数组对象可能不可迭代。
//这个对象类数组,
let arrayLike = { // 有索引和 length 属性 => 类数组对象
0: "Hello",
1: "World",
length: 2
};
// Error (no Symbol.iterator)
for (let item of arrayLike) {}
可迭代对象和类数组对象通常都不是数组,没有push和pop方法,如果有这样的对象,我们还想像数组那样操作它,会非常不方便
Array.from
可以接受一个可迭代对象或类数组,并获取成一个“真正的”数组,这样就可以用数组的方法啦
let arrayLike = {
0: "Hello",
1: "World",
length: 2
};
let arr = Array.from(arrayLike); // 获取并变成数组
console.log(arr.pop()); // World(pop 方法有效)
Array.from(obj[, mapFn, thisArg])
其中mapFn可以指定一个函数,thisArg可以为上面的函数设置一个this。
//指定方法求平方
let range = [1,2,3,4,5];
let arr = Array.from(range, num => num * num);
console.log(arr); // 1,4,9,16,25
// 将 str 拆分为字符数组
let str = 'ok';
let chars = Array.from(str);
console.log(chars[0]); // o
console.log(chars[1]); // k
console.log(chars.length); // 2
//上面的方法和下面一样,但是Array.from,更精简更优雅
let str = 'ok';
let chars = []; // 新建数组
for (let char of str) {//因为字符串可以迭代所以可以for of
chars.push(char);//逐个push值进chars
}
console.log(chars);
Map and Set(映射和集合)
map侧重于带键的数据项集合,允许任何类型的键(key),包括用对象当键,map有更强大的处理函数
let map = new Map();//创建函数
map.set('1', 'str1'); // 字符串键
map.set(1, 'num1'); // 数字键
map.set(true, 'bool1'); // 布尔值键
// 对象会将键转化为字符串
// Map 则会保留键的类型,所以下面这两个结果不同:
console.log( map.get(1) ); // 'num1'
console.log( map.get('1') ); // 'str1'
console.log( map.size ); // 3 元素个数
console.log( map.has(1) );//true 存在否
console.log( map.delete(1) );//删除某个键的值
console.log( map.clear() );//清空map
//链式调用
map.set('1', 'str1')
.set(1, 'num1')
.set(true, 'bool1');
map迭代
keys()返回全部的键,values()返回全部值,entries()返回所以实体(key和value)for..of可用
let recipeMap = new Map([
['cucumber', 500],
['tomatoes', 350],
['onion', 50]
]);
// 遍历所有的键(vegetables)
for (let vegetable of recipeMap.keys()) {
console.log(vegetable); // cucumber, tomatoes, onion
}
// 遍历所有的值(amounts)
for (let amount of recipeMap.values()) {
console.log(amount); // 500, 350, 50
}
// 遍历所有的实体 [key, value]
for (let entry of recipeMap) { // 与 recipeMap.entries() 相同
console.log(entry); // cucumber,500 (and so on)
}
//map也支持forEach方法
// 对每个键值对 (key, value) 运行 forEach 函数
recipeMap.forEach( (value, key, map) => {
console.log(`${key}: ${value}`); // 输出所有键和值
});
Object.entries:从对象创建 Map
该内建方法返回对象的键/值,该数组完全符合map
let obj = {
name: "John",
age: 30
ma:function(){console.log('haople');}
};
let map = new Map(Object.entries(obj));
console.log( map.get('name') ); // John
Object.fromEntries:从 Map 创建对象
let prices = Object.fromEntries([//从数组创建
['banana', 1],
['orange', 2],
['meat', 4]
]);
// 现在 prices = { banana: 1, orange: 2, meat: 4 }
console.log(prices.orange); // 2
let map = new Map();//从map创建
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);
let obj = Object.fromEntries(map); // 返回实体并创建一个普通对象
// obj = { banana: 1, orange: 2, meat: 4 }
console.log(obj.orange); // 2
Set 集合
set没有键(key),值只能出现一次。
let set = new Set();
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
// visits,一些访客来访好几次
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);
// set 只保留不重复的值
console.log( set.size ); // 3
console.log( set.has(john)); //true存在
for (let user of set) {
console.log(user.name); // John(然后 Pete 和 Mary)
}
set.delete(john); //删除john
set.clear(); //清空
Set迭代
let set = new Set(["oranges", "apples", "bananas"]);
//for..of方法
for (let value of set) console.log(value, "for..of");
// 与 forEach 相同:
set.forEach((value, valueAgain, set) => {
console.log(value, "forEach");//因为set没有键,所以返回的都是值
});
// 遍历所有的值(amounts).keys和。value一样效果
for (let amount of set.keys()) {
console.log(amount); // oranges,,apples,bananas
}
// 遍历所有的实体 [value]
for (let entry of set) { // 与 set.entries() 相同
console.log(entry); // oranges,,apples,bananas
}
Object.keys,values,entries
keys values entries在map、set、array里都有
对象也是可以使用这些方法的,不过要是使用:Object.keys(obj)
这样的方法
对象想使用例如map、set里的一些方法或特性,可以先转set,然后链式使用
let prices = {
banana: 1,
orange: 2,
meat: 4,
};
//prices对象所有值x2
let doublePrices = Object.fromEntries(//从数组或集合创建对象
// 转换为数组,之后使用 map 方法迭代x2
Object.entries(prices).map(([key, value]) => [key, value * 2])
);
console.log(doublePrices.meat); // 8
解构赋值
数组解构
本身没有变化,产生新的变量。
// 我们有一个存放了名字和姓氏的数组
let arr = ["Ilya", "Kantor"]
// 解构赋值 相当于:
// sets firstName = arr[0]
// and surname = arr[1]
let [firstName, surname] = arr;
console.log(firstName); // Ilya
console.log(surname); // Kantor
let [firstName, surname] = "Ilya Kantor".split(' ');//效果同上
// 不需要第二个元素
let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
console.log( title ); // Consul
//可以与任何可迭代对象使用
let [a, b, c] = "abc"; // ["a", "b", "c"]
let [one, two, three] = new Set([1, 2, 3]);
//也可以直接赋值给对象,准确的说是任何可被赋值的东西
let user = {};
[user.name, user.surname] = "Ilya Kantor".split(' ');
console.log(user.name); // Ilya
//在for..in方法里使用
let user = {
name: "John",
age: 30
};
// 循环遍历键—值对
for (let [key, value] of Object.entries(user)) {
console.log(`${key}:${value}`); // name:John, then age:30
}
//map里
let user = new Map();
user.set("name", "John");
user.set("age", "30");
for (let [key, value] of user) {
alert(`${key}:${value}`); // name:John, then age:30
}
//通过...接受后面的数据,不要抛弃
let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
console.log(name1); // Julius
console.log(name2); // Caesar
// 请注意,`rest` 的类型是数组
console.log(rest[0]); // Consul
console.log(rest[1]); // of the Roman Republic
console.log(rest.length); // 2 长度
let [firstName, surname] = ["Leon"];//当解构数据不足时
console.log(firstName); // Leon
console.log(surname); // undefined 不会报错
// 提供默认值
let [name = "Guest", surname = "Anonymous"] = ["Julius"];
console.log(name); // Julius(来自数组的值)
console.log(surname); // Anonymous(默认值被使用了)
对象解构
let obj = {
name: "John",
age: 30,
ma:function(){console.log('haople');}
};
let {age,ma} = obj;//只取需要的内容
ma();//方法也被解构
console.log(age);//30 ,与数组解构不同的是:顺序不重要,会对应变量名(映射关系)
//属性名字给另一个变量,可以用冒号指定,还可以设置缺失属性的默认值,可以冒号和等号结合使用
let {name: n, age: a, title:t = '没有',sex = "未知"} = obj;
// 冒号表示“什么值:赋值给谁” ,name -> n ,age -> a
console.log(n); // John
console.log(a); // 30
console.log(t); //没有
console.log(sex); //未知
//剩余模式 ...,其他属性成为单独对象
let {name, ...rest} = obj;
console.log(name);//John
console.log(rest.age);//30
嵌套结构
对象或数组包含了其他对象和数组,可以使用更复杂一点的方法提取更深层次的数据
let options = {
size: {
width: 100,
height: 200
},
items: ["Cake", "Donut"],
extra: true
};
//方便理解写成多行
let {
size: { // 把 size 赋值到这里
width,
height
},
items: [item1, item2], // 把 items 赋值到这里
title = "Menu" // 在对象中不存在(使用默认值)
} = options;
console.log(height,item1,title); //200 Cake Menu
//注意,size 和 items 没有对应的变量,因为我们取的是它们的内容。
智能函数参数
//当一个函数有多个参数是可选的,调用时就很不优雅
function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
// ...
}
// 在采用默认值就可以的位置设置 undefined
showMenu("My Menu", undefined,undefined , ["Item1", "Item2"])
//太难读懂了,如果使用解构赋值的话,就好啦!
// 我们传递一个对象给函数
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};
function showMenu({title = "Untitled", width = 200, height = 100, items = []}) {
//注意加个{}变对象解构
}
showMenu(options);//瞧瞧 多优雅!
//上例中嵌套也是可以的
function showMenu({
title = "Untitled",
width: w = 100, // width goes to w
height: h = 200, // height goes to h
items: [item1, item2] // items first element goes to item1, second to item2
}) {
//item1和2 直接解构,不用当数组调用了
console.log(item1,item2)
}
showMenu(options);//瞧瞧 更优雅了!
//注意如上例中,全部参数都有默认值,但调用时应当注意
showMenu();//使用了解构方法,这样不行
showMenu({});//至少要传入一个空对象让其可以解构
//或者
function showMenu({title = "Untitled", width = 200, height = 100, items = []} = {}) {
//这样默认值有了空对象,可以直接showMenu()了
}
日期和时间
JavaScript中内建对象Date可以帮助我们使用时间方法、存储时间、测量时间
let now = new Date();
console.log(now);//当前时间
let utc = new Date(0);//获取utc时间
let utc2 = new Date(3600*1000);//utc时间+3600000毫秒(utc后1小时)
let utc2 = new Date(-3600*1000);//utc时间+3600000毫秒(utc前1小时)
let date = new Date("2017-01-26");//解析字符串
//一些内置方法
date.getFullYear()//获取年份2021
.getMonth()//月份0-11
.getData()//1-31
getHours(),getMinutes(),getSeconds(),getMilliseconds()//相应的时间
getDay()//本周第几天,0-6,0是周日,星期天是第一天!
//UTC时间,get后加入UTC就能得到utc时间
getUTCDay();
getTime();//获取当前时间戳,毫秒级,不能使用utc
getTimezoneOffset();//返回当地时间和utc时间的时差,分钟级
下列方法可以设置日期/时间组件:
- setFullYear(year, [month], [date])
- setMonth(month, [date])
- setDate(date)
- setHours(hour, [min], [sec], [ms])
- setMinutes(min, [sec], [ms])
- setSeconds(sec, [ms])
- setMilliseconds(ms)
- setTime(milliseconds)(使用自 1970-01-01 00:00:00 UTC+0 以来的毫秒数来设置整个日期)
以上方法除了 setTime() 都有 UTC 变体,例如:setUTCHours()。使用方法:
let today = new Date();//获取当前时间,并存入today
today.setHours(0);
alert(today); // 日期依然是今天,但是小时数被改为了 0
today.setHours(0, 0, 0, 0);
alert(today); // 日期依然是今天,时间为 00:00:00。
自动校准
//3天后是几号?直接+3就行,会自动校准
let date = new Date(2016, 1, 28);//注意这是2016年2月28日
date.setDate(date.getDate() + 3);//获取日期然后+2天
console.log( date ); // 2016年3月1日
let date = new Date();
date.setSeconds(date.getSeconds() + 70);//70秒后
日期转化为数字,日期差值
let date = new Date();
console.log(+date);//转数字,和获取时间戳getTime一样
//差值计算
let start = new Date(); // 开始测量时间
// 循环10万次
for (let i = 0; i < 100000; i++) {
let doSomething = i * i * i;
}
let end = new Date(); // 结束测量时间
console.log( `${end - start} ms` );//得到差值(相减的),毫秒级
//还可以使用Date.now这样的方法,不用创建对象了,它相当于:new Date().getTime()
JSON 方法,toJSON
json中字符串使用双引号(单引号反引号被转)属性名也强制转为双引号,支持嵌套
不参与转换会被跳过的:函数属性(方法),symbol类型的属性、存储undefined的属性
可以有引用,但不能是互相引用的循环引用
//JSON.stringify 将对象或数组转为json
//JSON.parse 将 JSON 转换回对象
let student = {//对象
name: 'John',
age: 30,
isAdmin: false,
courses: ['html', 'css', 'js'],
wife: null
};
let json = JSON.stringify(student);//转json
console.log(typeof json); // 变成字符串string!
console.log(json);
/* JSON 编码的对象:
{
"name": "John",
"age": 30,
"isAdmin": false,
"courses": ["html", "css", "js"],
"wife": null
}
*/
console.log(typeof JSON.parse(json));//object 转回去
let json = JSON.stringify(value[, replacer, space])
value:要转的值
replacer:值的属性数组或映射函数 function(key, value)。注意解码时也支持此参数(可以将指定的字符串变成转换成实际的方法)
space:用于格式化的空格数量(输出到日志的时候美化用,默认无换号无缩进,加这个可以有!)
let room = {number: 23};
let meetup = {
title: "Conference",
participants: [{name: "John"}, {name: "Alice"}],
place: room // meetup 引用了 room
};
room.occupiedBy = meetup; // room 引用了 meetup
//replacer过滤
console.log( JSON.stringify(meetup, ['title', 'participants']) );
// {"title":"Conference","participants":[{},{}]} 因为只有title和participants,其中的name、后添加的occupiedBy和place引用和引用里的number,没有所以被过滤了。
//除了occupiedBy的话需使用:JSON.stringify(meetup, ['title', 'participants', 'place', 'name', 'number']) )
//不是很优雅太长了
//可以使用function replacer(key, value) TODO
自定义toJSON
let room = {
number: 23,
toJSON() {//自定义json方法,当被json的时候返回24
return this.number+1;
}
};
let meetup = {
title: "Conference",
room //被引用
};
console.log(meetup);
/* 没json的时候,结构正常
{
title: 'Conference',
room: { number: 23, toJSON: [Function: toJSON] }
}
*/
console.log( JSON.stringify(room) ); // 24,room被json就返回指定的24
console.log( JSON.stringify(meetup) );// json到room的时候出发tojson,返回24变成:
/*
{
"title":"Conference",
"room": 24
}
*/
评论已关闭