ES2015 一点点总结(1)

时间:2021-2-20 作者:admin

声明

  • const:声明一个常量,let:声明一个变量;const/let 声明的常量/变量都只能作用于代码块(块级作用域或函数作用域)里;
if (true) {
  let name = "布兰";
}
console.log(name); // undefined
  • const/let 不允许在同一个作用域内,重复声明;
function setName(name) {
  let name = ""; // SyntaxError
}
  • const 声明时必须初始化,且后期不能被修改,但如果初始化的是一个对象,那么不能修改的是该对象的内存地址;
const person = {
  name: "bbb",
};
person.name = "aaa";
console.log(person.name); // 'aaa'
person = ""; // TypeError
  • const/let 在全局作用域中声明的常量/变量不会挂到顶层对象(浏览器中是 window )的属性中;
var name = "bbb";
let age = 12;
console.log(window.name); // 'bbb'
console.log(window.age); // undefined

解构赋值

解构类型

  • 字符串解构
let [a, b, c = "c"] = "12";
console.log(a, b, c); // '1' '2' 'c'
  • 数组解构:等号右侧的数据具有 Iterator 接口可以进行数组形式的解构赋值;

    什么样的数据具有 Iterator 接口呢?如果一个对象能够通过 [Symbol.iterator] 访问,且能够返回一个符合迭代器协议的对象,那么该对象就是可迭代的。目前内置的可迭代对象有:String、Array、TypeArray、Map、Set、arguments 和 NodeList 等。

// 解构不成功的变量值为 undefined
let [a, b, c] = [1, 2];
console.log(a, b, c); // 1, 2, undefined

// 可以设置默认值
let [x, y, z = 3] = [1, 2, null];
console.log(x, y, z); // 1, 2, null
  • 对象解构:与数组按照索引位置进行解构不同,对象解构是按照属性名进行解构赋值,如果在当前对象属性匹配不成功则会去对象的原型属性上查找:
// 默认写法
let { name: name, age: age } = { name: "ab", age: 12 };
// 简写
let { name, age } = { name: "ab", age: 12 };
// 改名且设置默认值
let { name: name1, age: age1 = 12 } = { name: "ab" };
console.log(name1, age1); // 'ab' 12
  • 函数参数解构:其实就是运用上面的对象解构和数组解构规则;
function move({ x = 0, y = 0 } = {}) {
  console.log([x, y]);
  return [x, y];
}
move({ x: 3, y: 8 }); // [3, 8]
move({ x: 3 }); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

解构要点

  • 只要等号两边的模式相同(同是对象或同是数组),则左边的变量会被赋予对应的值;
  • 解构不成功的变量值为 undefined;
  • 默认值生效的前提是当等号右边对应的值全等于 undefined 的时候;
  • 只要等号右边的值不是对象或者数组,则会进行自动装箱将其转成对象;
  • null 和 undefined 都无法转成对象,所以无法解构。

解构应用

  • 交换变量的值
let x = 1,
  y = 2;
[x, y] = [y, x];
console.log(x, y); // 2 1
  • 通过函数返回对象属性
function getParams() {
  return {
    name: "布兰",
    age: 12,
  };
}
let { name, age } = getParams();
  • 通过定义函数参数来声明变量
let person = {
  name: "布兰",
  age: 12,
};
init(person);

// 普通用法
function init(person) {
  let { name, age } = person;
}

// 更简洁用法
function init({ name, age }) {}
  • 指定函数参数默认值
function initPerson({ name = "布兰", age = 12 } = {}) {
  console.log(name, age);
}
initPerson(); // '布兰' 12
initPerson({ age: 20 }); // '布兰' 20
  • 提取 JSON 数据
let responseData = {
  code: 1000,
  data: {},
  message: "success",
};
let { code, data = {} } = responseData;
  • 遍历 Map 结构
let map = new Map();
map.set("beijing", "北京");
map.set("xiamen", "厦门");

for (let [key, value] of map) {
  console.log(key, value);
}
  • 输入模块的指定方法和属性

const { readFile, writeFile } = require("fs")

字符串扩展

  • 可以使用 Unicode 编码来表示一个字符:
// 以下写法都可以用来表示字符 z
"\z"; // 转义
"\172"; // 十进制表示法
"\x7A"; // 十六进制表示法
"\u007A"; // Unicode 普通表示法
"\u{7A}"; // Unicode 大括号表示法

52unicode 这个网站可以查询到常见符号的 Unicode 编码。

  • 可以使用 for…of 正确遍历字符串:
let str = "??????";
for (const emoji of str) {
  console.log(emoji); // ??????
}
for (let i = 0, l = str.length; i < l; i++) {
  console.log(str[i]); // 不能正确输出表情
}
  • 模板字符串使用两个反引号标识(“),可以用来定义多行字符串,或者使用它在字符串中插入变量:
