学而时习之 不亦说乎

现代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对数组对象有特别的优化,如果需要有序集合就不要像使用对象那样使用它,否则他的速度优化会被关闭

  1. 添加一个非数字的属性,比如 arr.test = 5。
  2. 制造空洞,比如:添加 arr[0],然后添加 arr[1000] (它们中间什么都没有)。
  3. 以倒序填充数组,比如 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

为对象增加可迭代方法,

  1. 为对象增加这个方法,这个方法必须return 一个迭代器,里面必须包含next()
  2. 这样该对象支持for..of
  3. for..of循环到时就会调用next()方法
  4. 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
  }
*/

评论已关闭