ES6语法补充
一、let/var
- 首先我们可以将let看成更加完美的var(因为let拥有块级作用域)
- 注意函数也是一个作用域(闭包能够解决作用域问题的原因,具体参考1.2)
- 在ES6之前因为if和for都没有块级作用域的概念,所以在很多时候,我们都必须借助于function的作用域来解决引用外面变量的问题。
- 但在ES6中加入了let,let是有if和for的块级作用域的。
1.1 什么是作用域
变量作用域:变量在什么范围内是可用的(参考java的private,public,default,protected访问控制修饰符)
1.2 ES5没有块级作用域引起的问题
第一种场景:if
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
var func
if(true){
var name = 'xiong';
func = function(){
console.log(name)
}
}
name = 'kobe' //如果在执行下面func()之前,name的值产生了改变,那么所打印出来的name也将随之改变,这样就造成了覆盖的情况
func() //这样可以打印出来上面所定义的name
</script>
</body>
</html>
第二种场景:for
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<!--先写五个按钮,想法是点击哪一个就打印哪一个被点击的提示,但是事实是不管点击哪一个,打印出来的都是第5个-->
<script>
var btns = document.getElementsByTagName('button')
for(var i = 1;i<btns.length;i++) {
btns[i].addEventListener('click', function () {
console.log('第' + (i+1) + '个按钮被点击了')
})
}
/*因为单击相应的那个函数可以访问到外面的i 当他访问的时候 函数内的i=0 但是他没有块级作用域 改为了外面的i 外面的i已经循环了5次,所以函数内的i也改为了5,所以,并不能做到点击那个那个的索引值被打印出来*/
</script>
</body>
</html>
为了解决这种问题,在ES5中,通常采用闭包的方式(函数本身就是一个作用域)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<script>
var btns = document.getElementsByTagName('button')
for(var i = 0;i<btns.length;i++) {
/*以下就采用了闭包,也就是运行到这里需要立即执行
函数是一个作用域,所以外面如果想改掉这个i是改不掉的 你改循环里面的i的时候 对立即执行中的i不会产生任何影响*/
(function (i){
btns[i].addEventListener('click', function () {
console.log('第' + (i+1) + '个按钮被点击了')
})
})(i)
}
</script>
</body>
</html>
1.3 应用let解决上面的问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<script>
const btns = document.getElementsByTagName('button')
for(let i = 0;i<btns.length;i++) {
/*应用let之后,每个i都拥有属于自己的块级作用域,也就不是共享的i,所以i的值也就是每次循环自己使用,也就不存在不管点击那个按钮都是5的情况发生*/
btns[i].addEventListener('click', function () {
console.log('第' + (i+1) + '个按钮被点击了')
})
}
</script>
</body>
</html>
1.4 总结
let和var的作用域区别主要针对于块级作用域的拥有上面。
var不存在块级作用域,就导致了if和for存在引用外面变量时的问题,比如上述的情况,当for循环时,var定义的i由于是不存在独立的作用域,也就是每一次循环都共享了这个i的值,而i在最外层循环达到了上限值5(5个按钮的长度,btns.length),所以不管点击哪一个按钮,最终打印出来的都是第5个按钮。
当使用let之后,也就是让每一次循环都拥有了属于自己的作用域,在这个作用域中,这个i的值只属于本次循环,就不存在共享i的值导致冲突,所以就实现了点击哪一个按钮就打印点击了那一个按钮的信息。
二、const的使用
当我们修饰的标识符不会被再次赋值时,就可以使用const来保证数据的安全性。
建议:在ES6开发中,优先使用const,只有需要改变某一个标识符的时候再更改为let。
2.1 const的使用注意事项
- 注意事项一
const a = 20;
a = 30; // 错误使用方法:const修饰的标识符不能被修改
Attempt to assign to const or readonly variable(尝试分配给常量或只读变量),意思是不能给常量赋值
- 注意事项二
const name; //错误使用方法:const修饰的标识符必须赋值
‘const’ variable without initializer is not allowed(不允许使用没有初始值设定项的“const”变量)
- 注意事项三
常量的含义是:指向的对象不能修改,但是可以改变对象内部的属性。
const obj = {
name: 'xiong',
age: 20,
height:1.87
}
obj = ; //错误的使用方法
obj.name = 'x'; //这样才是正确方法
2.2 总结
const所修饰的标识符之所以不能够修改,是因为内存指向的问题,在C语言中我们称为指针,既当我们使用const name = 'kobe'
时,此时的name
所存放的就是name = 'kobe'
的内存地址,比如说0x100,这个地址由于是const所修饰的,就固定不再能更改,所以此时的name无法再次修改。
然后当使用const修饰对象时,如:
const obj = {
name: 'xiong',
age: 20,
height:1.87
}
此时obj所存放的就是后面各种属性的内存地址,比如说是0x111,这个地址是不能改变的,所以我们后面使用obj = {}
是会报错的,但是如果我只是修改obj.name = 'kobe'
,实质上来说我没有修改0x111这个内存地址,我只是修改了这个对象内部属性的一个值,所以可以执行。
换种方式说就是,const name = 'kobe'
就意味着name = 'kobe'
是你的儿子,如果你想更改name,就表示你想换儿子,但是只有你自己的儿子才属于你,别人的儿子你没办法换,所以const修饰的标识符是不可修改的。
而const修饰的对象,也相当于是儿子,如下:
const obj = {
name: 'xiong',
age: 20,
height:1.87
}
里面的name,age,height就相当于儿子的名字,年龄,身高,虽然说你的儿子不能更换,不能修改,但是你可以给你的儿子改名,你的儿子也会长高,年龄每年也在增长,这些属性的值都可以改变。
三、对象字面量增强写法
3.1 什么叫对象字面量
const obj = {
name = 'xiong',
age = 20,
message = 'hello world'
}
如上,等号右边大括号里面的所有内容就叫做对象字面量(所修饰的内容是一个对象),一般来说,给变量赋值时,等号右边都可以认为是字面量。
字面量分为字符串字面量(string literal)、数组字面量(array literal)和对象字面量(object literal),另外还有函数字面量(function literal)。
比如:
var test = "hello world!";
const movies = ['星际穿越', '大话西游', '少年派', '盗梦空间'];
var fn = function (x) {
alert(x)
}
其中"hello world!"就是字符串字面量,[‘星际穿越’, ‘大话西游’, ‘少年派’, ‘盗梦空间’]就是数组字面量,而函数字面量就是:
function (x) {
alert(x)
}
3.2 属性的增强写法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
const name = 'kobe';
const age = 20;
const height = 1.98;
/* //ES5的写法:
const obj = {
name: name,
age: age,
height: height
}*/
/*ES6的写法: 在ES6中,如果像下面这样写,它会自动将name的变量名设置为key,name属性的值设置为value,就不用像ES5中那么繁琐的去进行name: name*/
const obj = {
name,
age,
height
}
console.log(obj);
</script>
</body>
</html>
3.3 函数的增强写法
//ES5的写法:
const obj = {
run: function (){
}
}
//ES6的写法:
const obj = {
run() {
}
}
如上代码可知,ES6较ES5而言,可以省略一部分内容。