进阶大神之路 之 【ES6常用语法】

89 阅读6分钟

数组解构

// 两边相同模式。即可实现解构
// 按照一定顺序解构
const [a, b, [c]] = [1, 2, [3]];
console.log(a, b, c); // 1, 2, 3
// 解构不成功,变量值为 undefined;
const [s, d, w] = [1, 2];
console.log(s, d, w); // 1 2 undefined
// 只要变量具备 迭代器(Iterator)接口,就可以实现数据解构
const [x, y, z] = new Set([4, 5, 6]);
console.log(x, y, z); // 4, 5, 6
// 解构赋值允许指定默认值
const [name = "zhangsan"] = [];
console.log("name:", name); // zhangsan
const [name2 = "zhangsan"] = ["李四"];
console.log("name2:", name2); // 李四

对象解构

// 没有顺序,必须与属性同名,才能取到正确的值
// 变量没有对应属性名,不影响取值,取值结果为 undefined
let { foo, bar, baz } = { foo: "aaa", bar: "bbb" };
console.log("bar", bar); // bbb
console.log("foo", foo); // aaa
console.log("baz", baz); // undefined

// 变量名和属性名不一致,必须写成
// 内部机制:先找到同名属性,再赋给对应变量
const { foo: fooName } = { foo: "aaa", bar: "bbb" };
console.log("fooName:", fooName); // aaa

const { name: n, age: a } = { name: "张三", age: 16 };
console.log("名字:", n); // 张三
console.log("年龄:", a); // 16

// 指定默认值
var { x = 3 } = {};
console.log(x); // 3

var { x: y = 3 } = { x: 5 };
console.log(y); // 5

// 注意点
// 1、已经声明的变量用于解构,会报错
let x;
{ x } = { x: 1 } // error
({ x } = { x :1 })
console.log(x); // 1

// 2、解构赋值,允许等号左边的模式之中,不放置任何变量名。因此,可以写出下面的赋值表达式
({} = [true, false])
({} = "abc")
({} = [])

// 3、由于数组本质是特殊的对象,因此可以对数组进行对象属性的解构
let arr = [1, 2, 3]
let { 0: first, [arr.length - 1]: last } = arr
console.log(first) // 1
console.log(last) // 3

模版字符串

// 普通字符串
const name = "张三";

// es5
console.log("我的名字叫:" + name); // 我的名字叫张三
// es6
console.log(`我的名字叫:${name}`); // 我的名字叫张三
// es6 换行
console.log(`我的名字叫:
${name}`);
// 我的名字叫:
// 张三

函数参数的默认值

基本用法

  • es6 以前,不能直接给函数参数指定默认值,通常这么做:
function log(x, y) {
  y = y || "world";
  console.log(x, y);
}

log("hello"); // hello world
log("hello", "china"); // hello china
log("hello", ""); // hello world
  • es6 允许为函数的参数设置默认值,即直接写在参数定义的后面
function log(x, y = "world") {
  console.log(x, y);
}
log("Hello"); // Hello World
log("Hello", "China"); // Hello China
log("Hello", ""); // Hello
  • es6 写法的好处
    • 可读性:直观的看出哪些参数是缺省的
    • 健壮性:利于将来代码优化,即使未来版本在对外接口中,彻底拿掉换个参数,也不会导致以前代码无法运行

函数 - rest 参数(...变量名)

  • 用户获取函数的多余参数,就不需要使用 arguments 对象了
  • rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中
function add(...values) {
  let sum = 0;
  console.log(values); // [1, 2, 3]
  for (var val of values) {
    sum += val;
  }
  return sum;
}

console.log(add(1, 2, 3)); // 6
  • arguments 写法
function add() {
  let sum = 0;
  console.log(arguments); // Arguments(3) [1, 2, 3, callee: (...), Symbol(Symbol.iterator): ƒ]
  console.log(Array.prototype.slice.call(arguments).sort()); // [1, 2, 3]
  const values = Array.prototype.slice.call(arguments).sort();
  for (var val of values) {
    sum += val;
  }
  return sum;
}

console.log(add(1, 2, 3)); // 6
  • 对比

    • arguments 对象是一个类似数组的对象,使用 Array.prototype.slice.call 先将其转为数组。
    • rest 参数不存在这个问题,得到的参数就是一个真正的数组,数组特有的方法都可以使用
    • rest 参数后面不能有其他参数(只能是最后一个参数),否则会报错
    • 函数 length 属性,不包括 rest 参数
    function test(a) {}
    console.log("-----", test.length); // 1
    function test1(...a) {}
    console.log("-----", test1.length); // 0
    function test2(a, ...b) {}
    console.log("-----", test2.length); // 1
    

箭头函数

  • 定义函数用 箭头
// es5
function foo() {}

// es6
const foo = () => {};
const f = (v) => v;

// 等同于===

const f = function (v) {
  return v;
};
  • 如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分
