ES6 的那些事

82 阅读7分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情

let,const,var区别

① 是否变量提升 ② 是否有块级作用域 ③ 能否重复声明 ④ 能否修改声明的变量

/**
 * var:
 * 顶级变量(window对象的属性);
 * 可以变量提升;
 * 一个变量可以多次声明,后面会覆盖前面的值;
 * 函数内的var同一个变量名时候,这个变量就是局部变量,不用var的话它就是全局变量
 */
var a = 10;
console.log(window.a)

console.log(b)   // undefined
var b = 20

var c = 30
var c = 40
console.log(c)  // 40

var d  = 60
function fun(){
    var d = 70  // 这是局部变量
}
fun()
console.log(d)  // 60
/**
 * let:
 * 有代码块概念(块级作用域{});
 * 不会变量提升;
 * 不可以同一个作用域下重复声明;
 */

{
    let num = 100
    console.log(num)  // 100
}
console.log(num)   // 报错


console.log(num2)  // 报错
let num2 = 200

/**
 * const:
 * 声明一个只读常量,常量的值不能改变;
 * const定义必须赋值;
 * 其他跟let一样
 */

const sum = 500
sum = 600
console.log(sum)

const定义的对象属性能修改吗?

可以。

const obj = {
    name:'lily',
    age:22,
    sex:0
}

obj.name = 'cici'
console.log(obj.name)  // cici

/**
 * const定义基本数据类型(常量)的值不能改动;
 * const定义的引用类型数据(对象,数组)的指向内存地址不能修改,但对对象的属性值是能修改的
 */

ES6中的箭头函数

你是怎么理解:
1、this是静态的,写代码时候就确定this指向,而且不可以改变;
2、this始终指向函数声明时所在的作用域下的this;
3、箭头函数不能做构造函数(因为new过程this是动态的),不能用new命令;
4、没有argument对象,即不能用伪数组去接受参数,但可以用rest参数代替

var name = 'lily'
var obj = {
    name: 'cici'
}

function fun() {
    console.log(this.name)
}
var fun1 = () => {
    console.log(this.name)
}
fun()
fun1()
// 同一个作用域下声明和执行,指向一样,都是输出lily
console.log('------------')
fun.call(obj) // 改变了this指向,输出cici
fun1.call(obj) // this指向不能改变,依然是lily

function fn() {
    let fun2 = () => {
        console.log(this.name)
    } // 里面的箭头函数指向跟fn指向一样,想要改变箭头函数this就要改变fn的this
    fun2()
}

fn() // fn指向window,即箭头函数this指向也是window,输出lily
console.log('============')
fn.call(obj) // 改变fn的this,也改变箭头函数this指向obj,输出cici



// arguments 使用
function fn3() {
    console.log(arguments) // arguments 是作为普通函数的内置对象,不需要定义
}
// let fn4 = ()=>{
//     console.log(arguments)  // arguments is not defined
// }
let fn4 = (...rest) => {
    console.log(rest)
}
fn3(1, 2, 3) // [Arguments] { '0': 1, '1': 2, '2': 3 }
fn4(4, 5, 6, ) // [ 4, 5, 6 ]

image.png

箭头函数适用场景
与this无关的回调,例如:定时器,数组方法回调

不适用场景
与this有关的回调,例如:事件回调,对象方法回调

<style>
        .box {
            height: 100px;
            width: 100px;
            border: 3px solid pink;
        }

        .newbox {
            height: 100px;
            width: 100px;
            border: 3px solid pink;
            background-color: aquamarine;
        }
    </style>
</head>

<body>
    <div class="box">点击盒子魔法</div>
</body>
<script>
    let box = document.querySelector('.box')
    // 普通函数
    // box.addEventListener('click', function () {
    //     var that = this;
    //     // 定时器的this指向window,先把外面的this保存给that,在定时器里面that才能指向box
    //     setTimeout(function () {
    //         that.className = 'newbox'
    //     }, 1000);
    // })

    // 箭头函数
    box.addEventListener('click', function () {
        // 定时器用箭头函数的话,它的this跟外层的function的作用域一样,谁调用指向谁(指向box)
        setTimeout(() => {
            this.className = 'newbox'
        }, 1000);
    })
