ES6 课程概述②

更好的 Unicode 支持

早期,由于存储空间宝贵,Unicode 使用 16 位二进制来存储文字。我们将一个 16 位的二进制编码叫做一个码元(Code Unit)。

后来,由于技术的发展,Unicode 对文字编码进行了扩展,将某些文字扩展到了 32 位(占用两个码元),并且,将某个文字对应的二进制数字叫做码点(Code Point)。

ES6 为了解决这个困扰,为字符串提供了方法:codePointAt,根据字符串码元的位置得到其码点。

同时,ES6 为正则表达式添加了一个 flag: u,如果添加了该配置,则匹配时,使用码点匹配

更多的字符串 API

以下均为字符串的实例(原型)方法

  • includes

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

  • startsWith

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

  • endsWith

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

  • repeat

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

3-3. [扩展]正则中的粘连标记

标记名:y

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

模板字符串

ES6 之前处理字符串繁琐的两个方面:

  1. 多行字符串
  2. 字符串拼接

在 ES6 中,提供了模板字符串的书写,可以非常方便的换行和拼接,要做的,仅仅是将字符串的开始或结尾改为反引号 ` 符号

如果要在字符串中拼接 js 表达式,只需要在模板字符串中使用${JS表达式}

var love1 = "秋葵";
var love2 = "香菜";

var text = `邓哥喜欢${love1}
邓哥也喜欢${love2}
表达式可以是任何有意义的数据${1 + 3 * 2 / 0.5}
表达式是可以嵌套的:${`表达式中的模板字符串${love1 + love2}`}
\n\n
奥布瓦的发顺丰
在模板字符串中使用\${JS表达式}可以进行插值
`;

console.log(text);

3-5. [扩展]模板字符串标记

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

标记名`模板字符串`;
var love1 = "秋葵";
var love2 = "香菜";

var text = myTag`邓哥喜欢${love1},邓哥也喜欢${love2}`;

//相当于:
// var text = myTag(["邓哥喜欢", ",邓哥也喜欢", "。"], "秋葵", "香菜")

function myTag(parts) {
  console.log(parts);
}

console.log(text);

在这里插入图片描述

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

  1. 参数 1:被插值分割的字符串数组
  2. 后续参数:所有的插值

模板字符串的实现原理:

var love1 = "秋葵";
var love2 = "香菜";

var text = myTag`邓哥喜欢${love1},邓哥也喜欢${love2}`;

function myTag(parts) {
  const values = Array.prototype.slice.apply(arguments).slice(1);
  // console.log(values);
  let str = "";
  for (let i = 0; i < values.length; i++) {
    str += `${parts[i]}${values[i]}`;
    if (i === values.length - 1) {
      str += parts[i + 1];
    }
  }
  return str;
}

3-6 模板字符串之中还能调用函数。

function fn() {
  return "Hello World";
}

`foo ${fn()} bar`
// foo Hello World bar

3-7 模板字符串甚至还能嵌套。

const tmpl = addrs => `
  <table>
  ${addrs.map(addr => `
    <tr><td>${addr.first}</td></tr>
    <tr><td>${addr.last}</td></tr>
  `).join('')}
  </table>
