ES6知识点1

1 课程介绍

1.1简介

​ ES6 既是一个历史名词(特指2015年发布的标准ES2015),也是一个泛指,含义是 5.1 版以后的 JavaScript 的下一代标准,涵盖了ES2015、ES2016、ES2017等

1.2环境安装

​ Node.js是JavaScript语言的服务器运行环境,对ES6的支持度比浏览器更高。

​ 网址:https://nodejs.org/en/

​ 默认安装

1.2.1NPM使用

本地安装

​ npm install <模块名> 使用:require('模块名')

全局安装

安装模块    npm install    <模块名>  -g
卸载模块    npm uninstall  <模块名>
更新模块    npm update  <模块名>
搜索模块    npm search  <模块名>

2 声明和表达式

2.1解构赋值

​ ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。

​ 如果解构不成功,变量的值就等于undefined

​ 如果等号的右边不是数组(不是可遍历的结构),将会报错

​ 解构赋值不仅适用于var命令,也适用于let和const命令。

​ (1)设置默认值

​ 解构赋值允许指定默认值。当等式右边没值或是undefined时,使用默认值

语法  let [变量=默认值]=[赋值]

​ (2)对象的解构赋值

​ 解构不仅可以用于数组,还可以用于对象。

var { name, age } = { name: "sss", bar: "12" };    // name:sss ,age:12

​ 注意变量必须与属性同名,才能取到正确的值,位置顺序没关系

​ 如果变量名与属性名不一致,必须写成下面这样:

var { foo: baz } = { foo: 'aaa', bar: 'bbb' };        //  baz :"aaa"

​ 对象的解构赋值的内部机制是:先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

img

​ (3)字符串的解构赋值

​ 此时的字符串被转换成了一个类似数组的对象。

let [a, b, c, d] = 'nice';     // a为n,b为i,c为c,d为e

​ (4)函数参数的解构赋值

function add([x, y]){
  return x + y;
}
add([1, 2]);     // 3

//设置默认值
function move({x = 1, y = 2} = {}) {    // 默认参数 x=1,y=2
  return [x, y];
}
move({x: 3, y: 8});     // [3, 8]
move({x: 3});     // [3, 2]
move({});     // [1, 2]
move();     // [1, 2]

​ (5)不能使用圆括号的情况

建议只要有可能,就不要在解构中放置圆括号。

​ <1>变量声明语句中,不能带有圆括号。

​ <2>函数参数中,模式不能带有圆括号。

​ <3>赋值语句中,不能将整个模式或嵌套模式中的一层,放在圆括号之中。

总结:解构赋值的使用场景

  • 交换变量的值

    [x, y] = [y, x];
  • 方便从函数取值

    function func() {
      return [1, 2, 3];
    }
    var [a, b, c] = func();
  • 函数参数的定义

    // 参数是一组无次序的值
    function f({x, y, z}) { ... }
    
    f({z: 1, y: 2, x: 3});
  • 提取JSON数据

    var jsonData = {
      id: 12,
      name: "sss",
      data: [867, 5309]
    };
    
    let { id, name, data: number } = jsonData;
    • 函数默认参数值
function move({x = 1, y = 2} = {}) { // 默认值
  return [x, y];
}

move({x: 3});     //   y使用默认值,x:3, y:2

2.2 let和const

2.2.1 let命令

  • 只在let命令所在的代码块有效

  • let不存在变量提升

    变量的提升:javascript并不是严格的自上而下执行的语言,它会将当前作用域的所有变量的声明提升到程序的顶部

console.log(aa)  // 输出undefined
var aa=2222;    // 实际上是分两步执行  var aa;aa=2222;
  • let暂时性死区

    ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量就会报错

    总之,在代码块内使用let命令声明变量之前,该变量都是不可用的

  • let不能重复声明同个变量

    ​ let不允许在相同作用域内,重复声明同一个变量。

let num=111;
let num=222;  // 报错内容:Identifier 'num' has already been declared

2.2.2const命令

const 声明一个只读的常量。一旦声明,常量的值就不能改变。

​ const一旦声明变量就必须立即初始化,不能留到以后赋值。

只声明不赋值同样会报错。