</script>

可以new一个箭头函数吗?\

不行,会报错!
考察内容:new关键字的执行过程;箭头函数知识点

new关键字的执行过程:
① new 构造函数会在内存中创建一个空对象;
② this指向①中的空对象;
③ 执行构造函数中代码,给空对象添加属性和方法
④ 返回③中的新对象(所以构造函数不需要return,new做了返回这一步)

箭头函数特点:this是静态且不能改变!

扩展运算符作用

... 扩展运算符
第一个作用:复制

// ...  
let arr = [1, 2, 3, 4, 5, 6]
console.log(...arr) // 1 2 3 4 5 6  以空格分割数组

// 复制:对象里面的元素是:基本数据类型--深拷贝;引用类型--浅拷贝
// 1、数组的复制
var arr1 = [1, 2, 3, [20]]
var arr2 = [...arr1]
console.log(arr2) //[ 1, 2, 3, [ 20 ] ]

arr2[0] = 10
arr2[3][0] = 200
console.log('-------------')
console.log(arr1) // [ 1, 2, 3, [ 200 ] ]
console.log(arr2) // [ 10, 2, 3, [ 200 ] ]

// 2、对象的复制
var obj1 = {
    name: 'lily',
    age: 22,
    friend: {
        name: 'cici'
    }
}
var obj2 = {
    ...obj1
}

obj2.age = 18
obj2.friend.name = 'choi'
console.log(obj1) // { name: 'lily', age: 22, friend: { name: 'choi' } }
console.log(obj2) // { name: 'lily', age: 18, friend: { name: 'choi' } }

拷贝出来的对象改变属性,原对象也会被修改,为什么?
对于引用类型数据来说,拷贝出来的是一个真正的副本,在内存堆栈中它们的地址指向还是同一个,动其一全部动。

// 怎么实现深拷贝?
var obj3 = {
    ...obj1,
    // 深拷贝:给它另开一个新地址
    friend: {
        ...obj1.friend
    }
}

obj3.friend.name = 'jessi'
console.log(obj1.friend.name)  // cici
console.log(obj3.friend.name)  //jessi

扩展运算符另一个作用:合并

// 合并
// 数组合并
var arr5 = [7,8,9]
var arr6 = [77,88,99]
var arr7 = [...arr5,...arr6]
console.log(arr7)  // [ 7, 8, 9, 77, 88, 99 ]

// 对象合并
var obj5 = {name:'grace'}
var obj6 = {age:6}
var obj7 = {name:'lili',sex:'女'}  // 属性一样后面会覆盖前面的值
var obj8 = {...obj5,...obj6,...obj7}
console.log(obj8)  // { name: 'lili', age: 6, sex: '女' }


// 字符串变成数组
var myStr = [...'hello']
console.log(myStr)  // [ 'h', 'e', 'l', 'l', 'o' ]

rest参数

处理参数数量不确定的情况

// rest参数:获取多余的参数,整合为一个数字组形式
// rest参数一定要放在形参的最后
function sum(...rest) {
    console.log(rest) // [ 1, 2, 3, 4, 5, 6 ]
    return rest.reduce((pre, cur) => pre + cur)
}

console.log(sum(1, 2, 3, 4, 5, 6))  // 21
console.log(sum(1, 69,78))  // 148

Object.assign的用法

// Object.assign : 用于对象合并,将源对象的可枚举属性复制到目标对象
/**
 * 特点:
 * 会影响目标对象;
 * 同名属性,后面的值会覆盖前面的值;
 *  如果只有一个参数,返回本身;
 * 如果参数不是对象,先转为对象再返回;
 * 复制合并是浅拷贝
 */
let obj = {
    a: 1,
    c: 20
}
let obj2 = {
    b: 2,
    c: 30
}
Object.assign(obj, obj2)
console.log(obj) // { a: 1, c: 30, b: 2 }

console.log(Object.assign(100))  // Number {100}

// 常常用来给对象添加属性以及方法
function Test(name, age, hobby) {
    Object.assign(this, { name, age, hobby });
  }
  const test = new Test('lily', 22, '跑步');
  
  Object.assign(Test.prototype, {
    run() {
      console.log('跑起来');
    },
  });
  
  console.log(test.name)  // lily
  test.run();  // 跑起来

  

