FE_ES6基本语法学习

本文详细介绍了JavaScriptES6中的let命令和const命令的特性,包括变量声明、作用域、不能重复声明。接着讨论了块级作用域与var命令的区别。此外,文章还涵盖了模板字符串的使用、函数的默认参数、剩余参数、扩展运算符以及箭头函数的用法和注意事项。最后,提到了解构赋值、对象的新增功能,如属性的简洁表示,以及Object.is()和Object.assign()等方法。
摘要由CSDN通过智能技术生成

1 let 命令的解读

1 let 声明变量,没有变量提升

<script>
    // 不存在变量提升
    console.log(variable)
    let variable = 'zhaoshuai-lc'
</script>

在这里插入图片描述

2 作用域

  • 全局作用域
  • 函数作用域:function() {}
  • 块级作用域:{}
let 是一个块作用域
<script>
    if (true) {
        let variable = 'zhaoshuai-lc'
    }
    console.log(variable)
</script>

在这里插入图片描述

与var定义的变量对比
<script>
    if (true) {
        var variable = 'zhaoshuai-lc'
    }
    console.log(variable)
</script>

在这里插入图片描述

3 不能重复声明

<script>
    let variable = 'zhaoshuai-lc'
    let variable = 'zhaoshuai-lc'
</script>

在这里插入图片描述

2 const

const 除了 let 的三个特性,还有下面的特性:

1、声明常量,一旦被声明,无法修改

在这里插入图片描述

2、可以声明对象,并能修改对象里面的属性值

<script>
    const variable = {
        name: 'zhaoshuai-lc',
        age: 100
    }
    console.log(variable.name);
    variable.name = 'zhaoshuai-la'
    console.log(variable.name)
</script>

在这里插入图片描述
数组也可以修改元素的值:

<script>
    const array = ['zhaoshuai-lc', 'zhaoshuai-la', 'zhaoshuai-lb']
    array[1] = 'zhaoyushuai-la'
    console.log(array);
</script>

在这里插入图片描述

3 作用1、解决 for 变量提升的问题

<script>
    var arr = []
    for (var i = 0; i < 10; i++) {
        arr[i] = function () {
            return i;
        }
    }
    var newVar = arr[6]();
    console.log(newVar);
</script>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

作用2:不会污染全局变量

在这里插入图片描述
在这里插入图片描述
建议:在默认的情况下用 const,而只有在你知道变量值需要被修改的情况下使用 let。

3 模板字符串

使用 tab 键上面的反引号,插入变量时使用 ${变量名}。

<body>
<div class="box1"></div>
<div class="box2"></div>
<script>
    const name = 'zhaoshuai-lc';
    const age = 100;
    const box_1 = document.querySelector('.box1');
    box_1.innerHTML = `<ul>
    <li>
        ${name}
    </li>
    <li>
        ${age}
    </li>
</ul>`
</script>
</body>

在这里插入图片描述

4 函数之默认值、剩余参数

<script>
    function add(a, b) {
        a = a || 100
        b = b || 100
        return a + b
    }

    console.log(add());
</script>

在这里插入图片描述

<script>
    function add(a, b = 100) {
        return a + b;
    }

    console.log(add(200));
</script>

在这里插入图片描述

默认的表达式也可以是一个函数

<script>
    function getVal(val) {
        return val + 1;
    }
    function add(a, b = getVal(10)) {
        return a + b;
    }

    console.log(add(10));
</script>

在这里插入图片描述

剩余参数

由三个点 … 和一个紧跟着的具名参数指定,比如:…keys,这个解决了 arguments 的问题。

<script>
    function getPersonInfo(obj) {
        let result = Object.create(null);
        console.log(result); // {}
        console.log('arguments === ', arguments); 
        for (let i = 0; i < arguments.length; i++) {
            console.log('arguments[i] === ', arguments[i]);
            console.log('obj[arguments[i] === ', obj[arguments[i]]);
            result[arguments[i]] = obj[arguments[i]];
        }
        return result;
    }

    let person = {
        name: 'zhaoshuai-lc',
        age: 100,
        year: 28
    }
    let personInfo = getPersonInfo(person, 'name', 'age', 'year');
    console.log('result === ', personInfo); // {[object Object]: undefined, name: 'zhaoshuai-lc', age: 100, year: 28}