var f = () => 5;
// 等同于
var f = function () {
  return 5;
};

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function (num1, num2) {
  return num1 + num2;
};
  • 使用注意

    • 不可以作为构造函数使用,不能使用 new 命令
    • 没有原型
    • 不可以使用 arguments,要用 rest 参数代替
    • 不可以使用 yield 关键字
const test = (...rest) => {
  console.log(rest); // [1, 2, 3]
};
test(1, 2, 3);
const test = () => {};
const t = new test();
console.log(t); // TypeError: test is not a constructor
const test = () => {};
console.log(test.prototype); // undefined

function Test() {}
console.log("Test:", Test.prototype); // {constructor: ...}
  • this

    • 箭头函数没有 this
    • 箭头函数的 this 对象,就是定义时所在的对象,而不是使用时所在的对象
    • this 对象是固定的,父级作用域
    function foo() {
      setTimeout(() => {
        // 箭头函数的定义生消灾 foo 函数生成时
        // 在100毫秒之后真正执行
        console.log("id:", this); // { id: 42 }
        console.log("id:", this.id); // 42
      }, 100);
    }
    var id = 21;
    foo.call({ id: 42 }); // 给定this的值为 { id: 42 }
    // id: 42
    
    function foo() {
      // 普通函数,this指向window
      setTimeout(function () {
        console.log("id:", this); // Window
        console.log("id:", this.id); // 21
      }, 100);
    }
    var id = 21;
    foo.call({ id: 42 });
    

对象新增方法

  • Object.is() 比较两个值是否严格相等,与 === 的行为基本一致
console.log(Object.is("", "")); // true
console.log(Object.is("name", "name")); // true
console.log(Object.is(1, 3)); // false
console.log(Object.is({}, {})); // false
console.log(Object.is([], [])); // false
console.log(Object.is(+0, -0)); // false
console.log(Object.is(NaN, NaN)); // true
// es5 实现 Object.is()

Object.defineProperty(Object, "is", {
  value: function (x, y) {
    if (x === y) {
      // 针对 +0 不等于 -0 的情况
      return x != 0 || 1 / x === 1 / y;
    }

    // 针对 NaN 的情况
    return x !== x && y !== y;
  },
  configurable: true,
  enumerable: false,
  writable: true,
});
  • Object.assign() 用于对象的合并,将源对象的所有可枚举属性,复制到目标对象;
    • 实现浅拷贝
    • 同名属性,后面合并的属性替换掉先合并的属性
    • 若是处理数组,数组会被视为对象
    • 如果要复制的值是一个取值函数,那么将求值后再复制
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: { x: 1 } };

Object.assign(target, source1, source2);
console.log(target); // {a: 1, b: 2, c: { x:1 }}}
target.c.x = 6;
console.log("target", target); // {a: 1, b: 2, c: { x:6 }}}
console.log("source2", source2); // {x: {x: 6 }}
const arr1 = [1, 2, 3];
const arr2 = [4, 5];
Object.assign(arr1, arr2);
console.log(arr1); // [4, 5, 3] 把索引0由1替换成4,索引1由2替换成5
const source = {
  get foo() {
    return 1;
  },
};
const target = {};

Object.assign(target, source);
console.log(target); // {foo: 1}

// source 对象的 foo 属性是一个取值函数,Object.assign 不会复制这个取值函数,只会拿到值以后,将这个值复制过去
  • Object.getOwnPropertyDescriptors() 返回指定对象所有自身属性(非继承属性)的描述对象

    • es5 的 Object.getOwnPropertyDescriptor() 返回某个对象属性的描述对象
  • Object.keys()、 Object.values()、Object.entries()

    • Object.keys() ES5 引入方法,返回一个数组,成员时参数对象自身的所有可遍历属性的键名
    • Object.values() ES2017 引入 返回一个数组,可遍历属性的键值
    • Object.entries() ES2017 引入 返回一个数组,成员是参数对象自身所有可遍历属性的键值对数组
let { keys, values, entries } = Object;
let obj = { a: 1, b: 2, c: 3 };

console.log(keys(obj)); // ["a", "b", "c"]
console.log(values(obj)); // [1, 2, 3]
console.log(entries(obj)); // [ [a, 1], [b, 2], [c, 3]]
  • Object.fromEntries() 将一个键值对数组转为对象
const obj = Object.fromEntries([
  ["foo", "bar"],
  ["baz", 42],
]);
console.log("obj:", obj); // {foo: 'bar', baz: 42}
// 该方法主要目的是将键值对的数据解构还原为对象,因此特别适合将 Map 结构转为对象

const map1 = new Map([
  ["foo", "bar"],
  ["baz", 42],
]);

console.log("map1:", Object.fromEntries(map1)); // {foo: 'bar', baz: 42}

const map2 = new Map().set("foo", true).set("bar", false);
console.log("map2:", Object.fromEntries(map2)); // {foo: true, bar: false}
// 配合 URLSearchParams 对象,将查询字符串转为对象

const params = Object.fromEntries(new URLSearchParams("foo=bar&baz=qux"));
console.log("params:", params); // {foo: 'bar', baz: 'qux'}