ES6学习笔记

文章目录

ES6 概述

  1. ECMAscript、JavaScript、nodeJS,他们的区别是什么?

    ECMAscript:简称ES, 是一个语言标准(循环、判断、变量、数组等数据类型),理论上可以在任何终端实现
    JavaScript:运行在浏览器端的语言,该语言使用ES标准, es+webAPI(dom+bom) = JavaScript
    nodeJS:运行在服务器端的语言,该语言使用ES标准。es+nodeAPI = nodejs
    无论javascript,还是nodeJs,他们都是ES超集(super set)

  2. ECMAscript有哪些关键的版本?

    ES3.0:1999
    ES5.0:2009
    ES6.0:2015,从该版本开始,不再使用数字作为编号,而使用年份
    ES7.0:2016

  3. 为什么ES6如此重要?

    ES6解决了无法开发大型应用的语言层面的问题.

  4. 如何应对兼容性问题?

  5. 学习本课程需要的前置知识有哪些?

    html+css、JavaScript(es5)

块级绑定

ES6不仅引入let关键字用于解决变量声明的问题,同时引入了块级作用域的概念
块级作用域,代码执行时遇到花括号(判断,循环,函数, 手动写{}),会创建一个块级作用域,花括号结束,块级作用域结束

使用var声明变量的问题

  1. 允许重复的变量声明:导致数据被覆盖
  2. 变量提升:怪异的数据访问,闭包问题
  3. 全局变量挂载到window对象:全局对象成员污染问题

使用let声明变量

可以很好的解决var声明变量的问题

  1. let声明的变量不会挂载到全局对象

  2. let声明的变量,不允许在当前作用域重复的声明

    (不同的作用域可以声明同名变量)
    在块级作用域中使用Let定义的语言,不允许在外部作用域中使用

  3. 使用let不会有变量提升,因此,不能在定义之前使用它

    底层实现上,let声明的变量实际上也会有提升,但是,提升后会将其放入到’暂时性死区’,如果访问的变量位于暂时性死区,则会报错:‘cannot access ‘identify’ before initialization’,当代码运行到变量的声明语句时,会将其从暂时性死区中移除。

关于循环的特殊处理

在循环中,let会特殊处理,用Let声明的循环变量,每次进入循环,都会开启一个新的作用域,并且将循环变量绑定到该作用域(每次循环,使用的是一个全新的循环变量)
在循环中,使用let声明的循环变量,在循环结束后,会被销毁

使用const声明变量

const和let完全相同,仅在于用const声明的变量,必须在声明时赋值,而且不可以重新赋值.
实际上,在开发中,应该尽量使用const来声明变量,以保证变量的值不被篡改(就根本不能赋值,赋同样的值也不行)
原因:

  1. 根据经验,开发中的很多变量,都是不会更改,也不应该更改的
  2. 后续的很多框架或者第三方js库,都要求数据不可变,使用常量可以一定程度上保证这一点

注意的细节:

  1. 常量不可变,是指声明的常量(如果是对象的话就是一个引用,也就是一个地址,该地址指向具体的数据房间)的内存空间不可变,并不保证内存空间指向的内存不能改变(对象内具体的数据是可以更改的)

  2. 常量的命名

    1. 特殊的常量:该常量从字面意义上,一定是不可变的,比如圆周率、月地距离,通常,该常量的名称全部大写,多个单词之间使用下划线进行分割
    2. 普通的常量,看常量名不确定是否可变,使用普通小驼峰命名
  3. 在for循环中,循环变量不可以使用常量,for in循环可以,因为for(const prop in obj)中的prop不会改变,而且每次循环的时候都是新的块级作用域,也就是新的prop

字符串和正则表达式

更好的Unicode支持

早期,由于存储空间宝贵,Unicode使用16位二进制来存储文字(全世界的文字),我们将一个16位的二进制编码叫做一个码元(code unit)
后来,由于硬件的发展,Unicode对文字编码进行了扩展,将某些文字扩展到了32位(占用两个码元,一个码点),并且,将某个文字对应的二进制数字叫做码点(code point)

const text = '𠮷';

ES6为了解决这个困扰,为字符串提供了方法: String.prototype.codePointAt(index), 用来得到第index个码点
同时,es6为正则表达式添加了一个flag:u, 如果添加了该配置,匹配的时候根据码点匹配,而不是根据码元

//char[index]是不是32位的
function is32bit(char, index) {
   
    return char.charCodeAt(char, index) > 0xffff; //第一位码点是否大于16位,大于32位的话也就是用码点表示
}

function getTextLengthOfCodePoint(text) {
   
    let len = 0;
    for (len i = 0; i < text.length; ++i) {
   
        if (is32bit(text, i)) {
   
            i++; //跳过下一次循环
        }
        len++;
    }
}

字符串的API

字符串的实例的方法

  • Includes

判断字符串中是否包含指定的子字符串

  • startsWith:

判断字符串中是否以指定的字符串开头

  • endsWith

判断字符串是否以指定的字符串结尾

  • repeat

将字符串重复指定的次数,然后返回一个新的字符串

正则中的粘连标记

标记名:y
含义:匹配时,完全按照正则对象中的lastIndex位置(默认位0)开始匹配,并且匹配的位置必须在lastIndex位置

模板字符串