</script>

在这里插入图片描述

<script>
   function getPersonInfo(obj, ...keys) {
       console.log(keys);
       let result = Object.create(null);
       for (let i = 0; i < keys.length; i++) {
           console.log('keys[' + i +'] ===' , keys[i])
           console.log('obj[' + keys[i] +'] ===' , obj[keys[i]])
           result[keys[i]] = obj[keys[i]]
       }
       return result;
   }
   let person = {
       name: 'zhaoshuai-lc',
       age: 100,
       year: 28
   }
   var personInfo = getPersonInfo(person, 'name', 'age', 'year');
   console.log(personInfo);
</script>

在这里插入图片描述

剩余参数 对比 arguments:

<script>
    function checkArgs(...args) {
        console.log(args); // 真实数组
        console.log(arguments); // 伪数组
    }
    checkArgs("name", "author", "year");
</script>

在这里插入图片描述

5 函数之扩展运算符、箭头函数

扩展运算符

剩余运算符:把多个独立的参数合并到一个数组中
扩展运算符:将一个数组分割,并将各个项作为分离的参数传递给函数

<script>
    const arr = [1, 2, 3, 4, 5, 6, 7, 8]
    const maxValue = Math.max(...arr);
    console.log(maxValue);
</script>

在这里插入图片描述

箭头函数

ES6 允许使用“箭头”(=>)定义函数

<script>
    let func = function (val) {
        return val;
    }
    let func_ = val => val;
</script>

如果箭头函数不需要参数或需要多个参数, 就使用一个圆括号代表参数部分.

<script>
    let add = (a, b) => a + b;
    console.log(add(1, 2));
</script>

在这里插入图片描述
由于大括号被解释为代码块, 所以如果箭头函数直接返回一个对象, 必须在对象外面加上括号, 否则会报错

<script>
    let getObject = id => ({
        id: id,
        name: 'zhaoshuai-lc'
    })
    let obj = getObject(10086);
    console.log(obj);
</script>

在这里插入图片描述
箭头函数可以与变量解构结合使用

<script>
    const full = ({first, last}) => first + last
    let person = {
        first: 'zhaoshuai-lc',
        last: '@inspur.com'
    }
    console.log(full(person));
</script>

在这里插入图片描述

<script>
    let mapNew = [1, 2, 3].map(function (val) {
        return val * val;
    })
    console.log(mapNew);
</script>

在这里插入图片描述
修改为箭头函数:

<script>
    let mapNew = [1, 2, 3].map(val => val *val)
    console.log(mapNew);
</script>

使用注意点

箭头函数有几个使用注意点.

(1)函数体内的 [ this ] 对象, 就是定义时所在的对象, 而不是使用时所在的对象.

(2)不可以当作构造函数, 也就是说, 不可以使用new命令, 否则会抛出一个错误.

(3)不可以使用arguments对象, 该对象在函数体内不存在. 如果要用, 可以用 rest 参数代替.

(4)不可以使用yield命令, 因此箭头函数不能用作 Generator 函数

[this]对象的指向是可变的, 但是在箭头函数中, 它是固定的

<script>
    function func() {
        setTimeout(() => {
            console.log('id: ' + this.id)
        }, 100)
    }

    let id = 200
    let person = {
        name: 'zhaoshuai-lc',
        id: 300
    }
    func.call(person)
</script>

在这里插入图片描述
上面代码中, setTimeout()的参数是一个箭头函数,这个箭头函数的定义生效是在func函数生成时,而它的真正执行要等到 100 毫秒后。箭头函数导致 [ this ] 总是指向函数定义生效时所在的对象(本例是person), 所以打印出来的是300.

箭头函数可以让setTimeout里面的 [ this ] , 绑定定义时所在的作用域, 而不是指向运行时所在的作用域