对象和数组的解构

解构:一种提取数据的模式
数组解构:以元素的下标为匹配条件
对象解构:以属性名称为匹配条件

// 数组结构
let [a, b, c] = [1, 2, 3]
// 相当于let a = arr[0]
console.log(a, b, c) // 1 2 3

// 结构第一第三个,第二个留空
let [d, , f] = [4, 5, 6]
console.log(d, f) // 4 6


// 对象结构
let obj = {
    name: 'lily',
    age: 20,
    friend: {
        name1: 'cici',
        age: 21
    }
}
let {
    name:nn,   // 结构重命名
    age,
    friend: {
        name1,   // 嵌套结构
        sex='女'  // 属性值如果没有就取默认值,有的话被覆盖
    }
} = obj
console.log(`我叫${nn},今年${age}岁,有个朋友叫${name1},是${sex}的`)  // 我叫lily,今年20岁,有个朋友叫cici,是女的

对象的结构赋值

object-assign的用法

for...of 和 for...in 区别

var obj = {
    0: 1,
    1: 2,
    2: 3,
    length: 3
}
obj = Array.from(obj)
console.log(obj) // 变成真数组:[ 1, 2, 3 ]
for (let value of obj) {
    console.log(value) // 1,2,3
}
/**
 * ① 遍历对象是类数组对象,使用Arrary.from()转为数组
 * ② 遍历对象不是数组,给对象添加symbol.iterator属性,指向迭代器
 */

/**
 * iterator遍历过程:
 * 1、创建一个指针对象,指向当前数据结构的起始位置;
 * 2、第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员;
 * 3、以此类推,不断调用指针对象的next方法,直到它指向数据结构的结束位置;
 * (每次调用next方法,都会返回数据结构的当前成员的信息,一个包含value和done两个属性的对象
 * value:当前对象的值
 * done:布尔值,表示遍历是否结束)
 */

var person = {
    name: 'lily',
    age: 22,
    height: 165,
    wight: 100,
    sex: '女'
}
// for(let p of person){  // TypeError: person is not iterable
//     console.log(p)   
// }

for(let p in person){
    console.log(p)  // 返回对象的属性名
}

// 给person添加一个Symbol.iterator属性,可以用for...of遍历
person[Symbol.iterator] = function () {
    // 拿到对象的所有key值
    var keys = Object.keys(this) // 谁调用this指向谁,这里就是person
    // 定义数组的下标值
    var index = 0
    return {
        next() {
            if (index < keys.length) {
                return {
                    value: person[keys[index++]],  // 链表,每次循环index+1
                    done: false
                }
            } else {
                return {
                    value: undefined,
                    done: true
                }
            }

        }
    }
}

for(let p of person){
    console.log(p)   // 拿到person的各个属性值
} 

for...of 和 for...in 区别

  • for...of: 允许遍历一个有iterator接口的数据结构(数组,对象)
    获取对象键值
    只遍历当前对象
    对于数组遍历,返回数组下标对应的属性值

  • for...in:
    获取对象键名;
    遍历对象整个原型链
    对于数组遍历,返回所有可枚举的属性

// 对于对象遍历
function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.height = 120

var pp = new Person('lily', 22)
pp[Symbol.iterator] = function () {
    var keys = Object.keys(pp)
    var index = 0
    return {
        next() {
            if (index < keys.length) {
                return {
                    value: pp[keys[index++]],
                    done: false
                }
            } else {
                return {
                    value: undefined,
                    done: true
                }
            }

        }
    }
}

for (let value of pp) {
    console.log(value)  
}
// lily
// 22

for (let key in pp) {
    console.log(key)
}
// name
// age
// height  pp原型链上的属性


// 对于数组遍历
var arr = [1,2,3,4,5,6]
for(let i in arr){
    console.log(i)  // 下标
}
console.log('---------')
for(let i of arr){
    console.log(i)  // 值
}

for...in 主要为了遍历对象而产生,不适用遍历数组;
for...of 可以遍历类数组,类数组对象,字符串,对象,数组...