ES6(一)
一、let和const声明变量
关于ES6的内容如果遇到不会的可以查阅文档:阮一峰ES6
1.变量不能重复声明
下面这样会报错,而var就不会
let a = 1;
let a = 2;
2.块儿级作用域
大括号里(if else while for等)使用let声明变量的话,在外边是找不到的。但是var就可以
if (1==1){
let b = 2;
}
console.log(b);
3.不存在变量提升
这样写会报错
console.log(c);
let c = 3;
但是如果用var 的话,就相当于是
var c;
console.log(c);
c = 3;
4.const声明变量
const也不能重复声明,也是块级作用域,也不存在变量提升,但是和let不同的是,const赋值之后,不能再修改,还有就是for循环不能用const
比如我这么写,var和let都不会报错,但是const会报错
const d = 1;
d = 2;
console.log(d);
5.优点
(1)for循环中用let比较奶思
比如下面这个for
循环,如果用var i = 0
,那么最后结果是 5 5 5 5 5
,这是因为用var
是在全局只声明一个变量i
,那么每一次循环,全局的i
都会改变,里面的console.log(i)
指向的都是同一个i
,所以最后输出的是55555
for(let i = 0; i < 5; i++){
setTimeout(()=>console.log(i),1000)//0 1 2 3 4
}
但是如果使用let
声明就不一样了,i
只在当前作用域生效,每一次循环都会重新声明变量,上面的代码其实就类似这样:
for(let i = 0; i < 5; i++){
let i = 当前i的值;
setTimeout(()=>console.log(i),1000)//0 1 2 3 4
}
你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i
时,就在上一轮循环的基础上进行计算(如+1运算)。
也正是因为如此,for
循环中不能使用const
定义变量,因为const
定义后的变量不可更改,不能在上一轮循环的基础上进行计算(如++)再重新赋值给本轮声明
(2)for循环分为父作用域和子作用域
另外,for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for (let i = 0; i < 3; i++) {
let i = 'abc';
console.log(i);
}
// abc
// abc
// abc
上面代码正确运行,输出了 3 次abc。这表明函数内部的变量i与循环变量i不在同一个作用域,有各自单独的作用域(同一个作用域不可使用 let 重复声明同一个变量)。
(3)不会污染全局变量
除此之外,let和const不会污染全局变量,比如我如果这么写
var RegExp = 100;
console.log(RegExp);
console.log(window.RegExp);
那么最后的结果是两个100,window对象直接被改了,但是如果用let
第一个是100,第二个结果还是window对象的RegExp函数
let RegExp = 100;
console.log(RegExp);
console.log(window.RegExp);
综上所述:默认情况下用const,如果变量后面需要被修改再用let
二、模板字符串
比如有一个div,我想往里面塞下面这些内容:
<ul>
<li>
<p id="dj">从蓬莱写到仙台,哥们儿代码信手拈来</p>
</li>
</ul>
按照往常的写法,我们是这么写的:
const div = document.querySelector('div');
let id = 'dj';
let text = '从蓬莱写到仙台,哥们儿代码信手拈来';
div.innerHTML = "<ul><li><p id=" + id + ">" + text + "</p></li></ul>";
真的是相当麻烦啊,但是现在,我们可以这么写:
使用tab上面那个小点点包起来,里面的变量使用${变量名}来替换,非常奈斯
let id = 'dj';
let text = '从蓬莱写到仙台,哥们儿代码信手拈来';
div.innerHTML = `<ul>
<li>
<p id=${id}>${text}</p>
</li>
</ul>`;
三、函数默认值、剩余参数、扩展运算符
1.函数默认值
如果我们想写一个求和函数,要给个默认值的话,搁以前得这么写:
function add(a, b) {
a = a || 10;
b = b || 20;
return a + b;
}
console.log(add());
但是在ES6中,就非常easy了:
function addd(a = 10, b = 20) {
return a + b;
}
console.log(addd());
当然啊,如果下面这么写会报错:
function addd(a = 10, b) {
return a + b;
}
console.log(addd(20));
这是因为啥捏,调用函数给的值是按顺序的,这么写a是20,b没赋值,所以不行
当然啊,这个默认值也可以是一个函数
function add2(a = 10, b = getVal(5)) {
return a + b;
}
function getVal(val) {
return val + 5;
}
console.log(add2()); //20
2.剩余参数
我把它理解为代替arguments伪数组的一个真正的数组
比如说我要干一件事儿,我要干啥呢,我要把一个对象儿中的属性名和属性值拿出来传给另一个对象。按照ES5的写法,如果不确定用户传几个参数,我们肯定是使用arguments伪数组来接收
let book = {
title: '前端之王',
name: 'zzy',
age: 18
}
function pick(obj) {
let result = {};
for (let i = 1; i < arguments.length; i++) {
result[arguments[i]] = obj[arguments[i]];
}
return result;
}
let bookData = pick(book, 'title', 'name', 'age');
console.log(bookData);
但是在ES6中,我们可以获取一个真正的数组。使用(…+名字)这种语法来做形参,返回的是一个数组,但是这个一定要写在形参的最后一个。比如下面这个例子,obj对应book,args就是后面那三个属性名构成的数组
慢慢理解理解,问题不大。
let book = {
title: '前端之王',
name: 'zzy',
age: 18
}
function pick(obj, ...args) {
console.log(args); //['title', 'name', 'age']
let result = {}; // 定义一个空对象
for (let i = 0; i < args.length; i++) {
//把对象中的属性值拿过来,给空对象(如果该对象没有这个属性,那么就添加一个)
result[args[i]] = obj[args[i]];
}
return result;
}
let bookData = pick(book, 'title', 'name', 'age');
console.log(bookData);
3.扩展运算符
扩展运算符:将一个数组(或对象)分割,并将数组的各个项作为分离的参数传给函数。其实就是把数组或对象拆开
1、比如我要获取数组的最大值,以前我们会使用apply
const arr = [123, 545, 34, 234, 5];
console.log(Math.max.apply(null, arr));
但是现在我们可以这样写
console.log(Math.max(...arr));
2、其实也可以把对象里的东西拆开
let obj1 = {x:100, y:200};
let obj2 = {
a:1,
...obj1,
b:2
}
console.log(obj2); //{a: 1, x: 100, y: 200, b: 2}
四、箭头函数
1.箭头函数的语法
在ES5中这样定义函数:
let add = function (a,b) {
return a + b;
}
但是在ES6中,我们可以这么写:
let add = (a,b) => {
return a + b;
}
如果只有一个返回值,我们甚至可以把return省略:
let add = (a, b) => a + b;
如果我们返回的是一个对象或者数组什么的,要加()把它包起来才行,不然会报错
let getObj = id => ({ id: id, name: 'zzy' });
console.log(getObj(007)); // id: 007, name: 'zzy'
2.对象中的函数和箭头函数
比如一个Person对象,里面有eat方法:
let person = {
name: "jack",
// 以前:
eat: function (food) {
console.log(this.name + "在吃" + food);
},
// 箭头函数版:
eat2: food => console.log(person.name + "在吃" + food),// 这里拿不到this
// 简写版:
eat3(food){
console.log(this.name + "在吃" + food);
}
}
3.箭头函数的this指向
简单来说,想知道箭头函数中的this指向谁,就看这个箭头函数外边有没有包裹函数,如果它外面有函数,那么this指向的就是外层包裹函数的this,如果没有包裹函数,this指向的就是window。
看几个例子就明白了:
(1)例1
定义一个函数,定义一个对象,使用bind让函数中的this指向这个对象
这样的话,fn里的this指向的就是obj,返回值返回一个匿名箭头函数,箭头函数中的this指向的和包裹它的fn中的this指向相同,也是obj。
这里还要注意bind的特性,bind不会更改原函数,而是复制一份原函数生成一个新函数,新函数中this的指向改变,所以如果bind更改绑定后,调用fn的话,还是输出window
let obj = { name: 'zzy' }; //定义一个对象,让fn的this指向这个对象
function fn() {
console.log(this);
return () => {
console.log(this);
}
}
//更改fn函数的this指向
let newfn = fn.bind(obj); //bind不会更改原函数,复制一份新的给newfn
fn(); //window
newfn(); //obj
(2)例2
如果我定义一个对象,里面写个方法,方法里面的this指向的是函数的调用者,也就是Person这个对象,所以最后输出的结果是18
let Person = {
name: 'zzy',
age: 18,
getAge: function () {
console.log(this.age);
}
}
Person.getAge(); //18
但是我如果把这个函数写成箭头函数,那么this就往外边查找,指向window
let Person = {
name: 'zzy',
age: 18,
getAge: () => {
console.log(this.age);
}
}
Person.getAge(); //undefined
这里还有个注意点:对象有大括号,但不产生作用域
(3)例3
比如我定义一个Dj对象,里面有个事件监听,点击页面调用另一个方法
那么这么写会报错,因为this指向的是document,document里面根本没有dance方法
let Dj = {
id: 007,
drop: function () {
document.addEventListener('click', function () {
console.log(this); //document
this.dance();
})
},
dance: function () {
console.log('drop the beat');
}
}
Dj.drop(); //报错
但是如果我把监听事件里的函数写成箭头函数,this的绑定就消失了,此时this指向的是外层包裹函数的this,也就是drop函数里的this,也就是Dj对象,这样的话就可以找到dance方法了
let Dj = {
id: 007,
drop: function () {
document.addEventListener('click', () => {
console.log(this); //Dj
this.dance();
})
},
dance: function () {
console.log('drop the beat');
}
}
Dj.drop(); //drop the beat
那如果我把drop也写成箭头函数呢?因为箭头函数没有this绑定,此时就会往上找,而对象的大括号没有作用域,所以this就会找到window,懂了吗baby?
let Dj = {
id: 007,
drop: () => {
document.addEventListener('click', () => {
console.log(this); //window
this.dance();
})
},
dance: function () {
console.log('drop the beat');
}
}
Dj.drop(); //报错
4.箭头函数的注意事项
1.使用箭头函数,函数内部没有arguments
2.箭头函数不能用来构造函数,因为function函数是一个对象,但是箭头函数不是一个对象,实际上箭头函数就是个语法糖