<script>
    function Timer() {
        this.s1 = 0;
        this.s2 = 0;
// 箭头函数
        setInterval(() => this.s1++, 1000);
// 普通函数
        setInterval(function () {
            this.s2++;
        }, 1000);
    }

    var timer = new Timer();

    setTimeout(() => console.log('s1: ', timer.s1), 3100);
    setTimeout(() => console.log('s2: ', timer.s2), 3100);
    // s1: 3
    // s2: 0
</script>

Timer函数内部设置了两个定时器 - 分别使用了箭头函数和普通函数:前者的 [ this ] 绑定定义时所在的作用域(即Timer函数), 后者的 [ this ] 指向运行时所在的作用域(即全局对象)。所以,3100 毫秒之后, timer.s1被更新了 3 次,,而timer.s2一次都没更新。
在这里插入图片描述
上面代码之中, 只有一个 [ this ] , 就是函数foo的 [ this ] , 所以t1、t2、t3都输出同样的结果. 因为所有的内层函数都是箭头函数, 都没有自己的 [ this ] , 它们的 [ this ] 其实都是最外层foo函数的 [ this ] .

除了 [ this ] , 以下三个变量在箭头函数之中也是不存在的, 指向外层函数的对应变量: arguments、super、new.target.

<script>
    function foo() {
        setTimeout(() => {
            console.log('args:', arguments);
        }, 100);
    }

    foo(2, 4, 6, 8)
    // args: [2, 4, 6, 8]
</script>

上面代码中, 箭头函数内部的变量arguments, 其实是函数foo的arguments变量.

另外, 由于箭头函数没有自己的 [ this ] , 所以当然也就不能用call()、apply()、bind()这些方法去改变 [ this ] 的指向.

<script>
    (function () {
        return [
            (() => this.x).bind({x: 'inner'})()
        ];
    }).call({x: 'outer'});
    // ['outer']
</script>

上面代码中, 箭头函数没有自己的 [ this ] , 所以bind方法无效, 内部的 [ this ] 指向外部的 [ this ] .

由于箭头函数使得 [ this ] 从“动态”变成“静态”, 下面两个场合不应该使用箭头函数。第一个场合是定义对象的方法, 且该方法内部包括 [ this ]:
在这里插入图片描述
上面代码中, cat.jumps()方法是一个箭头函数, 这是错误的. 调用cat.jumps()时, 如果是普通函数, 该方法内部的 [ this ] 指向cat;如果写成上面那样的箭头函数, 使得 [ this ] 指向全局对象, 因此不会得到预期结果. 这是因为对象不构成单独的作用域, 导致jumps箭头函数定义时的作用域就是全局作用域.

第二个场合是需要动态 [ this ] 的时候, 也不应使用箭头函数:
在这里插入图片描述
上面代码运行时, 点击按钮会报错, 因为button的监听函数是一个箭头函数, 导致里面的 [ this ] 就是全局对象. 如果改成普通函数, [ this ] 就会动态指向被点击的按钮对象.

另外, 如果函数体很复杂, 有许多行, 或者函数内部有大量的读写操作, 不单纯是为了计算值, 这时也不应该使用箭头函数, 而是要使用普通函数, 这样可以提高代码可读性

6 赋值解构

解构赋值是对赋值运算符的一种拓展,它针对数组和对象来进行操作。
在这里插入图片描述

<script>
    let obj = {
        a: {
            name: "kaimo"
        },
        b: [],
        c: "hello cat"
    }
    // 不完全解构
    let {a} = obj;
    console.log(a);
</script>

在这里插入图片描述

<script>
    let obj = {
        a: {
            name: "kaimo"
        },
        b: [],
        c: "hello cat"
    }
    // 剩余运算符
    let {a, ...res} = obj;
    console.log(a)
    console.log(res)
</script>

在这里插入图片描述

<script>
    let {a, b = 30} = {a: 20};
    console.log(a);
    console.log(b);
</script>

在这里插入图片描述
对数组解构:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7 扩展的对象的功能

1 属性的简洁表示

在这里插入图片描述
在这里插入图片描述

2 CommonJS 模块输出一组变量, 就非常合适使用简洁写法

在这里插入图片描述

3 简洁写法在 属性 赋值器 和 取值器 中的应用

在这里插入图片描述
在这里插入图片描述

