ECMAScript 6.0,是JavaScript的下一个标准版本,ECMAScript是JavaScript的语言规范(标准),JavaScript是ECMAScript的实现和扩展,ECMAScript 6.0发布于2015年6月,成为国际标准。
1、块级作用域
这个是ES6中新增的一个作用域,之前的版本中只有全局作用域和函数作用域,那么什么是块级作用域呢? {}
中间的内容就是属于块级作用域,比如我们常见的for
,if
,while
等语句,首先在es6的块级作用域中,没有内层变量覆盖外层变量的现象:
var a = "外层变量"
function test1() {
console.log(a) // 外层变量
}
// var会变量提升,提升到函数的最上面,就相当于现在函数中声明了a
// 然后在if中初始化了a,需要注意的是,变量提升只提升声明,不提升初始化
function test2() {
console.log(a) // undefine
if (false) {
var a = "内层变量"
}
console.log(a) // 输出 undefine(若上面的if条件是true的话则输出 内层变量)
}
// let不会变量提升,所以函数内的a只在if中有效果
function test3() {
console.log(a) // 外层变量
if (false) {
let a = "内层变量"
}
console.log(a) // 外层变量
}
而且不存在变量泄漏为全局变量的现象:
if (true) {
var a = "内层变量"
}
console.log(a) // 内层变量
if (true) {
let b = "内层变量"
}
console.log(b) // Error: b is not defined
此外,还允许在块级作用域内声明函数,而且也存在函数变量提升的现象:
if (true) {
test() // test
function test() {
console("test")
}
}
内层作用域声明的同名函数不干扰外层作用域的同名函数:
if (true) {
test() // 外层函数
if (true) {
test() // 内层函数
function test() {
console("内层函数")
}
}
function test() {
console("外层函数")
}
test() // 外层函数
}
2、var、let和const
说完了块级作用域,我们再来看这几个定义变量的关键字,首先let和const是es6才出的新的关键字,这几个关键字的主要区别就是在不同作用域内是否可以访问,具体来看一下代码:
for (var i = 0; i < 10; i++) {
console.log(i) // 0,1,2...9
}
console.log(i) // 10
for (let i = 0; i < 10; i++) {
console.log(i) // 0,1,2...9
}
console.log(i) // Error:i is not defined
这就是var和let的区别,var可以跨块级作用域访问,而let没有这样的功能,只能在当前块级作用域内使用,但是两者均不能垮函数作用域访问,而const一般用来定义常量,使用时必须初始化,和let一样只能在当前块级作用域内使用,并且不能修改(一般情况下),原则上用const定义的常量是不能修改的,但是如果用const定义一个对象,这时候对象里面的内容是可以修改:
const user = {
name: "24K纯帅",
sex: 0
}
user.sex = 1
console.log(user.sex) // 输出1
// 下面会报错
user = {
name: "小明",
sex: 1
}
这是因为对象是引用类型,并且user中仅保存对象的指针,而const仅保证指针不发生改变,修改对象属性是不会改变对象的指针,所以上述操作修改性别是允许的;后面重新给user对象赋值,这就直接改变了对象的指针,所以会报错。
3、模板字符串
ES6之前传统的JS模版输出采用 “+” 连接,ES6开始支持反引号标识(`),既可以当做普通字符串使用,也可以在字符串中嵌套变量来使用:
var a = `I am 24K纯帅`
var b = `Hi! ${a}`
4、变量的解构赋值
先来看一个简单的例子,假如我有一个对象,我要取出对象中的值,最简单的方法就是:
var data = { name: '24K纯帅', sex: '1', province: '浙江', city: '杭州' }
var name = data.name
var sex = data. sex
var province = data.province
var city = data.city
在ES6之前,我们会采用上面的方法将对象中的值取出,这样看来是比较繁琐的,ES6中新增了变量的解构赋值,使用这种方式能够很快捷的取出我们想要的值:
// 全部取出
const { name, sex, province, city } = { name: '24K纯帅', sex: '1', province: '浙江', city: '杭州' }
// 按需取出
const { name, sex, city } = { name: '24K纯帅', sex: '1', province: '浙江', city: '杭州' }
就是这么简单,不仅仅是对象,数组也是同样适用,需要注意的是进行对象解构赋值时需要将变量名与之对应上,这样才能取到你要的值,数组是根据下标来取的,如果超出数组长度的话不会报错,取出来的值就是undefine
5、函数
(1)、参数默认值
在ES6之前,函数不能直接指定函数参数默认值,只能通过在函数内进行判断的方式来给默认值,比如:
function log(x, y) {
y = y || '默认值'
console.log(x, y)
}
一个很简单的打印的例子,当调用者传入一个x时,此时y是有一个默认值的,但是当用户传的y是一个空字符串时,此时y也会变成默认值,这样就存在一个弊端;在ES6之后,函数提供参数可以设置默认参数,直接在函数参数声明的地方即可:
function log(x, y = '默认值') {
console.log(x, y)
}
log('1', undefined)
这样就保证你传入的y如果不是undefined的话,那么就会使用传入的y,如果传入的y是undefined,那么也会使用默认值;这样设置默认参数的第二个好处是,调用者一眼就可以看出哪些参数是可以不用传省略的。
(2)、箭头函数
在ES6中,允许使用 "=>" 箭头来定义函数,比平常定义函数要简洁的多,比如:
// 一个参数
var f = function(v) {
return v;
}
var f = v => v;
// 多个参数
var add = function(a, b) {
return a + b;
}
var add = (a, b) => a + b;
// 无参数
var ff = function() {
return 666;
}
var ff = () => 666;
如果你把箭头函数想成是简化函数的一个操作,那么就大错特错了,箭头函数的一大主要的特点就是this作用域,我们来看一个例子:
var person1 = {
name: '24K纯帅',
getName:function() {
document.body.addEventListener("click", function(){
return this.name
}, false)
}
}
var person2 = {
name: '24K纯帅',
getName:function() {
var That = this
document.body.addEventListener("click", function(){
return That.name
}, false)
}
}
var person3 = {
name: '24K纯帅',
getName:function() {
document.body.addEventListener("click", () => {
return this.name
}, false)
}
}
person1.getName() // 会报错,找不到this.name
person2.getName() //正常运行
person3.getName() //正常运行
因为作用域的缘故,第一个对象中this和getName的this绑定不是同一个,所以会调用不了;然而在第二个对象中用That将this赋值了,所以That和getName的this绑定是同一个,所以可以调用到;所以这个地方就体现了箭头函数的作用了,使用箭头函数可以替代var That = this
的赋值操作,让调用生效。
6、数组的扩展
比较常用的就是扩展运算符(...),该运算符通常用来复制/合并数组,怎么个复制合并呢:
复制数组:
var a1 = [1, 2]
var a2 = [...a1]
这个时候如果改变a2[0] = 2,并不会改变a1的值
合并数组:
var a1 = [1, 2]
var a2 = [3, 4]
var a3 = [...a1, ...a2] // 1, 2, 3, 4
7、==、===、Object.is()的区别
两等号是相等运算符,它会将比较的变量自动转换数据类型;三等号是严格相等运算符,Object.is()是ES6新出的,它和三等号运算符功能差不多,但是有些细微的差别;搞懂这几个的区别,从几个简单的例子开始:
'1' == 1 // true
'1' === 1 // false
[1] == 1 // true
[1] === 1 // false
{1} == 1 // 会报错
当使用双等号和数字进行比较时,会自动将非数字的一方转成数字(前提是可以转成数字的,对象{}就不能和数字进行比较)类型再来进行比较,三等号不会转换数据类型,直接比较,所以三等号比两等号快;而Object.is的比较和三等差不多,在一些细微的地方有点差别:
0 === -0 // true
Object.is(0, -0) // false
NaN === NaN // false
Object.is(NaN, NaN) // true
这三种比较方式都有各自不同的步骤:
(1)、===
对于x === y,该运算符的比较步骤如下:
1、如果x的类型和y的类型不一样,返回false;
2、如果x的类型是数字,那么:
(1)、如果x是NaN,返回false;
(2)、如果y是NaN,返回false;
(3)、如果x和y是同一个数字值,返回true;
(4)、如果x是+0,y是-0,返回true;
(5)、如果x是-0,y是+0,返回true;
3、返回SameValueNonNumber(x, y)的结果。
SameValueNonNumber(x, y)抽象操作比较两个非数字并且同类型的x和y是否相等,比较步骤如下:
1、如果x的类型是null或者undefined,返回true;
2、如果x是字符串类型,
(1)、如果x和y是完全相同的字符编码序列,返回true,否则返回false;
3、如果x是布尔类型,
(1)、如果x和y同为true或者false,返回true,否则返回false;
4、如果x是symbol类型,
(1)、如果x和y是相同的符号值,返回true,否则返回false;
5、如果x和y是同一个对象值,返回true,否则返回false。
(2)、Object.is()
对于Object.is(x, y),会使用抽象操作SameValue(x, y)进行比较,该抽象操作的步骤如下:
1、如果x的类型和y的类型不一样,返回false;
2、如果x的类型是数字,那么:
(1)、如果x和y都是NaN,返回true;
(2)、如果x是+0,y是-0,返回false;
(3)、如果x是-0,y是+0,返回false;
(4)、如果x和y是同一个数字值,返回true;
3、返回SameValueNonNumber(x, y)的结果。
(3)、==
对于x == y,该运算符的比较步骤如下:
1、如果x和y的类型相同,返回x === y的结果;
2、如果x是null,y是undefined,返回true;
3、如果x是undefined,y是null,返回true;
4、如果x的类型是数字,y的类型是字符串,返回x == ToNumber(y)的结果;
5、如果x的类型是字符串,y的类型是数字,返回ToNumber(x) == y的结果;
6、如果x的类型是布尔类型,返回ToNumber(x) == y的结果;
7、如果y的类型是布尔类型,返回x == ToNumber(y)的结果;
8、如果x的类型是字符串、数字或者Symbol中的一种,y的类型是对象,返回x == ToPrimitive(y)的结果;
9、如果x的类型是对象,y的类型是字符串、数字或者Symbol中的一种,返回ToPrimitive(x) == y的结果;
上面用到了方法ToNumber,ToNumber(x)的步骤如下:
1、如果x的类型是Undefined,返回NaN;
2、如果x的类型是Null,返回+0;
3、如果x的类型是布尔类型,x为true返回1,false返回+0;
4、如果x的类型是数字,返回x;
5、如果x的类型是字符串,参考字符串转化为数字,本文不详细介绍这块内容;
6、如果x的类型是Symbol,返回NaN;
7、如果x的类型是对象,
(1)、让primValue的值是ToPrimitive(x, hint Number);
(2)、返回ToNumber(primValue)的结果;
参考
JavaScript中的==,===和Object.is()