const声明的常量不能重新赋值。

    const aa= []; 
    aa.push('firstNumber');     // 可执行 
    aa.length = 0;      // 可执行 
    aa= ['secondNumber'];      // 报错

2.3 Symbol数据类型

2.3.0概述

  • JavaScript的7中数据类型:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)、Symbol

  • Symbol是一种基础数据类型

  • 功能类似于一种标识唯一性的ID

  • 对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。

​ 通常情况下,我们可以通过调Symbol()函数来创建一个Symbol实例

let s1 = Symbol()
  • Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述。这主要是为了在控制台显示或者转为字符串时,比较容易区分。

传入一个可选的字符串参数,相当于给你创建的Symbol实例一个描述信息

let s2 = Symbol('another symbol')

2.3.1作为属性名的Symbol

由于每一个Symbol值都是不相等的,这意味着Symbol值可以作为标识符用于对象的属性名,这就能保证不会出现同名的属性。

var name= Symbol();

// 第一种写法
var student= {};
student[name] = 'jack!';    //  Symbol值name作为student的属性


// 第二种写法
var student= {
  [name]: 'jack!'        //注意要使用方括号包裹,不放在方括号中,该属性的键名就是字符串
};

注意,Symbol值作为对象属性名时,不能用点运算符,要使用方括号

取值对象的Symbol类型的属性也要用方括号。普通属性是直接用点运算符。

因为点运算符后面总是字符串

2.3.2 Symbol类型定义常量

- 用于定义一组常量,保证这组常量的值都是不相等的。

- 常量使用Symbol值最大的好处,就是其他任何值**都不可能有相同的值**了。
levels = {
  DEBUG: Symbol('debug'),
  INFO: Symbol('info'),
  WARN: Symbol('warn')
};

console.log(levels.DEBUG)        //  Symbol(debug)
console.log(levels.INFO)           //   Symbol(info)
console.log(levels.WARN)         //  Symbol(warn)

2.3.3 属性名的遍历

​ Symbol 作为属性名,该属性不会出现在for...in、for...of循环中,也不会被Object.keys()、JSON.stringify()等返回

  • ES6中有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名,返回的是一个数组。
var obj = {};

var name = Symbol('name');
var age = Symbol('age');
obj[name] = 'huang';
obj[age] = '23';

var objectSymbols = Object.getOwnPropertySymbols(obj);        // 是个数组,可以用for循环遍历
console.log(objectSymbols)        //  [ Symbol(name), Symbol(age) ]
console.log(obj[name])        //  huang     取出Symbol类型的属性要用方括号
  • Reflect.ownKeys方法可以返回对象的属性所有类型的键名,包括常规键名和 Symbol 键名
var name = Symbol('name');
var age = Symbol('age');

var obj = {
    [name] : 'huang',    //  Symbol类型属性
    [age] : '23',        //  Symbol类型属性
    num : 1        //  普通属性
};

console.log( Reflect.ownKeys(obj))  // [ 'num', Symbol(name), Symbol(age) ]返回值是数组

2.3.4Symbol.for(),Symbol.keyFor()

  • Symbol.for方法可以重新使用同一个Symbol值。

    • 它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值
    • Symbol.for方法和Symbol是不一样的,Symbol每次都是创建一个新的,Symbol.for只有不存在时才创建新的。
    • Symbol.for为Symbol值登记的名字,是全局环境的
    Symbol.for("foo") === Symbol.for("foo")        // true
    Symbol("foo") === Symbol("foo")         // false
  • Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key

    var s1 = Symbol.for("foo");
    Symbol.keyFor(s1)             // "foo"       使用Symbol.for有登记名字

    var s2 = Symbol("foo");
    Symbol.keyFor(s2)             // undefined        使用Symbol未登记名字

2.3.5 单例中使用Symbol

  • 单例(Singleton)指的是调用一个类,任何时候返回的都是同一个实例
const FOO_KEY = Symbol.for('foo');        // 使FOO_KEY变成Symbol类型

function A() { 

  this.foo = 'hello';
}

if (!global[FOO_KEY]) {    //  如果不存这个实例创建新的,存在就跳过

    global[FOO_KEY] = new A();           // 创建一个新的实例,把实例放到顶层对象global
}