8 属性的可枚举性和遍历

for…in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)

<script>
    let person = {
        name: 'name',
        age: 22,
        gender: 'male',
        hobby: ['打球', '读书', '写字']
    }
    for (let personKey in person) {
        console.log(personKey, person[personKey]);
    }
</script>

在这里插入图片描述

Object.keys返回一个数组, 包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名

<script>
    let person = {
        name: 'name',
        age: 22,
        gender: 'male',
        hobby: ['打球', '读书', '写字']
    }
    let propertyList = Object.keys(person);
    for (let property of propertyList) {
        console.log(property, person[property]);
    }
</script>

在这里插入图片描述

Object.getOwnPropertyNames返回一个数组, 包含对象自身的所有属性(不含 Symbol 属性, 但是包括不可枚举属性)的键名.

<script>
    let person = {
        name: 'name',
        age: 22,
        gender: 'male',
        hobby: ['打球', '读书', '写字']
    }
    let propertyList = Object.getOwnPropertyNames(person);
    for (let property of propertyList) {
        console.log(property, person[property]);
    }
</script>

在这里插入图片描述

Object.getOwnPropertySymbols返回一个数组, 包含对象自身的所有 Symbol 属性的键名.

<script>
    let person = {
        name: 'name',
        age: 22,
        gender: 'male',
        hobby: ['打球', '读书', '写字']
    }
    let propertyList = Object.getOwnPropertySymbols(person);
    for (let property of propertyList) {
        console.log(property, person[property]);
    }
</script>

在这里插入图片描述

Reflect.ownKeys返回一个数组, 包含对象自身的(不含继承的)所有键名, 不管键名是 Symbol 或字符串, 也不管是否可枚举

<script>
    let person = {
        name: 'name',
        age: 22,
        gender: 'male',
        hobby: ['打球', '读书', '写字']
    }
    let propertyList = Reflect.ownKeys(person)
    for (let property of propertyList) {
        console.log(property, person[property]);
    }
</script>

在这里插入图片描述

以上的 5 种方法遍历对象的键名, 都遵守同样的属性遍历的次序规则.

  • 首先遍历所有数值键, 按照数值升序排列.
  • 其次遍历所有字符串键, 按照加入时间升序排列.
  • 最后遍历所有 Symbol 键, 按照加入时间升序排列.

9 super 关键字

我们知道, this关键字总是指向函数所在的当前对象 , ES6 又新增了另一个类似的关键字 [ super ], 指向当前对象的原型对象.

<script>
    const proto = {foo: 'hello'};

    const obj = {
        foo: 'world',
        find() {
            return super.foo;
        }
    };
    console.log('obj-1 ', obj);
    Object.setPrototypeOf(obj, proto);
    console.log('obj-2 ', obj);
    obj.find() // "hello"
</script>

在这里插入图片描述
上面代码中, 对象obj.find()方法之中, 通过super.foo引用了原型对象proto的foo属性.

注意, super关键字表示原型对象时, 只能用在对象的方法之中, 用在其他地方都会报错.

<script>
    // 报错
    const obj = {
        foo: super.foo
    }

    // 报错
    const obj = {
        foo: () => super.foo
    }

    // 报错
    const obj = {
        foo: function () {
            return super.foo
        }
    }
</script>

上面三种super的用法都会报错, 因为对于 JavaScript 引擎来说, 这里的super都没有用在对象的方法之中.

  • 第一种写法是super用在属性里面,
  • 第二种和第三种写法是super用在一个函数里面, 然后赋值给foo属性.
    在这里插入图片描述

目前, 只有对象方法的简写法可以让 JavaScript 引擎确认, 定义的是对象的方法.

JavaScript 引擎内部, super.foo等同于Object.getPrototypeOf(this).foo(属性)或Object.getPrototypeOf(this).foo.call(this)(方法).

<script>
    const proto = {
        x: 'hello',
        foo() {
            console.log(this.x);
        },
    };

    const obj = {
        x: 'world',
        foo() {
            super.foo();
        }
    }

    Object.setPrototypeOf(obj, proto);

    obj.foo() // "world"
</script>