解决字符串的换行和字符串与变量的拼接
将引号换为 符号,内部拼接变量的话使用 ${} ` ,内部填写表达式

模板字符串标记: 在模板字符串书写之前,可以加上标记:

标记是一个函数,函数参数如下

  1. 参数1:被插值分割的字符串数组
  2. 后续参数:所有的插值
  3. 自定义标记
let like1 = 'music';
let like2 = 'basketball';
let result = myTag `我喜欢${
     like1},也喜欢${
     like2}`;
//myTag是一个函数,需要自己声明
function myTag(parts) {
   
    const restArugs = Array.prototype.slice.apply(arguments).slice(1); //该数组是插值数组
    let resultStr = '';
    // parts.length = restArugs.length + 1;
    for (let i = 0; i < restArugs.length; ++i) {
   
        resultStr += `${
     parts[i]}${
     restArugs[i]}`;
        if (i === restArugs.length - 1) {
   
            resultStr += parts[i + 1];
        }
    }
    return resultStr;
}
  1. String.raw

加上该标记的模板字符串内部的换行符,制表符等会被当作普通的字符来处理, 但是变量的插入依然生效

由于可以自定义模板字符串的标记,所以使用的时候也可以对内部拼接的变量做一些安全处理

例如将变量中的<>进行编码

function safe(parts) {
   
    let resultStr = '';
    const restArugs = Array.prototype.slice.apply(arguments, 1);
    for (let i = 0; i < restArugs.length; ++i) {
   
        resultStr += `${
     parts[i]}${
     restArugs[i].replace(/</g,'&lt;').replace(/>/g, '&gt;')}`
        if (i === resultStr.length - 1) {
   
            resultStr += parts[i];
        }
    }
    return resultStr;
}

4. 函数

参数默认值

在书写形参时,直接给形参赋值,赋的值即为默认值
这样一来,当调用函数时,如果参数传递为undefined, 它就会使用默认值

如果参数传递了值, 默认值不会赋值,如果参数默认值为函数执行,则函数不执行

参数默认值对arguments的影响

严格模式下,形参和arguments是脱离的(不是一一对应的)

只要给函数加上参数默认值,该函数会自动变为严格模式下的规则(形参和arguments不是一一对应的)

留意暂时性死区

形参和ES6中的let或const声明一样,具有作用域,并且根据参数的声明顺序,存在暂时性死区

function test(a = b, b) {
   
    console.log(a, b)
}
test(undefined, 2); //会报错,a在赋值的时候,b还未使用

4.2剩余参数

  • arguments的缺陷
  1. 如果和形参配合使用,容易导致混乱
  2. 从语义上 ,使用arguments获取参数,由于形参缺失,无法从函数定义上理解函数的真实用途

ES6的剩余参数专门用于收集末尾的所有参数,将其放置到一个形参数组(并不和arguments一样是伪数组,它是真数组)中
语法:

function(...形参名) {
   
    // 所有的实参都在形参名数组中
}

细节

  1. 一个函数只能写一个剩余参数
  2. 一个函数,如果有剩余参数,剩余参数必须是最后一个参数

4.3 展开运算符

对数组展开 ES6

  • 作为实参, 将需要传入罗列参数的函数传入数组就很方便

与剩余参数不同的是,对数组展开的参数可以放在实参的任何位置(开头,中间,结尾)
2

function sum(...args) {
   
    let result = 0;
    for (let i = 0; i < args.length; ++i) {
   
        result += args[i];
    }
    return result;
}

function getRandomArr(len) {
   
    let arr = [];
    for (let i = 0; i < len; ++i) {
   
        arr.push(Math.random());
    }
    return arr;
}
let numbers = getRandomArr(10);
let result = sum(...numbers); //相当于将Numbers数组一个一个传递进sum函数,剩余参数也即为一个10个数组成的伪数组
  • 克隆数组
let arr1 = [1, 2, 3, 4, 5];
let arr2 = [10, ...arr1, 29]; //得到新数组[10, 1, 2, 3, 4, 5, 29]

对对象展开 ES7

let obj1 = {
   
    name: '张三',
    loves: 'music',
    info: {
   
        weight: 70,
        height: 188
    }
}
let obj2 = {
   
    ...obj1,
    // 这就是对象的展开,这句话相当于是name:obj1.name,loves:obj1.love...
    elseProp: 'value'
}
//obj2是新的对象,指向的地址并不和obj1相同,但是对象内部也是浅克隆
//如果想要针对内部的info对象,可以将info对象在内部在进行一次展开
let obj3 = {
   
    ...obj1,
    info: {
   
        ...obj1.info
    }
}

柯里化

用于固定某个函数的前面的参数,得到一个新的函数,新的函数调用时,接收剩余的函数(如果剩余的参数不足够,则返回一个新的固定该剩余参数的函数,如果足够则调用);

function curry(func, ...fixedArgus) {
   
    return function(...remaindArgus) {
   
        const totalArgus = remaindArgus + fixedArgus;
        if (totalArgus >= func.length) {
   
            // 参数够了
            return func(...totalArgus);
        } else {
   
            // 参数不够,继续固定
            return curry(func, ...totalArgus);
        }
    }
}
//函数管道
function pipe(...funcs) {
   
    return val => {
   
        for (let i = 0; i < funcs.length; ++i) {
   
            val = funcs[i](val);
        }
        return val;
    }
    /*
    return  val => {
        funcs.reduce((result, cur)=>{
            return cur(result)
        }, val)
    }
    */
}

4.5 明确函数的双重用途

  1. 构造函数调用
  2. 直接调用(如果函数中有this对象则this指向的是window)

为了避免调用出错,在函数内部判断是否使用new的方式来调用的函数

  • 以前的判断方式
if (!this instance of 函数名
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值