let name = "hero";
let tips = `Hello ${name},welcome to my world.`;
alert(tips);
  • 标签模板:在函数名后面接一个模板字符串相当于给函数传入了参数进行调用:
let name = "布兰",
  age = 12;
let tips = parse`Hello ${name}, are you ${age} years old this year?`;
function parse(stringArr, ...variable) {}

// 相当于传递如下参数进行调用 parse 函数
parse(["Hello ", ", are you ", " years old this year?"], name, age);
  • 字符串是否包含子串:

    • includes():返回布尔值,表示是否找到了参数字符串。
    • startsWith():返回布尔值,表示参数字符串是否在原字符串的头部。
    • endsWith():返回布尔值,表示参数字符串是否在原字符串的尾部。
let s = "Hello world!";

s.includes("o"); // true
s.startsWith("Hello"); // true
s.endsWith("!"); // true
  • 这三个方法都支持第二个参数,表示开始搜索的位置:
let s = "Hello world!";

s.includes("Hello", 6); // false
s.startsWith("world", 6); // true
s.endsWith("Hello", 5); // true

上面代码表示,使用第二个参数 n 时,endsWith 的行为与其他两个方法有所不同。它针对前 n 个字符,而其他两个方法针对从第 n 个位置直到字符串结束。

  • repeat(n) 将当前字符串重复 n 次后,返回一个新字符串:
"x".repeat(2); // 'xx'
"x".repeat(1.9); // 'x'
"x".repeat(NaN); // ''
"x".repeat(undefined); // ''
"x".repeat("2a"); // ''
"x".repeat(-0.6); // '',解释:0 ~ 1 之间的小数相当于 0
"x".repeat(-2); // RangeError
"x".repeat(Infinity); // RangeError

数组扩展

  • 数组扩展运算符(…)将数组展开成用逗号分隔的参数序列,只能展开一层数组:
// 应用一:函数传参
Math.max(...[1, 2, 3]); // 3

// 应用二:数组合并
let merge = [...[1, 2], ...[3, 4], 5, 6]; // 1, 2, 3, 4, 5, 6

// 应用三:浅克隆
let a = [1, 2, 3];
let clone = [...a];
a === clone; // false

// 应用四:数组解构
const [x, ...y] = [1, 2, 3];
x; // 1
y; // [2, 3]
  • Array.from() 可以将类数组对象( NodeList,arguments)和可迭代对象转成数组:
// 应用一:字符串转数
Array.from("foo"); // ['f', 'o', 'o']
// 应用二:数组合并去重
let merge = [...[1, 2], ...[2, 3]];
Array.from(new Set(merge)); // ['1', '2', '3']

// 应用三:arguments 转数组
function f() {
  return Array.from(arguments);
}
f(1, 2, 3); // [1, 2, 3]

如果 Array.from() 带第二个参数 mapFn,将对生成的新数组执行一次 map 操作:

Array.from([1, 2, 3], (x) => x * x); // [1, 4, 9]
Array.from({ length: 3 }, (v, i) => ++i); // [1, 2, 3]
  • Array.copyWithin() 在当前数组内部,将制定位置的成员复制到其他位置(会覆盖原来位置的成员),最后返回一个新数组。接收 3 个参数,参数为负数表示右边开始计算:

target(必选):替换位置的索引;
start(可选):从该位置开始读取数据,默认为 0;
end(可选):从该位置结束读取数据(不包括该位置的数据),默认为原数组长度;

[1, 2, 3, 4, 5]
  .copyWithin(-1) // [1, 2, 3, 4, 1]
  [(1, 2, 3, 4, 5)].copyWithin(1) // [1, 1, 2, 3, 4]
  [(1, 2, 3, 4, 5)].copyWithin(0, 3, 4) // [4, 2, 3, 4, 5]
  [(1, 2, 3, 4, 5)].copyWithin(0, -3, -1); // [3, 4, 3, 4, 5]
  • 查找第一个出现的子成员:find() 和 findIndex():
// 找出第一个偶数
[1, 6, 9].find((val, index, arr) => val % 2 === 0); // 6
// 找出第一个偶数的索引位置
[1, 6, 9].findIndex((val, index, arr) => val % 2 === 0); // 1
  • fill() 使用给定的值来填充数组,有 3 个参数:

value:填充值;
start(可选),开始索引,默认为 0;
end(可选):结束索引,默认为数组长度,不包括该索引位置的值;

// 初始化空数组
Array(3).fill(1); // [1, 1, 1]
[1, 2, 3, 4].fill("a", 2, 4); // [1, 2, 'a', 'a']
  • 通过 keys()(键名)、entries()(键值)和 values()(键值对) 获取数组迭代器对象,可以被 for…of 迭代,