`;

4-1. 参数默认值

使用

在书写形参时,直接给形参赋值,附的值即为默认值

这样一来,当调用函数时,如果没有给对应的参数赋值(给它的值是 undefined),则会自动使用默认值。

function Point(x = 0, y = 0) {
  this.x = x;
  this.y = y;
}

const p = new Point();
p // { x: 0, y: 0 }

参数变量是默认声明的,所以不能用letconst再次声明。

function foo(x = 5) {
  let x = 1; // error
  const x = 2; // error
}

另外,一个容易忽略的地方是,参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。

let x = 99;
function foo(p = x + 1) {
  console.log(p);
}

foo() // 100

x = 100;
foo() // 101
上面代码中,参数p的默认值是x + 1。这时,每次调用函数foo,都会重新计算x + 1,而不是默认p等于 100。

与解构赋值默认值结合使用
参数默认值可以与解构赋值的默认值,结合起来使用。

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

[扩展]对 arguments 的影响

只要给函数加上参数默认值,该函数会自动变量严格模式下的规则:arguments 和形参脱离

[扩展]留意暂时性死区

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

4-2. 剩余参数

arguments 的缺陷:

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

ES6 的剩余参数专门用于收集末尾的所有参数,将其放置到一个形参数组中。

语法:

function (...形参名){

}

细节:

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

4-3. 展开运算符

使用方式:...要展开的东西

在这里插入图片描述

/**
 * 对所有数字求和
 * @param  {...any} args
 */
function sum(...args) {
  let sum = 0;
  for (let i = 0; i < args.length; i++) {
    sum += args[i];
  }
  return sum;
}

/**
 * 获取一个指定长度的随机数组成的数组
 * @param {*} length
 */
function getRandomNumbers(length) {
  const arr = [];
  for (let i = 0; i < length; i++) {
    arr.push(Math.random());
  }
  return arr;
}

const numbers = getRandomNumbers(10);
//将数组的每一项展开,依次作为参数传递,而不是把整个数组作为一个参数传递
// sum(numbers)

console.log(sum(...numbers)); //相当于传递了10个参数
console.log(sum(1, 3, ...numbers, 3, 5));

对数组展开 ES6

const arr1 = [3, 67, 8, 5];

//克隆arr1数组到arr2

const arr2 = [0, ...arr1, 1];

console.log(arr2, arr1 === arr2);
//  [0, 3, 67, 8, 5, 1] false

对对象展开 ES7

const obj1 = {
  name: "成哥",
  age: 18,
  love: "邓嫂",
  address: {
    country: "中国",
    province: "黑龙江",
    city: "哈尔滨",
  },
};

// 浅克隆到obj2

const obj2 = {
  ...obj1,
  name: "邓哥",
};

console.log(obj2);

console.log(obj1.address === obj2.address); //true
// {name: '邓哥', age: 18, love: '邓嫂', address: {…}}address: {country: '中国', province: '黑龙江', city: '哈尔滨'}age: 18love: "邓嫂"name: "邓哥"[[Prototype]]: Object

// true

函数柯里化

function cal(a, b, c, d) {
  return a + b * c - d;
}
//curry:柯里化,用户固定某个函数的前面的参数,得到一个新的函数,新的函数调用时,接收剩余的参数
function curry(func, ...args) {
  //第一个参数是函数,第二个参数是乘余参数
  return function (...subArgs) {
    //返回一个函数(高阶函数),接收乘余参数
    const allArgs = [...args, ...subArgs]; //拼接参数
    if (allArgs.length >= func.length) {
      //判断参数与函数参数个数
      return func(...allArgs); //参数够了,执行函数
    } else {
      //参数不够,继续固定
      return curry(func, ...allArgs);
    }
  };
}

const newCal = curry(cal, 1, 2);

console.log(newCal(3, 4)); // 1+2*3-4
console.log(newCal(4, 5)); // 1+2*4-5
console.log(newCal(5, 6)); // 1+2*5-6
console.log(newCal(6, 7)); // 1+2*6-7

4-5. 明确函数的双重用途

ES6 提供了一个特殊的 API,可以使用该 API 在函数内部,判断该函数是否使用了 new 来调用

new.target
//该表达式,得到的是:如果没有使用new来调用函数,则返回undefined
//如果使用new调用函数,则得到的是new关键字后面的函数本身

function Person(firstName, lastName) {
    //判断是否是使用new的方式来调用的函数

    // //过去的判断方式
    // if (!(this instanceof Person)) {
    //     throw new Error("该函数没有使用new来调用")
    // }

    if (new.target === undefined) {
        throw new Error("该函数没有使用new来调用")
    }
    this.firstName = firstName;
    this.lastName = lastName;
    this.fullName = `${firstName} ${lastName}`;
}

const p1 = new Person("袁", "进");
console.log(p1)

4-6. 箭头函数

回顾:this 指向

  1. 通过对象调用函数,this 指向对象
  2. 直接调用函数,this 指向全局对象
  3. 如果通过 new 调用函数,this 指向新创建的对象
  4. 如果通过 apply、call、bind 调用函数,this 指向指定的数据
  5. 如果是 DOM 事件函数,this 指向事件源

使用语法

箭头函数是一个函数表达式,理论上,任何使用函数表达式的场景都可以使用箭头函数

完整语法:

(参数1, 参数2, ...)=>{
    //函数体
}

如果参数只有一个,可以省略小括号

(参数) => {};

const sum = (a, b) => ({
  a: a,
  b: b,
  sum: a + b,
});

console.log(sum(3, 5));

如果箭

注意细节

  • 箭头函数的函数体中的 this,取决于箭头函数定义的位置的 this 指向,而与如何调用无关
  • 箭头函数中,不存在 this、arguments、new.target,如果使用了,则使用的是函数外层的对应的 this、arguments、new.target
  • 箭头函数没有原型
  • 箭头函数不能作用构造函数使用

应用场景

  1. 临时性使用的函数,并不会可以调用它,比如:
    1. 事件处理函数
    2. 异步处理函数
    3. 其他临时性的函数
  2. 为了绑定外层 this 的函数
  3. 在不影响其他代码的情况下,保持代码的简洁,最常见的,数组方法中的回调函数
const obj = {
  count: 0,
  start: function () {
    setInterval(() => {
      this.count++;
      console.log(this);
    }, 1000);
  },
  regEvent: function () {
    window.onclick = () => {
      console.log(this);
    };
  },
  // 这里this不用解决
  print: function () {
    console.log(this);
    console.log(this.count);
  },
};

// 在函数里面用函数出现this指向问题
obj.start();
obj.regEvent();
obj.print();

在这里插入图片描述

数组的扩展

  • 扩展运算符
  • Array.from()
  • Array.of()
  • 数组实例的 copyWithin()
  • 数组实例的 find() 和 findIndex()
  • 数组实例的 fill()
  • 数组实例的 entries(),keys() 和 values()
  • 数组实例的 includes()
  • 数组实例的 flat(),flatMap()
  • 数组的空位
  • Array.prototype.sort() 的排序稳定性

含义

扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。

console.log(...[1, 2, 3])
// 1 2 3

console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5

注意,只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错。

(...[1, 2])
// Uncaught SyntaxError: Unexpected number

console.log((...[1, 2]))
// Uncaught SyntaxError: Unexpected number

console.log(...[1, 2])
// 1 2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值