ES6(一):let和const、模板字符串、函数默认值、剩余参数、扩展运算符、箭头函数

一、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函数是一个对象,但是箭头函数不是一个对象,实际上箭头函数就是个语法糖

  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值