let arr = ["a", "b", "c"];
for (let x of arr.keys()) {
  console.log(x); // 1, 2, 3
}
for (let v of arr.values()) {
  console.log(v); // 'a' 'b' 'c'
}
for (let e of arr.entries()) {
  console.log(e);
  // [0, 'a'] [0, 'b'] [0, 'c']
}

对象扩展

  • 对象属性简写
let name = "abc";
let person = {
  name,
  getName() {
    return this.name;
  },
};
// 等同于
let person1 = {
  name: "abc",
  getName: function () {
    return this.name;
  },
};
  • 属性名表达式:在用对象字面量定义对象的时候,允许通过属性名表达式来定义对象属性
let name = 'name',
let person = {
    [name]: '布兰',
    ['get'+ name](){
        return this.name
    }
}
  • 方法的 name 属性,存在好几种情况,这里仅列出常见的几种:

    • 情况一:普通对象方法的 name 属性直接返回方法名,函数声明亦是如此,函数表达式返回变量名:

      let person = {
        hi() {},
      };
      person.hi.name; // 'hi'
      
    • 情况二:构造函数的 name 为 anonymous:

      (new Function).name // 'anonymous'

    • 情况三:绑定函数的 name 将会在函数名前加上 bound:

      function foo() {}
      foo.bind({}).name; // 'bound foo'
      
    • 情况四:如果对象的方法使用了取值函数(getter)和存值函数(setter),则 name 属性不是在该方法上面,而是该方法的属性的描述对象的 get 和 set 属性上面:

      let o = {
        get foo() {},
        set foo(x) {},
      };
      o.foo.name; // TypeError: Cannot read property 'name' of undefined
      let descriptor = Object.getOwnPropertyDescriptor(o, "foo");
      descriptor.get.name; // "get foo"
      descriptor.set.name; // "set foo"
      
    • 属性的可枚举性

      性都有一个描述对象(Descriptor),用来控制该属性的行为。可以通过 Object.getOwnPropertyDescriptor() 来获取对象某个属性的描述:

      let person = { name: "布兰", age: 12 };
      Object.getOwnPropertyDescriptor(person, "name");
      // {
      //     configurable: true,
      //     enumerable: true,
      //     value: "布兰",
      //     writable: true,
      // }
      

      这里的 enumerable 就是对象某个属性的可枚举属性,如果某个属性的 enumerable 值为 false 则表示该属性不能被枚举,所以该属性会被如下 4 种操作忽略:

      • for…in :只遍历对象自身的和继承的可枚举的属性;
      • Object.keys():返回对象自身的所有可枚举的属性的键名;
      • JSON.stringify():只串行化对象自身的可枚举的属性;
      • Object.assign(): 只拷贝对象自身的可枚举的属性。
  • Object.is() 用来判断两个值是否相等,表现基本和 === 一样,除了以下两种情况:

+0 === -0; //true
NaN === NaN; // false
Object.is(+0, -0); // false
Object.is(NaN, NaN); // true

函数扩展

  • 函数参数默认值。
function printInfo(name = "布兰", age = 12) {}
  • 使用参数默认值的时候,参数不能有同名的:
function f(x, x, y) {} // 不报错
function f(x, x, y = 1) {} // 报错
  • 函数体内不能用 let 和 const 声明同参数名的变量:
// 报错
function f(x, y) {
  let x = 0;
}
  • 函数的 length 属性会返回没有指定默认值的参数个数,且如果设置默认值的参数不是尾参数,则 length 不再计入后面的参数:
(function f(x, y) {}.length); // 2
(function f(x, y = 1) {}.length); // 1
(function f(x = 1, y) {}.length); // 0
  • 剩余(rest) 参数(…变量名)的形式,用于获取函数的剩余参数,注意 rest 参数必须放在最后一个位置,可以很好的代替 arguments 对象:
function f(x, ...y) {
  console.log(x); // 1
  for (let val of y) {
    console.log(val); // 2 3
  }
}
f(1, 2, 3);
  • 箭头函数语法比函数表达式更简洁,并且没有自己的 this、arguments,不能用作构造函数和用作生成器。几种箭头函数写法:
let f1 = () => {}; // 没有参数
let f2 = (x) => {}; // 1个参数
let f3 = (x) => {}; // 1个参数可以省略圆括号
let f4 = (x, y) => {}; // 2个参数以上必须加上圆括号
let f5 = (x = 1, y = 2) => {}; // 支持参数默认值
let f6 = (x, ...y) => {}; // 支持 rest 参数
let f7 = ({ x = 1, y = 2 } = {}) => {}; // 支持参数解构

箭头函数没有自己的 this;

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。