module.exports = global[FOO_KEY];        // 暴露这个实例,可以被其他文件引用

3 内置对象及扩展

3.1字符串的扩展

3.1.1字符的Unicode表示法

  • 允许采用 \uxxxx 形式表示一个字符,其中“xxxx”表示字符的码点
    • 这种表示法只限于\u0000——\uFFFF之间的字符。
    • 超出这个范围的字符,必须用两个双字节的形式表达
  • ES6改进:只要将码点放入大括号就能正确解读该字符
console.log("\u20BB7");    // JavaScript会理解成 \u20BB+7 ,所以只会显示一个空格,后面跟着一个7
console.log("\u{20BB7}");   //输出:告

3.1.2遍历字符串

  • for of与for in

    • for in遍历数组的毛病

      1.index索引为字符串型数字,不能直接进行几何运算
      2.遍历顺序有可能不是按照实际数组的内部顺序
      3.使用for in会遍历数组所有的可枚举属性,包括原型

      结论:for in更适合遍历对象,不要使用for in遍历数组

    • for of

      for of遍历的只是数组内的元素,而不包括数组的原型属性method和索引name

    结论:for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值。

  • ES6为字符串添加了遍历器接口,使得字符串可以被for...of循环遍历

    • 这个遍历器最大的优点是可以识别大于0xFFFF的码点传统的for循环无法识别这样的码点。
    for (let i of 'abcd') {
      console.log(i)
    }
    //a
    //b
    //c
    //d

3.1.3其它方法

  • 用来确定一个字符串是否包含在另一个字符串中
    • indexOf 返回参数字符串的位置,如果找到返回位置(第一个字符的位置);未找到,返回-1
    • includes() 返回布尔值,表示是否找到了参数字符串。
    • startsWith() 返回布尔值,表示参数字符串是否在源字符串的头部。
    • endWith() 返回布尔值,表示参数字符串是否在源字符串的尾部。
  • repeat()方法
    • 返回一个新字符串,表示将原字符串重复n次。
    • 语法 字符串.repeat(重复次数的参数)
'a'.repeat(3)        // "aaa"
'ok'.repeat(2)     // "okok"
'ok'.repeat(0)     // ""
'ok'.repeat(3.9)     // "okokok"        参数为小数只取整数部分,参数为负数会报错,参数为NaN就是0
  • codePointAt()方法
    • 正确处理4个字节储存的字符,返回一个字符的码点。