在这里插入图片描述
上面代码中, super.foo指向原型对象proto的foo方法, 但是绑定的this却还是当前对象obj, 因此输出的就是world.

10 对象的新增方法

1 Object.is()

ES5 比较两个值是否相等,只有两个运算符: 相等运算符(==)和严格相等运算符(===. 
它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0. 
JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等.

ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题. [ Object.is ] 就是部署这个算法的新方法. 它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致.

<script>
    let variable = 'hello-zhaoshuai-lc'
    let variable_ = 'hello-zhaoshuai-lc'
    var res = Object.is(variable, variable_);
    console.log(res); // true
</script>
<script>
    let variable = 100
    let variable_ = '100'
    var res = Object.is(variable, variable_);
    console.log(res); // false
</script>
<script>
    console.log(+0 === -0); //true
    console.log(NaN === NaN); // false
    console.log(Object.is(+0, -0)); // false
    console.log(Object.is(NaN, NaN)); // true
</script>

2 Object.assign()

开发中常能见到,这个方法还是要着重了解的,需要注意的就是此方法为:** 浅拷贝 **
[ Object.assign() ] 方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target).

<script>
    const target = { a: 1 };
    const source1 = { b: 2 };
    const source2 = { c: 3 };

    Object.assign(target, source1, source2);
    console.log(target); // {a: 1, b: 2, c: 3}
</script>

[ Object.assign() ] 方法的第一个参数是目标对象,后面的参数都是源对象.
注意: 如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性.

<script>
    const target = {a: 1, b: 1};

    const source1 = {b: 2, c: 2};
    const source2 = {c: 3};

    Object.assign(target, source1, source2);
    console.log(target); // {a:1, b:2, c:3}
</script>

如果只有一个参数, [ Object.assign() ] 会直接返回该参数.

<script>
    const obj = {a: 1};
    console.log(Object.assign(obj) === obj); // true
</script>

如果该参数不是对象,则会先转成对象,然后返回.

<script>
    console.log(typeof Object.assign(2)); // "object"
</script>

由于 undefined 和 null 无法转成对象,所以如果它们作为参数,就会报错.

Object.assign(undefined) // 报错
Object.assign(null) // 报错

如果非对象参数出现在源对象的位置(即非首参数),那么处理规则有所不同. 首先,这些参数都会转成对象,如果无法转成对象,就会跳过. 这意味着,如果 undefined 和 null 不在首参数,就不会报错.

let obj = {a: 1};
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true

其他类型的值(即数值、字符串和布尔值)不在首参数,也不会报错. 但是,除了字符串会以数组形式,拷贝入目标对象,其他值都不会产生效果.

<script>
    const v1 = 'abc';
    const v2 = true;
    const v3 = 10;

    const obj = Object.assign({}, v1, v2, v3);
    console.log(obj); // { "0": "a", "1": "b", "2": "c" }
</script>

上面代码中,v1、v2、v3分别是字符串、布尔值和数值,结果只有字符串合入目标对象(以字符数组的形式),数值和布尔值都会被忽略. 这是因为只有字符串的包装对象,会产生可枚举属性.

<script>
    console.log(Object(true)); 
    console.log(Object(10));  
    console.log(Object('abc')); 
</script>
</body>

在这里插入图片描述
上面代码中,布尔值、数值、字符串分别转成对应的包装对象,可以看到它们的原始值都在包装对象的内部属性[[PrimitiveValue]]上面,这个属性是不会被 [ Object.assign() ] 拷贝的. 只有字符串的包装对象,会产生可枚举的实义属性,那些属性则会被拷贝.

[ Object.assign() ] 拷贝的属性是有限制的,只拷贝源对象的自身属性(不拷贝继承属性),也不拷贝不可枚举的属性(enumerable: false).
在这里插入图片描述
上面代码中, [ Object.assign() ] 要拷贝的对象只有一个不可枚举属性invisible,这个属性并没有被拷贝进去.

属性名为 Symbol 值的属性,也会被 [ Object.assign() ] 拷贝.
在这里插入图片描述

浅拷贝

[ Object.assign() ] 方法实行的是浅拷贝,而不是深拷贝. 也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值