ES6
相关介绍ES6
1995年的美国,有一家名为netscape
(网景)的公司打造了一款主要用于check验证的脚本语言,而恰在此时,Sun
公司的java
语言火的一塌糊涂,netscape
公司为蹭其热度,便将该脚本语言命名为 JavaScript
。不料 JavaScript
居然被越来越多的人使用,后效仿大秦的货币统一政策将其提交给国际标准组织ECMA
。
该组织发布的标准被称做ECMAScript
。 2015年6月发布的版本称为ECMAScript2015
,简称ES6
。从ES6
开始,该组织每年会发布一个版本,版本号比年份最后一位大1,至今最新版本为ES12
。
ES6 既是一个历史名词,也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了 ES2015、ES2016、ES2017 等等,而 ES2015 则是正式名称,特指该年发布的正式版本的语言标准
语法糖:又称糖衣语法,指不改变语言的本质功能,只是使编程变的更加简洁。而本课程后续所学习的很多ES6
的新特性大多也都是由一粒粒语法糖所构成,所以ES6
的一些新特性并不是真的“新”,只是试图简化语法而已。简言之:ES6
是一大盒语法糖,解决了以前ES5
很多难受的地方。
Babel
:Babel
是一个广泛使用的ES6
转码器,可以将ES6
代码转为ES5
代码,从而在现有环境执行。这意味着,你可以用ES6
的方式编写程序,又不用担心现有环境是否支持。相信未来浏览器会逐渐对其提供原生的支持。
ES6
ES5
语言先天不足,通过学习ES6
可以将其很多丑陋的部分进行隐藏。vue
、reac
t以及小程序做好充足准备。ES6
,它是前端发展的趋势,就业必备技能之一。ES5 除了正常运行模式(又称为混杂模式),还添加了第二种运行模式:“严格模式”(strict mode)。
严格模式顾名思义,就
是使 JavaScript 在更严格的语法条件下运行。
在全局或函数的第一条语句定义为: 'use strict'
如果浏览器不支持,只解析为一条简单的语句, 没有任何副作用
var obj = {
name: "张阳",
age:18,
like:[
"早上跑步",
"晚上复习",
"人帅有腹肌"
]
}
遍历obj 对象中的键 (key)
遍历obj 对象的值 (value)
Object.create 方法可以以**指定对象为原型
创建新的对象,同时可以为新的对象
**设置属性, 并对属性进行描述
Object.create(proto, propertiesObject)
proto 指定的对象为原型
propertiesObject 设置新的属性
o2 = Object.create({}, {
p: {
value: 42,
writable: true,
enumerable: true,
configurable: true
}
});
value : 指定值
writable : 标识当前属性值是否是可修改的, 默认为 false
configurable:标识当前属性是否可以被删除 默认为 false
enumerable:标识当前属性是否能用for in 枚举 默认为 false
get: 当获取当前属性时的回调函数
set: 当设置当前属性时
Object.defineProperty(obj,“key”,{descriptors}) 只能写一个属性
get,set与value,writable不能同时存在
Object.defineProperty(obj,"age",{
value:18,
writable:true, //可编辑,默认为false
configurable:true,//可删除,默认为false
enumerable:true, //可遍历,默认为false
});
Object.defineProperty(obj,'name',{
get:function(){
return this._name || "今麦郎";
},
set:function(v){
this._name = v;
}
})
defineProperty复数形式
直接在一个对象上定义新的属性或修改现有属性,并返回该对象。
Object.defineProperties(object, descriptors)
object 要操作的对象
descriptors 属性描述
var obj2 = Object.defineProperties(obj,{
name:{
value:'老王'
},
age:{
value:18
},
like:{
get:function(){
// return _like || [];
// 如果不写return
// 默认return undefined;
},
set:function(v){
_like = v;
}
}
});
call 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数
apply 方法调用一个具有给定 this 值的函数,以及作为一个数组(或类似数组对象)提供的参数
bind 同 call 相似,不过该方法会返回一个新的函数,而不会立即执行
ES5
没有块级作用域在ES5中,JS 的作用域分为全局作用域和局部作用域。通常是用函数区分的,函数内部属于局部作用域。
ES5只有全局作用域和函数作用域,没有块级作用域,这带来很多不合理的场景
ES6
新增了let
命令,用来声明变量。它的用法类似于var
,但是所声明的变量,只在let
命令所在的代码块内有效,也就是增加了块级作用域。
使用块级作用域(let定义的变量属于块级作用域) 防止全局变量污染
块级作用域可以任意嵌套
for循环的计数器,就很合适使用let命令
你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算
for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
练习1
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6]();
* 练习2
```js
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6]();
let命令不存在变量提升
和var不同的还有,let命令不存在变量提升,所以声明前调用变量,都会报错,这就涉及到一个概念——暂时性死区。
暂时性死区即:区块中存在let
或const
命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
不允许重复声明
let 只能声明一次而var 可以声明多次。
块级作用域的出现,实际上使得获得广泛应用的匿名立即执行函数表达式不再必要了
let 是在代码块内有效,var 是在全局范围内有效
不影响作用域链
let与var都拥有作用域链。
作用域链: 如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。
使用var定义的全局变量相当于直接挂载在window对象上, 而let不会。
const
关键字注意:
变量:数据可以变化。在执行过程当中,有一些数据会使用多次,根据条件会变化,一般定义为变量。
常量:不会变化的数据,有些时候有的数据是不允许修改的,所以需要定义常量。
const
声明一个只读变量,声明之后不允许改变。意味着,一旦声明必须初始化,否则会报错。
值不允许修改
const 其实保证的不是变量的值不变,而是保证变量指向的内存地址不允许改动。所以 使用 const 定义的对象或者数组,其实是可变的。
const
只在声明所在的块级作用域内有效。(与let相同)
const
命令声明的常量也是不会提升(与let相同)
const
不可重复声明(与let相同)
不再是顶层全局对象的属性(与let相同)
let命令、const
命令、class命令声明的全局变量,不属于顶层对象的属性。也就是说,从 ES6
开始,全局变量将逐步与顶层对象的属性脱钩。
const使用的两点建议:
1、被多次使用且不允许更改的数据建议通过const定义;
2、项目全局常量建议大写,单词之间用-分隔;
3、如果不清楚要使用let还是const
,那么就用const
。如果后面发生值的改变,那么再将const
改成let.
4、以后不允许使用var
函数声明一般常用的是两种,一种是function声明,一种是函数表达式。
建议函数在顶层作用域和函数作用域之中声明,尽量避免在块级作用域声明。( "use strict"下报错)
如果确实需要,也应该写成函数表达式,而不是函数声明语句。
解构 → 解析,重构。
解构的目标:数组以及对象。
ES
6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。
解构赋值本质就是赋值:把结构解散重构然后赋值。
解构赋值是对赋值运算符=的一种扩展。
在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段获取。
在ES5
中,开发者们为了从对象和数组中获取特定数据并赋值给变量,编写了许多看起来同质化的代码 ;
解构的目标 = 解构源;(目标指的是定义的常量或变量,解析源一般指的是数组或对象)
解构目标:定义的常量或变量
解构源:待解构的数组或对象
对象的语法形式是在一个赋值操作符= 右边放置一个对象字面量
顺序不用一一对应
= 右侧可以是一个常或变量
嵌套对象解构
解构嵌套对象仍然与对象字面量的语法相似,可以将对象拆解以获取想要的信息
可忽略部分解构源的属性
剩余运算符
不完全解构:变量名称在对象中不存在
使用解构赋值表达式时,如果指定的变量名称在对象中不存在,那么这个变量会被赋值为undefined
解构默认值(常用)
当指定的属性不存在时,可以定义一个默认值:在属性名称后添加一个等号(=)和相应的默认值即可
如果希望使用不同命名的局部变量来存储对象属性的值,ES6
中的一个扩展语法可以满足需求,这个语法与完整的对象字面量属性初始化程序的很像。
* 函数传参数
解构赋值表达式的值与表达式右侧(也就是=右侧)的值相等,如此一来,在任何可以使用值的地方都可以使用解构赋值表达式
基本使用
与对象解构的语法相比,数组解构就简单多了,它使用的是数组字面量,且解构操作全部在数组内完成,而不是像对象字面量语法一样使用对象的命名属性 。
忽略元素
在解构模式中,可以直接省略元素,只为感兴趣的元素提供变量名 。
* 变量交换
数组解构语法还有一个独特的用例:交换两个变量的值。在排序算法中,值交换是一个非常常见的操作,如果要在ES5
中交换两个变量的值,则须引入第三个临时变量
* 添加默认值
也可以在数组解构赋值表达式中为数组中的任意位置添加默认值,当指定位置的属性不存在或其值为undefined时使用默认值
嵌套数组解构与嵌套对象解构的语法类似,在原有的数组模式中插入另一个数组模式,即可将解构过程深入到下一个层级
函数具有不定参数,而在数组解构语法中有一个相似的概念——不定元素。在数组中,可以通过…语法将数组中的其余元素赋值给一个特定的变量
在ES5
中,开发者们经常使用concat()
方法来克隆数组
混合使用对象解构和数组解构来创建更多复杂的表达式,可以从任何混杂着对象和数组的数据解构中提取想要的信息
面试题:
//复杂解构
let wangfei = {
name: '王菲',
age: 18,
songs: ['红豆', '流年', '暧昧', '传奇'],
history: [
{name: '窦唯'},
{name: '李亚鹏'},
{name: '谢霆锋'}
]
};
// console.log(userName,age,one,two,three,four,name,name2,name3);// 王菲 18 红豆 流年 暧昧 传奇 窦唯 李亚鹏 谢霆锋
解构赋值可以应用在函数参数的传递过程中。
如果调用函数时不传入参数或设置默认值,被解构的参数会导致程序抛出错误
可以为解构参数指定默认值
在ES6
中, 三个点(…) 有2个含义。分别表示扩展运算符(spread运算符) 和 剩余运算符(spread运算符)
...变量名
),rest运算符用于获取函数调用时传入的参数。length
属性,不包括 rest 参数
Array.from()
方法对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。不影响原数组
Array.from(arrayLike, mapFn, thisArg)
mapFn
可选 如果指定了该参数,新数组中的每个元素会执行该回调函数。让你可以在最后生成的数组上再执行一次 map
方法后再返回。thisArg
可选 可选参数,执行回调函数 mapFn
时 this
对象。let arr = [1,2,3];
let array = Array.from(arr);
console.log(arr === array); // output: false
console.log(Array.from([1, 2, 3], x => x + x)); // output: [2,4,6]
Array.of()
方法通过可变数量的参数创建一个新的Array
实例,而不考虑参数的数量或类型。
Array.of(element0, element1, /* … ,*/ elementN)
Array.of(7); // [7] Array.of 与 Array() 的区别
Array(7); // array of 7 empty slots 一个参数表示长度为7,但是数组为空,7个空元素
copyWithin()
方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。
const array1 = ['a', 'b', 'c', 'd', 'e'];
console.log(array1.copyWithin(0, 3, 4)); // expected output: Array ["d", "b", "c", "d", "e"]
console.log(array1.copyWithin(1, 3)); // expected output: Array ["d", "d", "e", "d", "e"]
copyWithin(target, start, end)
fill()
方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。
const array1 = [1, 2, 3, 4];
console.log(array1.fill(0, 2, 4)); // expected output: [1, 2, 0, 0]
console.log(array1.fill(5, 1)); // expected output: [1, 5, 5, 5]
console.log(array1.fill(6)); // expected output: [6, 6, 6, 6]
fill(value, start, end) value:填充值 start:起始索引,默认为0 end:终止索引,默认为数组的结尾
**
Object.entries()
**方法返回一个给定对象自身可枚举属性的键值对数组 Object.entries(obj)
let arr = ['one','two','three','four','five'];
for(let [key,val] of arr.entries()) { // 可能由于包装类
console.log(key,val); // output: 1 'one' 2 'two' 3 'three' 4 'four' 5 'five'
}
Object.keys()
方法会返回一个由一个给定对象的自身可枚举属性组成的数组 , 有包装类,数组实例可以使用。
for (let key of arr.keys()) {
console.log(key);
}
Object.values()
方法会返回一个由一个给定对象的自身可枚举属性组成的数组 , 有包装类,数组实例可以使用。
for (let key of arr.values()) {
console.log(key);
}
find()
方法返回数组中满足提供的测试函数的第一个元素的值。否则返回undefined
。
let arr = ['one', 'two', 'three', 'four'];
let result = arr.find(item => item === 'four');
console.log(result); // output: four
find(function(item, index, array) { /* … */ }, thisArg) item: 当前元素 index:当前索引 array:原数组 thisArg: 执行回调时用作
this
的对象。
find()
方法返回数组中满足提供的测试函数的第一个元素的索引。没有找到相应的元素返回 -1
let arr = ['one', 'two', 'three', 'four'];
let result = arr.findIndex(item => item === 'four');
console.log(result); // output: 3
findIndex(function(item, index, array) { /* … */ }, thisArg) item: 当前元素 index:当前索引 array:原数组 thisArg: 执行回调时用作
this
的对象。
includes()
方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true
,否则返回false
。
let arr = [1, 1, 2, 2, 3, 3, 4, 4, 5, 5];
let array = [];
arr.forEach((item,index) => array.includes(item) ? '' : array[array.length] = item)
console.log(array); // [1,2,3,4,5]
includes ( searchElement, fromIndex ) searchElement: 需要查找的元素值。 fromIndex: 从什么位置开始查找
flat()
方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。对原数组没有影响。
const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat()); // expected output: [0, 1, 2, 3, 4]
const arr2 = [0, 1, 2, [[[3, 4]]]];
console.log(arr2.flat(2)); // expected output: [0, 1, 2, [3, 4]]
flat()
默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将flat()
方法的参数写成一个整数,表示想要拉平的层数,默认为1Infinity
关键字作为参数ES5
默认参数ES6
之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
ES6
默认参数ES6
允许为函数的参数设置默认值,即直接写在参数定义的后面。
ES6
允许使用“箭头”(=>
)定义函数。
箭头函数分为以下几种情况
只有一个参数 并函数体是一句话的时候
没有参数或者多个参数的时候,参数的括号不能省略
当函数体不是一句话的时候,花括号 不可以省略
关于this
箭头函数没有自己的this,箭头函数内部的this并不是调用时候指向的对象,而是定义时指向的对象
箭头函数不能用于构造函数,也就是不能使用new关键字调用
箭头函数没有arguments对象
ES6
允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
JavaScript 定义对象的属性,有两种方法:点运算符和中括号运算符
但是,如果使用字面量方式定义对象(使用大括号),在 ES5
中只能使用标识符,而不能使用变量定义属性。
也就是说在ES5
中 key/value key是固定不变的,在ES6
中,支持属性表达式,支持key发生变化
Object.is(v1,v2)
Object.is()
方法判断两个值是否为同一个值
Object.is(NaN,NaN); // true 解决了NaN不等于NaN的问题
console.log(NaN === NaN); // false
Object.is(value1, value2); 被比较的第一个值。 value2 被比较的第二个值
Object.assign()
Object.assign()
方法将所有可枚举的自有属性从一个或多个源对象复制到目标对象,返回修改后的对象。
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };
const returnedTarget = Object.assign(target, source);
console.log(target); // output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget === target); // output: true
Object.assign(target, …sources) target 拷贝后返回的新对象 sources 源对象,包含将被合并的属性。
Object.keys()
方法会返回一个由一个给定对象的自身可枚举属性组成的数组 , 有包装类,数组实例可以使用。
let obj = {
name:'ling',
age:16
}
console.log(Object.keys(obj)) // output: [name,age]
Object.values()
方法会返回一个由一个给定对象的自身可枚举属性组成的数组 , 有包装类,数组实例可以使用。
let obj = {
name:'ling',
age:16
}
console.log(Object.values(obj)) // output: ['ling',16]
在Math中提供了 pow的方法 用来计算一个值的n次方
ES11
提出了新的方法求一个值的n次方 那就是 ** 操作符
ES6
提供了二进制和八进制数值的新的写法,分别用前缀0b
和0o
表示。
Math.trunc()
方法会将数字的小数部分去掉,只保留整数部分Math.sign()
判断一个数字的正数还是负数 还是0 或者是NaN
Math.sqrt()
平方根Math.cbrt()
立方根Math.hypot()
求所有参数平方和的平方根Number.isFinite(i)
: 用来检查一个数值是否为有限Number.isNaN(i)
: 判断是否是NaN
Number.isInteger(i)
: 判断是否是整数Number.parseInt(str)
: 将字符串转换为对应的数值ES5
的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法,新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是ES6
引入Symbol
的原因。
ES6
引入了一种新的原始数据类型Symbol
,表示独一无二的值。它是 JavaScript 语言的第七种数据类型
Symbol 值通过Symbol
函数生成。
这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突
> 使用 `Symbol.for`("尚硅谷") 通过相同参数设置相同值
Symbol中传入的字符串没有任何意义,只是用来描述Symbol
Symbol不能使用New调用
类型转换的时候,不能转数字
for in 不能遍历出来,可以使用Object.getOwnPropertySymbols
方法来拿;
BigInt
Infinity
。BigInt
(大整数),来解决这个问题。BigInt
只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。BigInt
类型的数据必须添加后缀n
。
BigInt
与普通整数是两种值,它们之间并不全等。typeof
运算符对于 BigIn
t 类型的数据返回bigint
。
Bigint
类型与Number类型互相转换ES6
提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set
本身是一个构造函数,用来生成 Set 数据结构。
Set
函数可以接受一个数组(或者具有 iterable
接口的其他数据结构)作为参数,用来初始化
利用set对象,实现数组去重
Set
的成员keys()
:返回键名的遍历器,由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys
方法和values
方法的行为完全一致。values()
:返回键值的遍历器entries()
:返回键值对的遍历器forEach()
:使用回调函数遍历每个成员 - 求两数组去重后的交集
- 求两数组去重后的并集
- 求两数组去重后的差集
JavaScript 的对象(Object),本质上是键值对的集合,但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
ES6
提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。size
属性返回 Map 结构的成员总数。set
方法设置键名key
对应的键值为value
,然后返回整个 Map 结构。如果key
已经有值,则键值会被更新,否则就新生成该键。set
方法返回的是当前的Map
对象,因此可以采用链式写法。get
方法读取key
对应的键值,如果找不到key
,返回undefined
。has
方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。delete
方法删除某个键,返回true
。如果删除失败,返回false
。clear
方法清除所有成员,没有返回值。keys()
:返回键名的遍历器。values()
:返回键值的遍历器。entries()
:返回所有成员的遍历器。forEach()
:遍历 Map 的所有成员。Array
)和对象(Object
),ES6 又添加了Map
和Set
。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map
,Map
的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。ES6
创造了一种新的遍历命令for...of
循环,Iterator 接口主要供for...of
消费。在ES6
中,只要一种数据结构具有了Symbol.iterator
属性,那么就认为是可以迭代的
在ES6
中,很多数据结构都部署了iterator接口(Array,set,Map,string)
应用场景:
扩展运算符使用的时候页默认调用iterator接口
对象是没有部署Iterator接口
案例:将对象中的两个数组使用for…of进行遍历