3.1.4模板字符串

  • 传统方式:使用加号拼接字符串和变量

    $('.op').append(
        "I am '+info.age+'years old"
    )
  • ES6使用:模板字符串(template string)是增强版的字符串,用反引号(`)标识

    • 变量用 ${变量} 来表示。大括号内部可以放入任意的JavaScript表达式,可以进行运算,引用对象属性,调用函数,甚至还能嵌套。

    • 如果在模板字符串中需要使用反引号,则前面要用反斜杠转义

    • 模板字符串中所有的空格和缩进都会被保留在输出之中,可以用来定义多行字符串。

    $('.op').append(`
        I am ${info.age} years old
    `)

3.15标签模板

  • 模板字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串。这被称为“标签模板”功能(tagged template)
  • 标签模板其实不是模板,而是函数调用的一种特殊形式
  • “标签”指的就是函数,紧跟在后面的模板字符串就是它的参数
  • 如果模板字符里面有变量,就不是简单的调用了,而是会将模板字符串先处理成多个参数,再调用函数
alert`123`    // 字符串123跟在 alert函数后面
// 等同于
alert(123)

var a = 5;
var b = 10;
tag`Hello ${ a + b } world ${ a * b }`;        // tag表示一个函数
// 等同于
tag(['Hello ', ' world ', ''], 15, 50);    
  • 应用场景:
    • 过滤 HTML 字符串,防止用户输入恶意内容
    • 多语言转换(国际化处理)

3.2正则的扩展

3.2.1正则(RegExp)构造函数

  • ES5中,RegExp构造函数参数

    • 1.第一个参数是字符串,第二个参数表示正则表达式的修饰符(flag)
    var regex = new RegExp('xyz', 'i');
    // 等价于
    var regex = /xyz/i;
    • 参数是一个正则表示式
     var regex = new RegExp(/xyz/i);
    // 等价于
    var regex = /xyz/i;
    
    //★ES5不允许此时使用第二个参数,添加修饰符,否则会报错。
    var regex = new RegExp(/xyz/, 'i');   
    // 报错  Uncaught TypeError: Cannot supply flags when constructing one RegExp from another
  • ES6

    • 如果RegExp构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符

      而且,返回的正则表达式会忽略原有的正则表达式的修饰符,只使用新指定的修饰符。

      new RegExp(/xyz/ig, 'i').flags // 返回值 i   flags属性,会返回正则表达式的修饰符。

      原有正则对象的修饰符是ig,它会被第二个参数 i 覆盖

3.2.2 u修饰符

​ ES6对正则表达式添加了u修饰符,含义为“Unicode模式”,用来正确处理大于\uFFFF的Unicode字符。也就是说,会正确处理四个字节的UTF-16编码

/^\uD83D/u.test('\uD83D\uDC2A')               // 打印值为false
//★结果说明:加了u修饰符以后,ES6就会识别其为一个字符,所以第一行代码结果为false。
  • Unicode字符表示法

    S6新增了使用大括号表示Unicode字符,这种表示法在正则表达式中必须加上u修饰符,才能识别

    /\u{61}/.test('a')             // 打印结果 false
    /\u{61}/u.test('a')            // 打印结果true
    //★如果不加u修饰符,正则表达式无法识别\u{61}这种表示法,只会认为这匹配61个连续的u。

3.2.3y 修饰符

​ y修饰符,叫做“粘连”(sticky)修饰符

  • y修饰符与g修饰符的区别

    • y修饰符 全局匹配,后一次匹配从剩余字符的头部开始匹配
    • g修饰符 全局匹配,后一次匹配 只要剩余位置中存在匹配即可
    var s = 'aaa_aa_a';        // 字符串
    
    // 正则声明,此处是字面量方式,还有一种是构造函数方式
    var r1 = /a+/g;        // 带g修饰符
    var r2 = /a+/y;        // 带y修饰符
    
    //第一次匹配
    r1.exec(s)         // 匹配结果 ["aaa"]  ,  剩余字符串_aa_a
    r2.exec(s)        //   ["aaa"]    剩余字符串_aa_a
    
    //第二次匹配
    r1.exec(s)         //   ["aa"]
    r2.exec(s)         //  null    剩余字符串_aa_a头部 是 _ 不符合,所以返回null
  • sticky属性

var r = /abc\d/y;
r.sticky     // true
  • flags属性

    ES6为正则表达式新增了flags属性,会返回正则表达式的修饰符

    // ES5的source属性,返回正则表达式的正文
    /abc/ig.source        // "abc"
    //  ES6的flags属性,返回正则表达式的修饰符
    /abc/ig.flags        // 'gi'

3.3数值的扩展

3.3.1二进制和八进制表示法

  • 二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示

    // 十进制456转换为二进制111001000 ,二进制使用0b开头
    0b111001000 === 456    // true   
     //  十进制456转换为二进制710,八进制使用0o开头
    0o710 === 456     // true   
  • 将0b和0o前缀的字符串数值转为十进制,要使用Number方法

    Number('0b110')      // 6    二进制
    Number('0o11')      // 9    八进制

3.3.2Number对象

​ ES6新提供了Number.isFinite()和Number.isNaN()两个方法,只对数值有效,非数值一律返回false

  • Number.isFinite()

    用来检查一个数值是否为有限的(finite)。有限返回值为true,不是有限的返回false。

  • Number.isNaN()

    用来检查一个值是否为NaN。是NaN返回值为true,不是NaN返回值为false。

  • Number.isInteger()

    用来判断一个值是否为整数。需要注意的是,在JavaScript内部,整数和浮点数是同样的储存方法,所以3和3.0被视为同一个值

    Number.isFinite(0.545877);     // true
    Number.isFinite('aaaa');     // false    非数值返回false
    Number.isFinite(NaN);     // false
    Number.isFinite(Infinity);     // false   Infinity 属性用于存放表示正无穷大的数值。
    
    Number.isNaN(NaN)     // true
    Number.isNaN(15)     // false
    Number.isNaN('15')     // false    
    Number.isNaN(true)     // false
    Number.isNaN(9/NaN)     // true
    Number.isNaN('true'/0)     // true
    Number.isNaN('true'/'true')     // true
    
    Number.isInteger(10)     // true
    Number.isInteger(10.0)     // true
    Number.isInteger(10.1)     // false
    Number.isInteger("10")     // false
    Number.isInteger(true)     // false 
  • Number.parseInt(), Number.parseFloat()
    • 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。
    • parseInt()函数可解析一个字符串,并返回一个整数
    • parseFloat() 函数可解析一个字符串,并返回一个浮点数
  • Number.EPSILON 常量
    • 浮点数计算是不精确的。

    • ES6在Number对象上面,作为一个误差范围。

      当这个误差能够小于Number.EPSILON,我们就可以认为得到了正确结果

  • Number.isSafeInteger() 安全整数
    • JavaScript能够准确表示的整数范围在-2^53到2^53之间(不含两个端点),超过这个范围,无法精确表示这个值。
    • 用来判断一个整数是否落在这个范围之内
    // ES5的写法
    parseInt('12.34')     // 12
    parseFloat('12.34#')     // 12.34
    // ES6的写法
    Number.parseInt('12.34')     // 12
    Number.parseFloat('12.34abcd这里是字符串')     // 12.34
    
    var num=0.1+0.2-0.3        // 5.551115123125783e-17
    num< Number.EPSILON         //true  表示小于误差范围
    
    var num=Math.pow(2, 53)    // 9007199254740992
    var num2=num+1     //  num2超过 -2^53到2^53这个范围,打印出来和num值相等,这是错误的
    num==num2   // true ☆结果分析:超出2的53次方之后,一个数就不精确了。num和num2本来应该是不相等,但是返回值是相等。
    
    Number.isSafeInteger(num2)   // false

3.3.3Math对象的扩展

  • Math.trunc 用于去除一个数的小数部分,返回整数部分

  • Math.sign 判断一个数到底是正数、负数还是零

    ​ 返回五种值:

    • 参数为正数,返回+1;
    • 参数为负数,返回-1;
    • 参数为0,返回0;
    • 参数为-0,返回-0;
    • 其他值,返回NaN。
  • Math.cbrt 计算一个数的立方根

    Math.trunc(4.1)     // 4
    Math.trunc('123.456') //123    对于非数值,Math.trunc内部使用Number方法将其先转为数值
    Math.trunc(NaN);      // NaN    对于空值和无法截取整数的值,返回NaN
    
    Math.cbrt('8')     // 2    2的三次方为8
  • Math.expm1(x) 返回ex - 1,即Math.exp(x) - 1。

  • Math.log1p(x) 方法返回1 + x的自然对数,即Math.log(1 + x)。如果x小于-1,返回NaN。

  • Math.log10(x) 返回以10为底的x的对数。如果x小于0,则返回NaN。

  • Math.log2(x) 返回以2为底的x的对数。如果x小于0,则返回NaN。

  • Math.sinh(x) 返回x的双曲正弦
  • Math.cosh(x) 返回x的双曲余弦
  • Math.tanh(x) 返回x的双曲正切
  • Math.asinh(x) 返回x的反双曲正弦
  • Math.acosh(x) 返回x的反双曲余弦
  • Math.atanh(x) 返回x的反双曲正切

3.4数组的扩展

3.4.1Array.from方法

​ 用于将两类对象转为真正的数组类似数组的对象可遍历(iterable)的对象(包括ES6新增的数据结构Set和Map)。

  • 只要是部署了Iterator接口的数据结构,Array.from都能将其转为数组,如字符串。
  • 接受第二个参数,用来对每个元素进行处理,将处理后的值放入返回的数组。
// 类似数组的对象
var arr= {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};
// ES6的写法
var  arr2 = Array.from(arr);     // ['a', 'b', 'c']

Array.from('hello')        //  ['h', 'e', 'l', 'l', 'o']

Array.from([1, 2, 3], (x) => x * x)        // [1, 4, 9]        =>是箭头函数

3.4.2Array.of方法

  • Array.of方法用于将一组值,转换为数组。如果没有参数,就返回一个空数组。
  • Array.of基本上可以用来替代Array()或new Array(),并且Array.of不会因为参数不同而导致重载。
  • 如果使用Array()方法,当参数只有一个时,实际上是指定数组的长度
Array.of(7, 8, 9)     // [7,8,9]
Array.of(13)         // [13]
Array.of()     // []
Array.of(13).length     // 1

var num = Array(2);        // 使用Array 方法
var num2 = Array.of(2);    // 使用Array.of 方法
num.length          //  2
num2.length        //  1

3.4.3数组实例copyWithin()方法

​ 在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。

语法 Array.prototype.copyWithin(target, start = 0, end = this.length)

其中有三个参数(应该是数值类型,不是的话会自动转为数值):

  • target(必须):从该位置开始替换数据
  • start(可选):从该位置开始读取数据,默认为0。如果为负值,表示倒数。
  • end(可选): 到该位置前停止读取数据,没取到这个数,默认等于数组长度。如果为负值,表示倒着数。
//将从3号位直到数组结束的成员(4和5),复制到从0号位开始的位置,结果覆盖了原来的1和2。
[1, 2, 3, 4, 5].copyWithin(0, 3)  // 替换后的结果 [4, 5, 3, 4, 5]

//将下标为3号和4号位复制到下标为0号位和1号位  
[1, 2, 3, 4, 5].copyWithin(0, 3, 5)        //     [4, 5, 3, 4, 5]

//     -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)        //     [4, 2, 3, 4, 5]

/从下标为2开始替换(也就是数值9),后两个参数没写,替换的内容默认从下标为0开始(数值8,5,9)
[8,5,9,4,3].copyWithin(2)    //  [ 8, 5, 8, 5, 9 ]    

3.4.4数组实例的find()和findIndex()

  • 数组实例的find方法,用于找出第一个符合条件的数组成员

  • 参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。

  • find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组

  • 数组实例的findIndex方法,返回第一个符合条件的数组成员的位置(下标),如果所有成员都不符合条件,则返回-1

    // 查找数组中小于0的数
    [1, 2, -3, 4].find(  (n) => n < 0  )    // -3   => 是箭头函数,等于 function(n) {  return n<0; }
    
    [1, 2, 3, 4].find(function(value, index, arr) {  return value > 2; })     // 返回值为3
    
     [1, 2, 3, 4].findIndex(function(value, index, arr) {
        return value > 2;        // 返回值为2 
    }        

3.4.5其他数组实例的方法

  • fill
    • 使用给定值,填充一个数组

    • 还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置

      ['a', 'b', 'c'].fill(7, 1, 2)        //  ['a', 7, 'c']
      //fill方法从1号位开始,向原数组填充7,到2号位之前结束。
      ['a', 'b', 'c'].fill(7, 1, 3)        //  ['a', 7, 7]
  • entries(),keys()和values() 用于遍历数组
    • keys()是对键名的遍历
    • values()是对键值的遍历
    • entries()是对键值对的遍历
    console.log("----------keys方法---------")
    for (let index of ['hello', 'world'].keys()) {
    
        console.log("输出结果:", index);
    }
    
    console.log("----------values方法发---------")
    for (let elem of ['hello', 'world'].values()) {
    
        console.log("输出结果:",elem);
    }
    
    console.log("----------entries方法发---------")
    for (let [index, elem] of ['hello', 'world'].entries()) {
    
        console.log("输出结果:",index, elem);
    }
  • includes()

    返回一个布尔值,表示某个数组是否包含给定的值

    [1, 2, 3].includes(2);     // true
    [1, 2, 3].includes(4);     // false

3.5函数的扩展

3.5.1箭头函数

​ ES6允许使用“箭头”(=>)定义函数。箭头函数使得表达更加简洁。

  • 由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号
var    f = (参数)=>{ 代码块}
var    ff = (x,y)=>{ return x+y }
// 相当于
var f = function(参数){ 代码块 }
var ff = function(x,y){ return x+y }

const full = ({ first, last }) => first + ' ' + last;
// 等同于
var person={ first, last };
function full(person) {
  return person.first + ' ' + person.last;
}

3.5.2箭头函数this对象指向

  • 普通函数this 的指向
    • js中的this是执行上下文,在普通函数中,this指向它的直接调用者
    • 在默认情况下(非严格模式),在全局环境下,js中的this指向window
  • 箭头函数的this指向
    • 箭头函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象。
    • 简单而言,箭头函数使用时,不绑定this对象,箭头函数没有自己的this,它的this是继承而来的,默认指向在定义箭头函数时所处的对象
    function foo() {
      return () => {
        return () => {
          return () => {
            console.log('id:', this.id);
          };
        };
      };
    }
    var f = foo.call({id: 1});        // 设置foo的id为1
    var t1 = f.call({id: 2})()();     // id: 1
    var t2 = f().call({id: 3})();     // id: 1
    var t3 = f()().call({id: 4});     // id: 1
    //★结果分析:因为所有的内层函数都是箭头函数,都没有自己的this,它们的this其实都是最外层foo函数的this。所以箭头函数的this指向是创建它所在的对象,不会改变。
    • 箭头函数有几个使用注意点:

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

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

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

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

3.5.3函数的默认值

  • ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法
  • ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。
  • 与解构赋值默认值结合使用
//ES6之前
function log(x, y) {
    y = y || 'aaa';        // 设置y的默认值为aaa
    console.log(x, y);
}

//ES6
function log(x, y = 'aaa') {
    console.log(x, y);
}

//对象的解构赋值
function foo({x, y = 5}) {
  console.log(x, y);
}
foo({})     // undefined, 5
foo({x: 1})     // 1, 5
foo({x: 1, y: 2})     // 1, 2
foo()     // 报错内容:TypeError: Cannot read property 'x' of undefined

3.5.4函数的length属性

  • 指定了默认值以后,函数的length属性,返回的是没有默认值的参数个数
  • 如果设置了默认值的参数不是尾参数(排在最右边),那么length属性也不再计入后面的参数了(就是说,以第一个默认参数位置开始计数,只计算第一个默认参数前边的个数,不计算后边的
(function (a) {}).length     // 1
(function (a = 5) {}).length     // 0
(function (a, b, c = 5) {}).length     // 2

(function (a = 0, b, c) {  }).length     // 0
(function (a, b = 1, c) {  }).length     // 1

3.5.5函数默认值的作用域

var x=1;    // 全局变量x
function f(x, y = x) { console.log(y);  }
f(5);   //5 
//★:结果说明:调用时,由于函数作用域内部的变量x已经生成,所以y等于参数x,而不是全局变量x。

let x = 1;        // 全局变量 x
function f(y = x) {
  let x = 2;        // 局部变量 x 
  console.log(y);
}
f() // 1
//★结果说明:函数调用时,y的默认值变量x尚未在函数内部生成,所以x指向全局变量。

3.5.6rest参数

  • rest参数(形式为“...变量名”),用于获取函数的多余参数
  • rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
  • rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。
  • 函数的length属性,不包括rest参数
function add(...values) {
  let sum = 0;
  for (var num of values) {
    sum += num;
  }
  return sum;
}
add(4, 5, 6,7)    // 22

3.5.7扩展运算符

  • 扩展运算符(spread)是三个点(...)
  • 它好比rest参数的逆运算将一个数组转为用逗号分隔的参数序列
  • 运算符主要用在函数调用
console.log( ...[1, 2, 3] )    // 1   2   3
console.log(  ...[1, 2, ...[1,2,5],3] )    // 1  2  1  2  5  3
console.log(  ...[1, 2, [1,2,5],3] )    // 1  2  [ 1, 2, 5 ]  3

function add(x, y) {
    return x + y;
}
var numbers = [4, 38];
add(...numbers)     // 42

3.5.8尾调用和尾递归

  • 尾调用(Tail Call)指某个函数最后一步调用另一个函数,它是函数式编程的一个重要概念。
  • 函数调用自身,称为递归。
  • 如果尾调用自身称为尾递归
//**尾调用**
function f(x){
    return g(x);
}

//递归**
function foo(n) {
  if (n === 1) return 1;
  return n * foo(n - 1);
}
foo(5)     // 120

猜你喜欢

转载自www.cnblogs.com/xuzhengguo/p/12036959.html