ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准,2015.06 发版。
ES6 主要是为了解决 ES5 的先天不足,比如 JavaScript 里并没有类的概念,
ES6新增了let和const来声明变量,主要是解决var声明变量所造成的困扰和问题:
let site = 'itLike';
let site = 'itLike';
console.log(site);
console.log(site);
let site = 'itLike';
const E = 2.718;
E = 2.71;
console.log(E);
// 引用类型
const LK = {
name:'itLike',
intro: '喜欢IT, 就上撩课(itLike.com)'
};
LK.name = '撩课';
console.log(LK);
{let site = 'itLike';}
console.log(site);
if(1){ let str = '04'; }
console.log(str);
解构赋值是对赋值运算符的扩展。
他是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。
在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段获取。
let nameArr = ['撩课', '小撩', '小煤球'];
let name1 = nameArr[0];
let name2 = nameArr[1];
let name3 = nameArr[2];
// 解构写法
let [name1, name2, name3] = nameArr;
console.log(name1, name2, name3);
// 写法1
let {name, age, sex}
= {name: '小煤球', age: 1, sex: '公'};
// 结果: 小煤球 1 公
console.log(name, age, sex);
// 写法2: 解构重命名
let {name: lkName, age: lkAge, sex: lkSex}
= {name: '小煤球', age: 1, sex: '公'};
// 结果: 小煤球 1 公
console.log(lkName, lkAge, lkSex);
// 写法3: 可以设置默认值
let {name, age, sex = '公'}
= {name: '小煤球', age: 1};
console.log(sex); // 公
// 写法4:省略解构
let [, , sex] = ['小煤球', 1, '公 '];
console.log(sex);
模板字符串用反引号()包含,变量用${}括起来; 在开发中使用是非常灵活的。
let name = '小煤球';
let sex = '公';
let result = `我叫 ${name} , 我是 ${sex} 的`;
console.log(result);
let url = 'http://www.itlike.com';
console.log(url.startsWith('http')); // true
let file = 'index.html';
console.log(file.endsWith('html')); // true
let str = 'liaoke';
console.log(str.includes('ao')); // true
let title = '撩课在线';
console.log(title.repeat(100));
padStart()用于头部补全,
padEnd()用于尾部补全;
第一个参数用来指定字符串的最小长度,
第二个参数是用来补全的字符串。
// "2030111111"
let y1 = '2030'.padEnd(10, '1');
// "2030-11-22"
let y2 = '11-22'.padStart(10, '2030-MM-DD');
console.log(y1, y2);
let arr1 = [ 'a', 'b', 'c'];
let arr2 = [1, 2, 3];
let result = [...arr1, ...arr2];
console.log(result);
// [ "a", "b", "c", 1, 2, 3 ]
let smallDog = {name:'小煤球', age: 1};
let bigDog = {name: 'Python', age: 2};
let dog = {...smallDog, ...bigDog};
console.log(dog);
// {name: "Python", age: 2}
注意: 如果对象中的属性一致, 会被覆盖
function getMinValue() {
console.log(Math.min(...arguments));
}
getMinValue(1, -99, 22, 10, 9); // -99
0.1 + 0.2 === 0.3; // false
// 在误差范围内即视为相等
var equal = (Math.abs(0.1 - 0.3 + 0.2) < Number.EPSILON); // true
最大安全整数
安全整数:Number.MAX_SAFE_INTEGER
安全整数表示在 JavaScript 中能够精确表示的整数,安全整数的范围在 2 的 -53 次方到 2 的 53 次方之间(不包括两个端点),超过这个范围的整数无法精确表示。
最大安全整数
安全整数范围的上限,即 2 的 53 次方减 1 。
最小安全整数
安全整数范围的下限,即 2 的 53 次方减 1 的负数。
Number.MIN_SAFE_INTEGER
ES6 在 Math 对象上新增了 17 个数学相关的静态方法,这些方法只能在 Math 中调用。
Math.cbrt(1); // 1
Math.cbrt(0); // 0
Math.cbrt(-1); // -1
// 会对非数值进行转换
Math.cbrt('1'); // 1
// 非数值且无法转换为数值时返回 NaN
Math.cbrt('hhh'); // NaN
两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。
// 大多数情况下,结果与 a * b 相同
Math.imul(1, 2); // 2
// 用于正确返回大数乘法结果中的低位数值
Math.imul(0x7fffffff, 0x7fffffff); // 1
Math.hypot(3, 4); // 5
// 非数值会先被转换为数值后进行计算
Math.hypot(1, 2, '3'); // 3.741657386773941
Math.hypot(true); // 1
Math.hypot(false); // 0
// 空值会被转换为 0
Math.hypot(); // 0
Math.hypot([]); // 0
// 参数为 Infinity 或 -Infinity 返回 Infinity
Math.hypot(Infinity); // Infinity
Math.hypot(-Infinity); // Infinity
// 参数中存在无法转换为数值的参数时返回 NaN
Math.hypot(NaN); // NaN
Math.hypot(3, 4, 'foo'); // NaN
Math.hypot({}); // NaN
Math.clz32(0); // 32
Math.clz32(1); // 31
Math.clz32(0b01000000000100000000000000000000); // 1
// 当参数为小数时,只考虑整数部分
Math.clz32(0.5); // 32
// 对于空值或非数值,会转化为数值再进行计算
Math.clz32('1'); // 31
Math.clz32(); // 32
Math.clz32([]); // 32
Math.clz32({}); // 32
Math.clz32(NaN); // 32
Math.clz32(Infinity); // 32
Math.clz32(-Infinity); // 32
Math.clz32(undefined); // 32
Math.clz32('hhh'); // 32
用于返回数字的整数部分。
Math.trunc(12.3); // 12
Math.trunc(12); // 12
// 整数部分为 0 时也会判断符号
Math.trunc(-0.5); // -0
Math.trunc(0.5); // 0
// Math.trunc 会将非数值转为数值再进行处理
Math.trunc("12.3"); // 12
// 空值或无法转化为数值时时返回 NaN
Math.trunc(); // NaN
Math.trunc(NaN); // NaN
Math.trunc("hhh"); // NaN
Math.trunc("123.2hhh"); // NaN
Math.fround
用于获取数字的32位单精度浮点数形式。
// 对于 2 的 24 次方取负至 2 的 24 次方之间的整数(不含两个端点),返回结果与参数本身一致
Math.fround(-(2**24)+1); // -16777215
Math.fround(2 ** 24 - 1); // 16777215
// 用于将 64 位双精度浮点数转为 32 位单精度浮点数
Math.fround(1.234) // 1.125
// 当小数的精度超过 24 个二进制位,会丢失精度
Math.fround(0.3); // 0.30000001192092896
// 参数为 NaN 或 Infinity 时返回本身
Math.fround(NaN) // NaN
Math.fround(Infinity) // Infinity
// 参数为其他非数值类型时会将参数进行转换
Math.fround('5'); // 5
Math.fround(true); // 1
Math.fround(null); // 0
Math.fround([]); // 0
Math.fround({}); // NaN
判断
Math.sign
判断数字的符号(正、负、0)。
Math.sign(1); // 1
Math.sign(-1); // -1
// 参数为 0 时,不同符号的返回不同
Math.sign(0); // 0
Math.sign(-0); // -0
// 判断前会对非数值进行转换
Math.sign('1'); // 1
Math.sign('-1'); // -1
// 参数为非数值(无法转换为数值)时返回 NaN
Math.sign(NaN); // NaN
Math.sign('hhh'); // NaN
对数方法
Math.expm1()
用于计算 e 的 x 次方减 1 的结果,即 Math.exp(x) - 1 。
Math.expm1(1); // 1.718281828459045
Math.expm1(0); // 0
Math.expm1(-1); // -0.6321205588285577
// 会对非数值进行转换
Math.expm1('0'); //0
// 参数不为数值且无法转换为数值时返回 NaN
Math.expm1(NaN); // NaN
Math.log1p(x)
用于计算1 + x 的自然对数,即 Math.log(1 + x) 。
Math.log1p(1); // 0.6931471805599453
Math.log1p(0); // 0
Math.log1p(-1); // -Infinity
// 参数小于 -1 时返回 NaN
Math.log1p(-2); // NaN
Math.log10(x)
用于计算以 10 为底的 x 的对数。
Math.log1p(1); // 0.6931471805599453
Math.log1p(0); // 0
Math.log1p(-1); // -Infinity
// 参数小于 -1 时返回 NaN
Math.log1p(-2); // NaN
Math.log10(1); // 0
// 计算前对非数值进行转换
Math.log10('1'); // 0
// 参数为0时返回 -Infinity
Math.log10(0); // -Infinity
// 参数小于0或参数不为数值(且无法转换为数值)时返回 NaN
Math.log10(-1); // NaN
Math.log2()
用于计算 2 为底的 x 的对数。
Math.log2(1); // 0
// 计算前对非数值进行转换
Math.log2('1'); // 0
// 参数为0时返回 -Infinity
Math.log2(0); // -Infinity
// 参数小于0或参数不为数值(且无法转换为数值)时返回 NaN
Math.log2(-1); // NaN
双曲函数方法
Math.sinh(x): 用于计算双曲正弦。
Math.cosh(x): 用于计算双曲余弦。
Math.tanh(x): 用于计算双曲正切。
Math.asinh(x): 用于计算反双曲正弦。
Math.acosh(x): 用于计算反双曲余弦。
Math.atanh(x): 用于计算反双曲正切。
指数运算符
1 ** 2; // 1
// 右结合,从右至左计算
2 ** 2 ** 3; // 256
// **=
let exam = 2;
exam ** = 2; // 4
基本用法
function fn(name,age=17){
console.log(name+","+age);
}
fn("Amy",18); // Amy,18
fn("Amy",""); // Amy,
fn("Amy"); // Amy,17
注意点:使用函数默认参数时,不允许有同名参数。
// 不报错
function fn(name,name){
console.log(name);
}
// 报错
//SyntaxError: Duplicate parameter name not allowed in this context
function fn(name,name,age=17){
console.log(name+","+age);
}
只有在未传递参数,或者参数为 undefined 时,才会使用默认参数,null 值被认为是有效的值传递。
function fn(name,age=17){
console.log(name+","+age);
}
fn("Amy",null); // Amy,null
函数参数默认值存在暂时性死区,在函数参数默认值表达式中,还未初始化赋值的参数值无法作为其他参数的默认值。
function f(x,y=x){
console.log(x,y);
}
f(1); // 1 1
function f(x=y){
console.log(x);
}
f(); // ReferenceError: y is not defined
function f(...values){
console.log(values.length);
}
f(1,2); //2
f(1,2,3,4); //4
箭头函数提供了一种更加简洁的函数书写方式。基本语法是:
参数 => 函数体
function 函数名称(参数列表){
函数执行体
}
this指针的固化
var 函数名称 = (参数列表)=>{
函数执行体
}
基本用法:
var f = v => v;
//等价于
var f = function(a){
return a;
}
f(1); //1
当箭头函数没有参数或者有多个参数,要用 () 括起来。
var f = (a,b) => a+b;
f(6,2); //8
当箭头函数函数体有多行语句,用 {} 包裹起来,表示代码块,当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。
var f = (a,b) => {
let result = a+b;
return result;
}
f(6,2); // 8
// 报错
var f = (id,name) => {id: id, name: name};
f(6,2); // SyntaxError: Unexpected token :
// 不报错
var f = (id,name) => ({id: id, name: name});
f(6,2); // {id: 6, name: 2}
注意点:没有 this、super、arguments 和 new.target 绑定。
var func = () => {
// 箭头函数里面没有 this 对象,
// 此时的 this 是外层的 this 对象,即 Window
console.log(this)
}
func(55) // Window
var func = () => {
console.log(arguments)
}
func(55); // ReferenceError: arguments is not defined
箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象。
function fn(){
setTimeout(()=>{
// 定义时,this 绑定的是 fn 中的 this 对象
console.log(this.a);
},0)
}
var a = 20;
// fn 的 this 对象为 {a: 19}
fn.call({a: 18}); // 18
var a = {
a: 18,
fn:function(){
setTimeout(()=>{
// 定义时,this 绑定的是 fn 中的 this 对象
console.log(this.a);
},0)
}
}
// call():
//将该函数的定义放到传入的对象下
//调用
不可以作为构造函数,也就是不能使用 new 命令,否则会报错
// 回调函数
var Person = {
'age': 18,
'sayHello': function () {
setTimeout(function () {
console.log(this.age);
});
}
};
var age = 20;
Person.sayHello(); // 20
var Person1 = {
'age': 18,
'sayHello': function () {
setTimeout(()=>{
console.log(this.age);
});
}
};
var age = 20;
Person1.sayHello(); // 18
所以,当我们需要维护一个 this 上下文的时候,就可以使用箭头函数。
var Person = {
'age': 18,
'sayHello': ()=>{
console.log(this.age);
}
};
var age = 20;
Person.sayHello(); // 20
// 此时 this 指向的是全局对象
var Person1 = {
'age': 18,
'sayHello': function () {
console.log(this.age);
}
};
var age = 20;
Person1.sayHello(); // 18
// 此时的 this 指向 Person1 对象
需要动态 this 的时候
var button = document.getElementById('userClick');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
button 的监听函数是箭头函数,所以监听函数里面的 this 指向的是定义的时候外层的 this 对象,即 Window,导致无法操作到被点击的按钮对象。
console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]
// 参数值可为不同类型
console.log(Array.of(1, '2', true)); // [1, '2', true]
// 参数为空时返回空数组
console.log(Array.of()); // []
// 参数为数组,返回与原数组一样的数组
console.log(Array.from([1, 2])); // [1, 2]
// 参数含空位
console.log(Array.from([1, , 3])); // [1, undefined, 3]
参数
Array.from(arrayLike[, mapFn[, thisArg]])
返回值为转换后的数组。
arrayLike
想要转换的类数组对象或可迭代对象。
console.log(Array.from([1, 2, 3])); // [1, 2, 3]
mapFn
可选,map 函数,用于对每个元素进行处理,放入数组的是处理后的元素。
console.log(Array.from([1, 2, 3], (n) => n * 2)); // [2, 4, 6]
thisArg
可选,用于指定 map 函数执行时的 this 对象。
let map = {
do: function(n) {
return n * 2;
}
}
let arrayLike = [1, 2, 3];
console.log(Array.from(arrayLike, function (n){
return this.do(n);
}),map); // [2, 4, 6]
let arr = Array.from({
0: '1',
1: '2',
2: 3,
length: 3
});
console.log(); // ['1', '2', 3]
// 没有 length 属性,则返回空数组
let array = Array.from({
0: '1',
1: '2',
2: 3,
});
console.log(array); // []
// 元素属性名不为数值且无法转换为数值,返回长度为 length 元素值为 undefined 的数组
let array1 = Array.from({
a: 1,
b: 2,
length: 2
});
console.log(array1); // [undefined, undefined]
let map = new Map();
map.set('key0', 'value0');
map.set('key1', 'value1');
console.log(Array.from(map)); // [['key0', 'value0'],['key1',
// 'value1']]
转换 set集合
let arr = [1, 2, 3];
let set = new Set(arr);
console.log(Array.from(set)); // [1, 2, 3]
转换字符串
let str = 'abc';
console.log(Array.from(str)); // ["a", "b", "c"]
find()
查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素。
let arr = Array.of(1, 2, 3, 4);
console.log(arr.find(item => item > 2)); // 3
// 数组空位处理为 undefined
console.log([, 1].find(n => true)); // undefined
findIndex()
查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引。
let arr = Array.of(1, 2, 1, 3);
// 参数1:回调函数
// 参数2(可选):指定回调函数中的 this 值
console.log(arr.findIndex(item => item = 1)); // 0
// 数组空位处理为 undefined
console.log([, 1].findIndex(n => true)); //0
填充
fill()
将一定范围索引的数组元素内容填充为单个指定的值。
let arr = Array.of(1, 2, 3, 4);
// 参数1:用来填充的值
// 参数2:被填充的起始索引
// 参数3(可选):被填充的结束索引,默认为数组末尾
console.log(arr.fill(0,1,2)); // [1, 0, 3, 4]
copyWithin()
将一定范围索引的数组元素修改为此数组另一指定范围索引的元素。
// 参数1:被修改的起始索引
// 参数2:被用来覆盖的数据的起始索引
// 参数3(可选):被用来覆盖的数据的结束索引,默认为数组末尾
console.log([1, 2, 3, 4].copyWithin(0,2,)); // [3, 4, 3, 4]
// 参数1为负数表示倒数
console.log([1, 2, 3, 4].copyWithin(-2, 0)); // [1, 2, 1, 2]
console.log([1, 2, ,4].copyWithin(0, 2, 4)); // [, 4, , 4]
遍历
entries()
遍历键值对。
for(let [key, value] of ['a', 'b'].entries()){
console.log(key, value);
}
// 0 "a"
// 1 "b"
// 不使用 for... of 循环
let entries = ['a', 'b'].entries();
console.log(entries.next().value); // [0, "a"]
console.log(entries.next().value); // [1, "b"]
// 数组含空位
console.log([...[,-'a'].entries()]); // [[0, undefined], [1, "a"]]
keys()
遍历键名。
for(let key of ['a', 'b'].keys()){
console.log(key);
}
// 0
// 1
// 数组含空位
console.log([...[,'a'].keys()]); // [0, 1]
values()
遍历键值。
for(let value of ['a', 'b'].values()){
console.log(value);
}
// "a"
// "b"
// 数组含空位
console.log([...[,'a'].values()]); // [undefined, "a"]
包含
includes()
数组是否包含指定值。
注意:与 Set 和 Map 的 has 方法区分;Set 的 has 方法用于查找值;Map 的 has 方法用于查找键名。
// 参数1:包含的指定值
[1, 2, 3].includes(1); // true
// 参数2:可选,搜索的起始索引,默认为0
[1, 2, 3].includes(1, 2); // false
// NaN 的包含判断
[1, NaN, 3].includes(NaN); // true
嵌套数组转一维数组
flat()
console.log([1 ,[2, 3]].flat()); // [1, 2, 3]
// 指定转换的嵌套层数
console.log([1, [2, [3, [4, 5]]]].flat(2)); // [1, 2, 3, [4, 5]]
// 不管嵌套多少层
console.log([1, [2, [3, [4, 5]]]].flat(Infinity)); // [1, 2, 3, 4, 5]
// 自动跳过空位
console.log([1, [2, , 3]].flat()); // [1, 2, 3]
flatMap()
先对数组中每个元素进行了的处理,再对数组执行 flat() 方法。
// 参数1:遍历函数,该遍历函数可接受3个参数:当前元素、当前元素索引、原数组
// 参数2:指定遍历函数中 this 的指向
console.log([1, 2, 3].flatMap(n => [n * 2])); // [2, 4, 6]
var foo = "bar";
var baz = { foo };
baz.foo
baz; // { foo: "baz" }
// 等价于
var baz = { foo: foo };
function f(x, y) {
return { x, y };
// 等价于: return { x: x, y: y };
}
f(1, 2); // {x: 1, y: 2}
var obj = {
method() {
return "yuan is an animal";
}
}
// 等价于
var obj = {
method: function() {
return "yuan is an animal";
}
}
在ES5中,定义对象的属性有两种方法
// 方法一:标识符作为属性名
obj.foo = true;
// 方法二:表达式作为属性名
obj[“foo”] = true;
如果使用字面量方式定义对象,在ES5中只能使用方法一,而在ES6中可以使用方法二:
let yuan = "monkey";
let animal = {
"type": "animal",
[yuan]: "yuan is not monkey"
"monkey":"yuan is not monkey"
}
animal["type"]; // "animal"
animal[yuan]; // "yuan is not monkey"
animal["monkey"]; // "yuan is not monkey"
属性名定义方法:
let monkey = {
['yuan']() {
return "yuan is a monkey";
}
}
monkey.yuan(); // yuan is a monkey"
let monkey = {
['yuan']() {
return "yuan is a monkey";
}
}
monkey.yuan.name; // "yuan"
+0 === -0; // true
NaN === NaN; // false
Object.is(+0, -0); // false
Object.is(NaN, NaN); // true
let target = { x: 1};
let s1 = { y: 2 };
let s2 = { z: 3 };
Object.assign(target, s1, s2); // {x: 1, y: 2, z: 3}
注意点
1、如果目标对象与源对象用同名属性,或多个源对象具有同名属性,则后面的属性会覆盖前面的属性:
let target2 = { x: 1};
let s3 = { x: 3, y: 2 };
let s4 = { y: 4, z: 3 };
Object.assign(target2, s3, s4); // {x: 3, y: 4, z: 3}
2、如果参数不是对象,则会先转成对象,在返回:
typeof Object.assign(3); // "object"
3、若参数中有undefined 或者 null,这两个参数不能放在目标对象的位置,否则会报错:
Object.assign(undefined); // Cannot convert undefined or null to object at Function.assign ()
Object.assign(null); // Cannot convert undefined or null to object at Function.assign ()
4、除了字符串会以数组的形式复制到目标对象上,其他值都不会产生效果:
let a1 = 'yuan';
let a2 = true;
let a3 = 11;
let a4 = NaN;
Object.assign({}, a1, a2, a3, a4); // {0: "y", 1: "u", 2: "a", 3: "n"}
5、Object.assign()这个方法是对对象引用的复制,即是浅复制,而不是深复制。这里需要规避同名属性替换带来的问题:
var obj1 = { a: { b: 3, c: 4 } };
var obj2 = { a: { b: "yuan" } } ;
Object.assign(obj1, obj2); // { a: { b: "yuan" } }
obj1.a.b; // "yuan"
基本用途
给对象添加属性
class Geo {
constructor(x, y) {
Object.assign(this, x, y);
}
}
给对象添加方法
Object.assig(SomeClass.prototype, {
someMethod(arg1, arg2) { ... },
anotherMethod() { ... }
})
克隆对象
function clone(originObj) {
return Object.assign({}, originObj); // 将原始对象复制给空对象
}
合并多个对象
const mergeObjs = {
(target, ...sources) => Object.assign(target, ...sources);
}
为属性指定默认值
const DEFAULTS = {
logleve: 0,
outputFormat: 'html'
};
function processContent(options) {
retrun Object.assign({}, DEFAULTS, options);
}
ES5 中有3个操作会忽略 Enumerable 为 false 的属性:
1、for…in 循环:只遍历对象自身的和继承的可枚举性。
2、Object.keys():返回对象自身的所有可枚举属性的键名。
3、JSON.stringify():只串行化对象自身的可枚举属性。
在ES6 中,Object.assign() 操作会忽略 Enumerable 为false 的属性。
在上面四个操作中,只有 for…in 操作会返回继承的属性,为了不使问题复杂化,只关心对象自身的属性。所以,尽量不要使用 for…in 循环,用 Object.keys() 代替。
ES6还规定,所有 Class 的原型的方法都是不可枚举的。
在ES6 中有五种方法来遍历对象的属性:
1、for…in
返回对象自身的和继承的可枚举属性(不含 Symbol 属性)。
2、Object.keys(ob)
返回一个数组,包括对象自身的(不含继承)所有可枚举属性(不含 Symbol 属性)。
3、Object.getOwnPropertyNames(obj)
返回一个数组,包括自身的所有属性(不含Symbol属性,但包括不可枚举属性)。
4、Object.getOwnPropertySymbols(obj)
返回一个数组。包含对象自身的所有 Symbol 属性。
5、Object.ownKeys(obj)
返回一个数组,包含对象所有的属性.。
以上五种方法都遵循同样的遍历顺序:
(1)、首先遍历所有属性名为数值的属性,按数字排序;
(2)、其次遍历所有属性名为字符串的属性,按照生成时间排序;
(3)、最后遍历所有属性名为 Symbol 的属性,按照生成时间排序。
八、Object.keys(),Object.values(),Object.entries()
1、Object.keys(obj)
返回一个数组,是由参数对象自身的(不含继承)所有可遍历属性的键名:
/* Array 对象 */
let arr = ["a", "b", "c"];
console.log(Object.keys(arr));
// ['0', '1', '2']
/* Object 对象 */
let obj = { foo: "bar", baz: 42 },
keys = Object.keys(obj);
// CCAC: Chrome Console Auto Copy
copy(keys);
// ["foo","baz"]
/* 类数组 对象 */
let obj = { 0 : "a", 1 : "b", 2 : "c"};
console.log(Object.keys(obj));
// ['0', '1', '2']
// 类数组 对象, 随机 key 排序 (遵循上述的属性的遍历顺序)
let anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj));
// ['2', '7', '100']
/* getFoo 是个不可枚举的属性 */
var my_obj = Object.create(
{},
{ getFoo : { value : function () { return this.foo } } }
);
my_obj.foo = 1;
console.log(Object.keys(my_obj));
// ['foo']
2、Object.values(obj)
返回一个数组,是由参数对象自身的(不含继承)所有可遍历属性的键值:
var obj = { foo: "bar", baz: 42 };
console.log(Object.values(obj)); // ['bar', 42]
// 类数组对象
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.values(obj)); // ['a', 'b', 'c']
// 随机键值的类数组对象 (遵循上述的属性的遍历顺序)
var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.values(an_obj)); // ['b', 'c', 'a']
// getFoo 是不可枚举属性
var my_obj = Object.create({}, { getFoo: { value: function() { return this.foo; } } });
my_obj.foo = "bar";
console.log(Object.values(my_obj)); // ['bar']
// 参数是非对象会转变成对象
console.log(Object.values("foo")); // ['f', 'o', 'o']
Object.values() 会过滤属性名为 Symbol 值的属性:
Object.values({ [Symbol() ]: 123, "foo": "faa"}) // ["faa"]
3、Object.entries(obj)
返回一个数组,是由参数对象自身的(不含继承)所有可遍历属性的键值对数组:
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
// 类数组对象
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
// 随机键值的类数组对象 (遵循上述的属性的遍历顺序)
const anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]
// getFoo 是不可枚举属性
const myObj = Object.create({}, { getFoo: { value() { return this.foo; } } });
myObj.foo = 'bar';
console.log(Object.entries(myObj)); // [ ['foo', 'bar'] ]
// 参数是非对象会转变成对象
console.log(Object.entries('foo')); // [ ['0', 'f'], ['1', 'o'], ['2', 'o'] ]
// 优雅地遍历键值
const obj = { a: 5, b: 7, c: 9 };
for (const [key, value] of Object.entries(obj)) {
console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
}
Object.entries() 的另一个用处:将对象转为真正的Map 数据结构:
var obj = { foo: "bar", baz: 42 };
var map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }
Iterator 是 ES6 引入的一种新的遍历机制,迭代器有两个核心概念:
迭代器是带有特殊接口的对象。含有一个next()方法,调用返回一个包含两个属性的对象,分别是value和done,value表示当前位置的值,done表示是否迭代完,当为true的时候,调用next就无效了。
ES5中遍历集合通常都是 for循环,数组还有 forEach 方法,对象就是 for-in,ES6 中又添加了 Map 和 Set,而迭代器可以统一处理所有集合数据的方法。迭代器是一个接口,只要你这个数据结构暴露了一个iterator的接口,那就可以完成迭代。ES6创造了一种新的遍历命令for…of循环,Iterator接口主要供for…of消费。
const fruits = ['apple','coconut','mango','durian'];
//for循环数组,通过下标取得每一项的值
for (let i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}
//数组的forEach方法,相对for循环语法更简单
fruits.forEach(fruit => {
console.log(fruit);
})
//forEach有个问题是不能终止循环
fruits.forEach(fruit => {
if(fruit === 'mango' ){
break; //Illegal break statement
}
console.log(fruit);
})
//for...in循环,遍历数组对象的属性,MDN不推荐使用for...in遍历数组
//for...in循环会打印出非数字属性
const fruits = ['apple','coconut','mango','durian'];
fruits.fav = 'my favorite fruit';
for(let index in fruits){
console.log(fruits[index]); //...my favorite fruit
}
const fruits = ['apple','coconut','mango','durian'];
fruits.fav = 'my favorite fruit';
//ES6中的for...of循环,遍历属性值
for(let fruit of fruits){
console.log(fruit);
}
//支持终止循环,也不会遍历非数字属性
for(let fruit of fruits){
if(fruit === 'mango' ){
break;
}
console.log(fruit); //apple coconut durian
}
//arguments是个类数组对象,通常先要将其转换为数组才能遍历,但for...of可以直接遍历
function sum() {
let sum = 0
for(let num of arguments){
sum += num
}
console.log(sum); //15
}
sum(1,2,3,4,5)
//遍历字符串
let name = 'Asher';
for (let char of name){
console.log(char); //A s h e r
}
//遍历nodelists,效果如下图
- yoga
- boxing
- press
var set = new Set()
set.add(1);
set.add('1');
console.log(set)
console.log(set.size)
var set1 = new Set([1,2,3,3,3,3,3,2]) // 结果数组会去重
可以使用has()方法来判断某个值是否存在于Set中
使用delete()方法从Set中删除某个值,或者使用clear()方法从Set中删除所有值
set1.delete(1)
console.log(set1)
set1.clear()
console.log(set1)
set2.forEach(function(value,key){
console.log(value)
console.log(key)
})
var map = new Map()
var map = new Map()
console.log(map)
map.set('title','baidu')
map.set('year','2018');
console.log(map.get('year')) // 2018
console.log(map.size) // 2
map.delete('title');
map.clear();
console.log(map.has('year'));
map.forEach(function(value,key){
console.log(value) // 值 2018
console.log(key) // 健 year
})
都是用来存储数据用的,但是存储的数据格式不同
set 直接存储 任意类型数据
map 存储数据的时候,必须以key,value的形式,
set 使用forEach 遍历的时候,key和value值是一样的
而map 遍历的时候,key就是存进去的对象的key,value就是存在的值
https://www.jianshu.com/p/3bb77516fa7e
Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。
在JavaScript中,所有代码都是单线程执行的。这样导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现:
function callback() {
console.log('Done');
}
console.log('before setTimeout()');
setTimeout(callback, 1000); // 1秒钟后调用callback函数
console.log('after setTimeout()');
=
//执行结果
// before setTimeout()
// after setTimeout()
// (等待1秒后)
// Done
异步操作会在将来的某个时间点触发一个函数调用。
document.getElementById('#start').addEventListener('click', start, false);
function start() {
// 响应事件,进行相应的操作
}
// jquery on 监听
$('#start').on('click', start)
// 比较常见的有ajax
$.ajax('http://www.wyunfei.com/', {
success (res) {
// 这里可以监听res返回的数据做回调逻辑的处理
}
})
// 或者在页面加载完毕后回调
$(function() {
// 页面结构加载完成,做回调逻辑处理
})
//写法一
new Promise(
function (resolve, reject) {
// 一段耗时的异步操作
resolve('成功') // 数据处理完成
// reject('失败') // 数据处理出错
}
).then(
(res) => {console.log(res)}, // 成功
(err) => {console.log(err)} // 失败
)
//写法二
var p1 = new Promise(function (resolve, reject) {
// 一段耗时的异步操作
resolve('成功') // 数据处理完成
// reject('失败') // 数据处理出错
});
var p2 = p1.then(function (result) {
console.log('成功:' + result);
});
var p3 = p2.catch(function (reason) {
console.log('失败:' + reason);
});
参数:函数类型
resolve作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
promise有三个状态:
1、pending[待定]初始状态
2、resolve[实现]操作成功
3、rejected[被否决]操作失败
当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;
promise状态一经改变,不会再变。
Promise对象的状态改变,只有两种可能:
从pending变为fulfilled
从pending变为rejected。
这两种情况只要发生,状态就凝固了,不会再变了。
then 方法就是把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
而 Promise 的优势就在于这个链式调用。我们可以在 then 方法中继续写 Promise 对象并返回,然后继续调用 then 来进行回调操作。
可有两个参数,第一个是成功 resolve 调用的方法,第二个是失败 reject 调用的方法
then方法注册 当resolve(成功)/reject(失败)的回调函数
// onFulfilled 是用来接收promise成功的值
// onRejected 是用来接收promise失败的原因
promise.then(onFulfilled, onRejected);
then方法是异步执行的
const promise = new Promise((resolve, reject) => {
resolve('fulfilled'); // 状态由 pending => fulfilled
});
promise.then(result => { // onFulfilled
console.log(result); // 'fulfilled'
}, reason => { // onRejected 不会被调用
})
const promise = new Promise((resolve, reject) => {
reject('rejected'); // 状态由 pending => rejected
});
promise.then(result => { // onFulfilled 不会被调用
}, reason => { // onRejected
console.log(rejected); // 'rejected'
})
function buy(){
console.log("开始买笔");
var p = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("买了笔芯");
resolve("数学作业");
},1000);
});
return p;
}
//写作业
function work(data){
console.log("开始写作业:"+data);
var p = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("写完作业");
resolve("作业本");
},1000);
});
return p;
}
function out(data){
console.log("开始上交:"+data);
var p = new Promise(function(resolve,reject){
setTimeout(function(){
console.log("上交完毕");
resolve("得分:A");
},1000);
});
return p;
}
/* 不建议使用这种方式
buy().then(function(data){
return work(data);
}).then(function(data){
return out(data);
}).then(function(data){
console.log(data);
});*/
//推荐这种简化的写法
buy().then(work).then(out).then(function(data){
console.log(data);
});
// ajax函数将返回Promise对象:
function ajax(method, url, data) {
var request = new XMLHttpRequest();
return new Promise(function (resolve, reject) {
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
resolve(request.responseText);
} else {
reject(request.status);
}
}
};
request.open(method, url);
request.send(data);
});
}
var p = ajax('GET', '/api/categories');
p.then(function (text) { // 如果AJAX成功,获得响应内容
log.innerText = text;
}).catch(function (status) { // 如果AJAX失败,获得响应代码
log.innerText = 'ERROR: ' + status;
});
resolve 方法把 Promise 的状态置为完成态(Resolved),这时 then 方法就能捕捉到变化,并执行“成功”情况的回调。
而 reject 方法就是把 Promise 的状态置为已失败(Rejected),这时 then 方法执行“失败”情况的回调(then 方法的第二参数)
Promise 的 all 方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
只有全部为resolve才会调用 通常会用来处理 多个并行异步操作
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
const p2 = new Promise((resolve, reject) => {
resolve(2);
});
const p3 = new Promise((resolve, reject) => {
reject(3);
});
Promise.all([p1, p2, p3]).then(data => {
console.log(data); // [1, 2, 3] 结果顺序和promise实例数组顺序是一致的
}, err => {
console.log(err);
});
race()方法:
race 按字面解释,就是赛跑的意思。race 的用法与 all 一样,只不过 all 是等所有异步操作都执行完毕后才执行 then 回调。而 race 的话只要有一个异步操作执行完毕,就立刻执行 then 回调。
注意:其它没有执行完毕的异步操作仍然会继续执行,而不是停止。
Promise.race 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。
function timerPromisefy(delay) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(delay);
}, delay);
});
}
var startDate = Date.now();
Promise.race([
timerPromisefy(10),
timerPromisefy(20),
timerPromisefy(30)
]).then(function (values) {
console.log(values); // 10
});
race 使用场景很多。比如我们可以用 race 给某个异步请求设置超时时间,并且在超时后执行相应的操作。
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = 'xxxxxx';
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise.race([requestImg(), timeout()]).then(function(results){
console.log(results);
}).catch(function(reason){
console.log(reason);
});
//上面代码 requestImg 函数异步请求一张图片,timeout 函数是一个延时 5 秒的异步操作。我们将它们一起放在 race 中赛跑。
//如果 5 秒内图片请求成功那么便进入 then 方法,执行正常的流程。
//如果 5 秒钟图片还未成功返回,那么则进入 catch,报“图片请求超时”的信息。