Vinson

Vinson

ES6

发表于 2023-08-12
Vinson
阅读量 45

JavaScript ES6 let、cont、解构

let 和 const

  1. 遇到 {} 就形成作用域
  2. 同一作用域不能重复声明变量或函数 [如:let 声明过不能用 const 和 var 声明相同名字]
  3. 没有变量提升
  4. const 必须初始化赋值,不能被修改,而 let 声明的变量不用
  5. const 定义的对象或者数组,其实是可变的。
  • 可以修改添加属性
  const car = {color:"white"};
  car.color = "red";// 修改属性:
  car.owner = "Johnson";// 添加属性
js
  • 不能赋值
  const car = {};
  car = {};  // 错误
js

数组同理

  1. 全局作用域 var 属于 window 对象。let 不属于 window 对象。
  2. let 有暂时性死区、要先声明再使用。

解构

[模式] = [数组];

{模式} = {对象};

  • 完全解构
  • 不完全解构 [,可以被忽略 为赋值成功为 undefined ]
  • 嵌套
  • 默认值 , undefined 不会被赋值,null 可以被赋值
    数组解构
let [, [x, y], m = 88,...z] = [2, [3, 4], undefined,1,2]; // m 为 88
js
  1. , 会忽略
  2. [] 可以嵌套
  3. undefined 不会赋值上去, m = 88 默认值,null 会被赋值
  4. …z 剩余运算符 收集对象/数组/值 或 展开数组/对象/值
let [x,...arr] = [1,2,3]; // arr = [2,3]
let {a,...obj} = {a : 'aa', b : "bb" , "c" : "cc"};// obj = {b : "bb" , "c" : "cc"}
js
  • 字符串
let [a, b, c] = 'hel'; // 'h' 'e' 'l'
js
  • 对象
// 剩余运算符
let {a, ...obj} = {a: 10, c: 30, d: 40}; // 10,{c: 30, d: 40}
js
  • 解构默认值
let {a = 10, b = 5} = {a: 3}; // a = 3; b = 5;

({a} = {a: '1'}) // 变量已经被声明需要用括
js
  • 对象 = 数组
let {0:first, [arr.length-1]: last} = [1,2,3,4,5,6,7]

console.log(first);// 输出1
console.log(last);// 输出7
js

运用

var [x,y] = [10,20]; // 交换值
[x,y] = [y,x];

// 函数可以返回多个值
var [x,y,z] = show();
function show(){
  return ["结果1","结果2","结果3"]
}
js
  • 设置默认值,可以改变传入参数的顺序
function showSelf({name,age = 18}){// 可以
  console.log(`我叫${name}今年${age}`); // 我叫aa 今年18
}
showSelf({name: "aa"})

// 取出数组中的值
var {0:first,3:last} = [10,20,30]; // first等于10 last是30
js

JavaScript ES6 Object 对象

简写

对象简写

const age = 12;
const name = "Amy";
const person = {age, name}; // {age: 12, name: "Amy"}
// 等同于 person = {age: age, name: name}
js

方法简写

const person = {
 sayHi(){
  console.log("Hi");
 }
}
js

Generator 函数,要在前面加星号

const obj = {
 * myGenerator() {
  yield 'hello world';
 }
};

属性名表达式

  • [变量]
let a="aaa";
let obj={
  [a]: "bbb"
}

const obj = {
 ["he"+"llo"](){
  return "Hi";
 }
}
obj.hello(); // "Hi"
js

合并对象

let age = {age: 15};
let name = {name: "Amy"};
let person = {...age, ...name}; // {age: 15, name: "Amy"}
js

注意
如果两个对象有同名的属性会被覆盖(数组也是同理)

Object.assign()

Object.assign(target, source_1, ···) 
js

将后面所有可枚举的属性赋值到target对象中。重复的值会覆盖

let target = {a: 1};
let object2 = {b: 2};
let object3 = {c: 3};
Object.assign(target,object2,object3); // {a: 1, b: 2, c: 3}
// 第一个参数是目标对象,后面的参数是源对象
js

只有一个参数不是对象,也会转换为对象返回

Object.assign(3); // Number {3}
js

第一个参数时 null 或 undefined 会报错

Object.assign(null); // TypeError: Cannot ...
js

null 和 undefined 放第二个之后会跳过

Object.assign(1,undefined); // Number {1}
// 注意: assign 的属性拷贝是浅拷贝
let sourceObj = { a: { b: 1}};
let targetObj = {c: 3};
Object.assign(targetObj, sourceObj);
targetObj.a.b = 2; // 原始值修改
sourceObj.a.b; // 2 已经拷贝的值也会跟着变
js

同名属性会被替换

targetObj = { a: { b: 1, c:2}};
sourceObj = { a: { b: "hh"}};
Object.assign(targetObj, sourceObj);// {a: {b: "hh"}}
js

数组的处理

Object.assign([2,3], [5]); // [5,3] 下标0被覆盖
js

链判断运算符

const firstName = message?.body?.user?.firstName || 'default';
const fooValue = myForm.querySelector('input[name=foo]')?.value
// 如果左侧的值为 null或undefined 则不再往下运算
iterator.return?.() // 判断方法是否存在
js

三种用法

a?.b
// 等同于
a == null ? undefined : a.b

a?.[x]
// 等同于
a == null ? undefined : a[x]

a?.b()
// 等同于
a == null ? undefined : a.b()

a?.()
// 等同于
a == null ? undefined : a()
js

JavaScript ES6 Symbol 对象

符号本身是原始类型,所以typeof 操作符对符号返回symbol。

let sym = Symbol();
console.log(typeof sym); // symbol
js

声明

  1. 原始数据类型,不能使用new
  2. 括号内是备注的意思,独一无二的值,两个 Symbol 不相等
  3. typeof 检测为 Symbol
  4. 作为对象的键名独一无二、使用 [] 设置/获取,不能用.运算符获取设置
  5. 作为对象属性名是,是共有属性,不是私有属性,可以在类的外部访问
  6. 不能被枚举 for in、for of、Object.keys()、Object.getOwnPropertyNames() 返回
  7. 可以被 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到
let sy = Symbol('name');// 不能用 new 命令,参数是备注
Symbol("KK") == Symbol("KK") // false
typeof Symbol("KK"); // 'symbol'
js

作为属性名

let sy = Symbol('name');
var obj = {}; // 设置symbol属性
obj[sy] = 'kk'; 

// 获取
obj.sy // undefined 不能使用.运算符获取
obj[sy] // kk
js

为了避免创建符号包装对象,Symbol()函数不能与new 关键字一起作为构造函数使用。

let myBoolean = new Boolean();
console.log(typeof myBoolean); // "object"

let myString = new String();
console.log(typeof myString); // "object"

let myNumber = new Number();
console.log(typeof myNumber); // "object"

let mySymbol = new Symbol(); // TypeError: Symbol is not a constructor
js

如果要包装对象

let mySymbol = Symbol();
let myWrappedSymbol = Object(mySymbol);
console.log(typeof myWrappedSymbol); // "object"
js

全局符号注册表 第一次调用会检查全局的注册表,不存在则生成新的符号添加到注册表,存在则会直接返回对于的。

let fooGlobalSymbol = Symbol.for('foo'); // 创建新符号
let otherFooGlobalSymbol = Symbol.for('foo'); // 重用已有符号
js

与Symbol()定义的符号也并不等同

let localSymbol = Symbol('foo');
let globalSymbol = Symbol.for('foo');
console.log(localSymbol === globalSymbol); // false
js

Symbol.for()的任何值都会被转换为字符串

let emptyGlobalSymbol = Symbol.for();
console.log(emptyGlobalSymbol); // Symbol(undefined)
js

Symbol.keyFor()来查询全局注册表

// 创建全局符号
let s = Symbol.for('foo');
console.log(Symbol.keyFor(s)); // foo
// 创建普通符号
let s2 = Symbol('bar');
console.log(Symbol.keyFor(s2)); // undefined
// 如果传给Symbol.keyFor()的不是符号,则该方法抛出TypeError:
Symbol.keyFor(123); // TypeError: 123 is not a symbol
js

计算属性语法中使用符号作为属性:

let s1 = Symbol('foo'),
  s2 = Symbol('bar'),
  s3 = Symbol('baz'),
  s4 = Symbol('qux');
Object.defineProperties({
  [s1]: 'foo val',
  
}, {
[s3]: {value: 'baz val'},
[s4]: {value: 'qux val'}
});
console.log(o);
// {Symbol(foo): foo val, Symbol(bar): bar val,
// Symbol(baz): baz val, Symbol(qux): qux val}
js

如果没有保存Symbol的引用,需要遍历才能找到:

let o = {
  [Symbol('foo')]: 'foo val',
  [Symbol('bar')]: 'bar val'
};
console.log(o);
// {Symbol(foo): "foo val", Symbol(bar): "bar val"}
let barSymbol = Object.getOwnPropertySymbols(o)
.find((symbol) => symbol.toString().match(/bar/));
console.log(barSymbol);
// Symbol(bar)
js

Symbol.for()

var sy = Symbol('a');
console.log(Symbol.for('a'));; // Symbol(a)
js
  1. 全局搜索是否存在该名称
  2. 有:返回本身,没有:新建一个返回
  3. 使两个 Symbol 类型的变量相等,生成同一个 Hash 值
Symbol("Yellow") === Symbol.for("Yellow");   // false
Symbol.for("Yellow") === Symbol.for("Yellow");   // true
js

Symbol.keyFor(sym)

  1. 查找键值的某个 Symbol
  2. 找到返回该 Symbol 的 key 值,字符串类型。否则 undefined
var globalSym = Symbol.for("foo");// 创建一个全局 Symbol
Symbol.keyFor(globalSym); // "foo"

var localSym = Symbol();
Symbol.keyFor(localSym); // undefined,

// 以下Symbol不是保存在全局Symbol注册表中
Symbol.keyFor(Symbol.iterator) // undefined
js

Symbol.asyncIterator

该方法返回对象默认的 AsyncIterator 由 for-await-of 语句使用。

class Foo {
  async *[Symbol.asyncIterator]() {}
}
let f = new Foo();
console.log(f[Symbol.asyncIterator]());
// AsyncGenerator {<suspended>}
js

技术上来说,由Symbol.asyncIterator 函数生成的对象应该通过next()方法陆续返回 Promise 实例。

也可以隐式通过异步生成器函数返回:

class Emitter {
  constructor(max) {
    this.max = max;
    this.asyncIdx = 0;
  }
  async *[Symbol.asyncIterator]() {
    while(this.asyncIdx < this.max) {
      yield new Promise((resolve) => resolve(this.asyncIdx++));
    }
  }
}
async function asyncCount() {
  let emitter = new Emitter(5);
  for await(const x of emitter) { // next 调用后的 value
    console.log(x);
  }
}
asyncCount();
// 0
// 1
// 2
// 3
// 4
js

Symbol.hasInstance

ECMAScript 规范,这个符号作为一个属性表示“一个方法,传入对象返回该对象是否是它的实例。

instanceof 操作符可以用来确定一个对象实例的原型链上是否有原型。

class Bar {}
let b = new Bar();
console.log(b instanceof Bar); // true
js

ES6 中,instanceof 操作符会使用Symbol.hasInstance 函数来确定关系

class Bar {}
let b = new Bar();
console.log(Bar[Symbol.hasInstance](b)); // true
js

Baz 继承了 Bar 并覆写了 Symbol.hasInstance,instanceof 操作符也会在原型链上寻找这个属性,所以说就就跟在原型链上寻找其他属性一样,这里重新定义了这个函数

class Bar {}
class Baz extends Bar {
  static [Symbol.hasInstance]() {
    return false;
  }
}
let b = new Baz();
console.log(Bar[Symbol.hasInstance](b)); // true
console.log(b instanceof Bar); // true
console.log(Baz[Symbol.hasInstance](b)); // false 调用 Baz 覆写后的 Symbol.hasInstance
console.log(b instanceof Baz); // false 同上,返回的值是转换为 Boolean
js

Symbol.isConcatSpreadable

  1. ECMAScript 规范,这个符号作为一个属性表示“一个布尔值”
  2. 默认情况下则对象应该用Array.prototype.concat()展开数组元素
  3. 如果是true,则被忽略
let initial = ['foo'];
let array = ['bar'];
console.log(array[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(array)); // ['foo', 'bar']
array[Symbol.isConcatSpreadable] = false;
console.log(initial.concat(array)); // ['foo', Array(1)]

let arrayLikeObject = { length: 1, 0: 'baz' };
console.log(arrayLikeObject[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(arrayLikeObject)); // ['foo', {...}]
arrayLikeObject[Symbol.isConcatSpreadable] = true;
console.log(initial.concat(arrayLikeObject)); // ['foo', 'baz']

let otherObject = new Set().add('qux');
console.log(otherObject[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(otherObject)); // ['foo', Set(1)]
otherObject[Symbol.isConcatSpreadable] = true;
console.log(initial.concat(otherObject)); // ['foo']
js

Symbol.iterator

  1. ECMAScript 规范,这个符号作为一个属性表示“一个方法,该方法返回对象默认的迭代器,由 for-of 语句使用”
  2. for-of 循环时,会调用以 Symbol.iterator 为键的函数,并默认这个函数会返回一个实现迭代器API 的对象。
  3. 很多时候,返回的对象是实现该 API 的 Generator:
class Foo {
  *[Symbol.iterator]() {}
}
let f = new Foo();
console.log(f[Symbol.iterator]());
// Generator {<suspended>}
js

Symbol.iterator 返回的对象可以通过next()方法陆续返回值。也可以隐式地通过生成器函数返回:

class Emitter {
  constructor(max) {
    this.max = max;
    this.idx = 0;
  }
  *[Symbol.iterator]() {
    while(this.idx < this.max) {
      yield this.idx++;
    }
  }
}
function count() {
  let emitter = new Emitter(5);
  for (const x of emitter) {
    console.log(x);
  }
}
count();
// 0
// 1
// 2
// 3
// 4
js

Symbol.match

  1. ECMAScript 规范,这个符号作为一个属性表示“一个正则表达式方法,该方法用正则表达式去匹配字符串。由String.prototype.match()方法使用”
  2. String.prototype.match() 会调用以 Symbol.match 为键的函数来正则求值
  3. 正则表达式的原型上就有 Symbol.match 这个函数
console.log(RegExp.prototype[Symbol.match]);
// ƒ [Symbol.match]() { [native code] }
console.log('foobar'.match(/bar/));
// ["bar", index: 3, input: "foobar", groups: undefined]
js

match 方法传入非正则表达式会被转为 RegExp 对象,如果要改变默认行为,需要重新定义 Symbol.match 函数。

Symbol.match接收一个参数,就是调用 match() 方法之前的字符串实例。

class FooMatcher {
  static [Symbol.match](target) {
    return target.includes('foo');
  }
}
console.log('foobar'.match(FooMatcher)); // true
console.log('barbaz'.match(FooMatcher)); // false
class StringMatcher {
  constructor(str) {
    this.str = str;
  }
  [Symbol.match](target) {
    return target.includes(this.str);
  }
}
console.log('foobar'.match(new StringMatcher('foo'))); // true
console.log('barbaz'.match(new StringMatcher('qux'))); // false
js

Symbol.replace

  1. 这个符号作为一个属性表示“一个正则表达式方法,该方法替换一个字符串中匹配的子串。由String.prototype.replace()方法使用”
  2. String.prototype.replace() 会调用 Symbol.replace 为键的方法来正则求值
  3. 正则表达式原型上也有 Symbol.replace
console.log(RegExp.prototype[Symbol.replace]);
// ƒ [Symbol.replace]() { [native code] }
console.log('foobarbaz'.replace(/bar/, 'qux'));
// 'fooquxbaz'
js

这个方法传入非正则表达式值会导致该值被转换为RegExp 对象,可以重新定义Symbol.replace 函数取代默认对正则表达式求值的行为。

Symbol.replace 函数接收两个参数,即调用replace()方法的字符串实例和替换字符串

class FooReplacer {
  static [Symbol.replace](target, replacement) {
    return target.split('foo').join(replacement);
  }
}
console.log('barfoobaz'.replace(FooReplacer, 'qux')); // "barquxbaz"
class StringReplacer {
  constructor(str) {
    this.str = str;
  }
  [Symbol.replace](target, replacement) {
    return target.split(this.str).join(replacement);
  }
}
console.log('barfoobaz'.replace(new StringReplacer('foo'), 'qux')); // "barquxbaz"
js

Symbol.search

  1. ECMAScript 规范,这个符号作为一个属性表示“一个正则表达式方法,该方法返回字符串中匹配正则表达式的索引。由String.prototype.search()方法使用”
  2. String.prototype.search() 会调用 Symbol.search 为键的方法来正则求值
  3. 正则表达式原型上也有 Symbol.search
console.log(RegExp.prototype[Symbol.search]);
// ƒ [Symbol.search]() { [native code] }
console.log('foobar'.search(/bar/)); // 3
js

search 方法传入非正则表达式值会被转换为 RegExp 对象,重新定义Symbol.search 函数以取代默认对正则表达式求值的行为

Symbol.search 函数接收一个参数,就是调用search()方法

class FooSearcher {
  static [Symbol.search](target) {
    return target.indexOf('foo');
  }
}
console.log('foobar'.search(FooSearcher)); // 0
console.log('barfoo'.search(FooSearcher)); // 3
console.log('barbaz'.search(FooSearcher)); // -1
class StringSearcher {
  constructor(str) {
    this.str = str;
  }
  [Symbol.search](target) {
    return target.indexOf(this.str);
  }
}
console.log('foobar'.search(new StringSearcher('foo'))); // 0
console.log('barfoo'.search(new StringSearcher('foo'))); // 3
console.log('barbaz'.search(new StringSearcher('qux'))); // -1
js

Symbol.species

  • ECMAScript 规范,这个符号作为一个属性表示“一个函数值,该函数作为创建派生对象的构造函数”。
class Bar extends Array {}
class Baz extends Array {
  static get [Symbol.species]() {
    return Array;
  }
}
let bar = new Bar();
console.log(bar instanceof Array); // true
console.log(bar instanceof Bar); // true
bar = bar.concat('bar');
console.log(bar instanceof Array); // true
console.log(bar instanceof Bar); // true

let baz = new Baz();
console.log(baz instanceof Array); // true
console.log(baz instanceof Baz); // true
baz = baz.concat('baz');
console.log(baz instanceof Array); // true
console.log(baz instanceof Baz); // false
js

刚看到这有点懵,看下面例子:

class MyArr extends Array{}
const a = new MyArr(1,2,3)
const b = a.map(item => item+1)
b instanceof MyArr // true
js
  1. MyArr 继承 Array,a 是 MyArr 构造的实例,b 又是 a 实例衍生的对象。
  2. 按道理说 b 应该是 Array 构造出来的实例,但实际上 MyArr 构造的实例(instanceof 返回 true 说明 b 原型链上存在 MyArr)
  3. 而 Symbol.species 属性就是为了解决这个问题而提供的:
class MyArray extends Array {
  static get [Symbol.species]() { return Array; }
}
const a = new MyArray(1, 2, 3);
const b = a.filter(x => x < 5);
b instanceof Array // true
b instanceof MyArray // false
js

这样,衍生出来的 b 则不是 MyArray 构造出来的了,而是 return 返回 Array 的实例

Symbol.split

  • ECMAScript 规范,这个符号作为一个属性表示“一个正则表达式方法,方法匹配正则的索引位置拆分字符串。由String.prototype.split()方法使用”。
  • String.prototype.split()方法会使用以Symbol.split 为键的函数来对正则表达式求值
  • 正则表达式原型上也有 Symbol.split
console.log(RegExp.prototype[Symbol.split]);
// ƒ [Symbol.split]() { [native code] }
console.log('foobarbaz'.split(/bar/));
// ['foo', 'baz']
js

Symbol.split 第一个参数就是调用match()方法的字符串实例。

class FooSplitter {
  static [Symbol.split](target) {
    return target.split('foo');
  }
}
console.log('barfoobaz'.split(FooSplitter)); // ["bar", "baz"]
class StringSplitter {
  constructor(str) {
    this.str = str;
  }
  [Symbol.split](target) {
    return target.split(this.str);
  }
}
console.log('barfoobaz'.split(new StringSplitter('foo'))); // ["bar", "baz"]
js

Symbol.toPrimitive

  • ECMAScript 规范,这个符号作为一个属性表示“一个方法,该方法将对象转换为相应的原始值。由ToPrimitive 抽象操作使用”
  • 很多内置操作都会尝试强制将对象转换为原始值,包括字符串、数值和未指定的原始类型
  • 重写 Symbol.toPrimitive 属性上的方法可以改变默认行为:
class Foo {}
let foo = new Foo();
console.log(3 + foo); // "3[object Object]"
console.log(3 - foo); // NaN
console.log(String(foo)); // "[object Object]"
class Bar {
  constructor() {
    this[Symbol.toPrimitive] = function(hint) {
      switch (hint) {
        case 'number':
          return 3;
        case 'string':
          return 'string bar';
        case 'default':
        default:
          return 'default bar';
      }
    }
  }
}
let bar = new Bar();
console.log(3 + bar); // "3default bar"
console.log(3 - bar); // 0
console.log(String(bar)); // "string bar"
js

Symbol.toStringTag

  • ECMAScript 规范,这个符号作为一个属性表示“一个字符串,该字符串用于创建对象的默认字符串描述。由内置方法Object.prototype.toString()使用”。
  • toString()获取对象标识时,会查找 Symbol.toStringTag 指定的实例标识符,默认为"Object"。
let s = new Set();
console.log(s); // Set(0) {}
console.log(s.toString()); // [object Set]
console.log(s[Symbol.toStringTag]); // Set
js

内置类型已经定义了 Symbol.toStringTag 属性,但自定义类需要自己定义:

class Foo {}
let foo = new Foo();
console.log(foo); // Foo {}
console.log(foo.toString()); // [object Object]
console.log(foo[Symbol.toStringTag]); // undefined
class Bar {
  constructor() {
    this[Symbol.toStringTag] = 'Bar';
  }
}
let bar = new Bar();
console.log(bar); // Bar {}
console.log(bar.toString()); // [object Bar]
console.log(bar[Symbol.toStringTag]); // Bar
js

Symbol.unscopables

  • ECMAScript 规范,这个符号作为一个属性表示“一个对象,该对象所有的以及继承的属性,都会从关联对象的with 环境绑定中排除”
  • 让对应的属性值的键为 true,就可以阻止改属性出现在 with 中
let o = { foo: 'bar' };
with (o) {
  console.log(foo); // bar
}
o[Symbol.unscopables] = {
  foo: true
};
with (o) {
  console.log(foo); // ReferenceError
}

js

JavaScript ES6 Map 对象

  • 方法
    set()、get()、delete()、has()、clear()、size

set,get 键值对

key 是字符串

var myMap = new Map();
myMap.set('键', "和键关联的值"); // 设置 键值对
myMap.get('键'); // "和键关联的值" 获取值
js

key是对象

var myMap = new Map();
var keyObj = {}
myMap.set(keyObj, "和键 keyObj 关联的值");
myMap.get(keyObj); // "和键 keyObj 关联的值"
myMap.get({}); // undefined, 因为 keyObj !== {}
js

key 是函数时也与 key 是对象同理。

key 是 NaN ,NaN 作为 Map 的键没有区别。

var myMap = new Map();
myMap.set(NaN, "not a number");
myMap.get(NaN); // "not a number"
myMap.get(Number("无法转换")); // "not a number"
js

Map 的迭代 遍历

var myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
js

for…of

for (var [key, value] of myMap) {
 console.log(key + " = " + value);
}
/* 打印两次
  "0 = zero"
  "1 = one"
*/

myMap.entries() // MapIterator {0 => "zero", 1 => "one"}
for (var [key, value] of myMap.entries()) {
 console.log(key + " = " + value);
}
// entries() 方法返回 Iterator 对象,它按set插入键值对顺序包含 Map 对象中每个元素的 [key, value] 数组。

myMap.keys(); // MapIterator {0, 1} 返回键
for (var key of myMap.keys()) {
 console.log(key); // 打印 0 和 1
}
// keys()返回 Iterator 对象,按插入顺序包含 Map 对象每个元素的键

myMap.values(); // MapIterator {"zero", "one"}
for (var value of myMap.values()) {
 console.log(value); // 一个是 "zero" 另一个是 "one"
}
/* values() 返回 Iterator 对象,按插入顺序包含 Map 对象每个元素的值。 */
js

forEach

myMap.forEach(function(value, key) {
 console.log(key + " = " + value);
 // 一个是 "0 = zero" 另一个是 "1 = one"
}
js

对象操作

转换

var kvArray = [["key1", "value1"], ["key2", "value2"]];
// 可以将 二维 键值对数组转换 Map 对象

var myMap = new Map(kvArray); // {"key1" => "value1", "key2" => "value2"}
// Array.from 函数将 Map 对象转回去

var outArray = Array.from(myMap); // [["key1", "value1"], ["key2", "value2"]]
js

克隆

var myMap1 = new Map([["key1", "value1"], ["key2", "value2"]]);
var myMap2 = new Map(myMap1);
/* {"key1" => "value1", "key2" => "value2"} */

console.log(myMap1 === myMap2); // false 新地址
js

合并

var first = new Map([[1, 'one'], [2, 'two'], [3, 'three'],]);
var second = new Map([[1, 'uno'], [2, 'dos']]);
// 合并两个 Map 对象时,有重复的键值,则后面的会覆盖前面的,对应值即 uno,dos, three

var merged = new Map([...first, ...second]); // 解构传参
console.log(merged); // {1 => "uno", 2 => "dos", 3 => "three"}
js

JavaScript ES6 Set 对象

  • 原型方法
    • add()、delete()、has()、clear()、 size
  • Set 对象允许存储任何类型的唯一值,原始值或引用值都可。
  • 特殊值
    • +0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复。
    • undefined 与 undefined 是恒等的,所以不重复。
    • NaN 与 NaN 是不恒等的,但是在 Set 中只能存一个,不重复。

创建

let mySet = new Set();
mySet.add(5); // Set(1) {5}
mySet.add(5); // Set(1) {5} 值的唯一性

mySet.add({a: 1, b: 2});
mySet.add({a: 1, b: 2}); 
// Set(5) {1, {…}, {…}} 
// 对象之间引用不同不恒等,即使值相同,Set 也能存储
js

转换

// Array 转 Set
var mySet = new Set(["value1", "value2", "value3"]);// {"value1", "value2", "value3"}

// 用...操作符,将 Set 转 Array
var myArray = [...mySet];// ["value1", "value2", "value3"]

// String
// String 转 Set
var mySet = new Set('hello'); // Set(4) {"h", "e", "l", "o"}
// 注:Set 中 toString 方法是不能将 Set 转换成 String
js

数组去重

var mySet = new Set([1, 2, 3, 4, 4]);
[...mySet]; // [1, 2, 3, 4]
js

并集

var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var union = new Set([...a, ...b]); // {1, 2, 3, 4}
/* 解构传入,唯一值,相当于去重 */
js

交集

var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
js

Set.has(x) 是 set 中的一个方法。

判断当前 set 对象 中是否含有 x,返回 true/false。

var intersect = new Set([...a].filter(x => b.has(x))); // {2, 3}
js

[…a] 解构成数组。filter 过滤

传入 x,返回 set 对象 b 中是否存在 x

存在返回 true,不存在返回 false 被过滤掉

差集

与交集同理

var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var difference = new Set([...a].filter(x => !b.has(x))); // {1}
js

JavaScript ES6 Proxy

  • target:即目标对象
  • handler:是一个对象,代理 target 的指定行为
let proxy = new Proxy(target, handler)
let target = {
  name: 'Tom',
  age: 24
}

let handler = { 
  get: function(target, key) { // 获取target的值会触发次函数
    console.log('getting '+ key);
    return target[key]; // 不是target.key
  },
  set: function(target, key, value) {// 设置target的值会触发次函数
    console.log('setting '+ key);
    target[key] = value;
  }
}
js

JavaScript ES6 String 字符串新增方法

模板字面量

console.log(`Hello, ${ `World` }!`); // Hello, World!
js

所有插入的值都会使用toString()强制转型为字符串:

let foo = { toString: () => 'World' };
console.log(`Hello, ${ foo }!`); // Hello, World!
js

也可以插入自己之前的值:

let value = '';
function append() {
  value = `${value}abc`
  console.log(value);
}
append(); // abc
append(); // abcabc
append(); // abcabcabc
js

标签函数

let a = 6;
let b = 9;
function simpleTag(strings, ...expressions) {
  console.log(strings);
  for(const expression of expressions) {
    console.log(expression);
  }
  return 'foobar';
}
let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;
// ["", " + ", " = ", ""]
// 6
// 9
// 15
console.log(taggedResult); // "foobar"
js

标签函数的表达式参数的个数始终是n,传给标签函数的第一个参数所包含的字符串个数则始终是n+1。返回正常的字符串结果:

let a = 6;
let b = 9;
function zipTag(strings, ...expressions) {
  return strings[0] +
  expressions.map((e, i) => `${e}${strings[i + 1]}`)
  .join('');
}
let untaggedResult = `${ a } + ${ b } = ${ a + b }`;
let taggedResult = zipTag`${ a } + ${ b } = ${ a + b }`;
console.log(untaggedResult); // "6 + 9 = 15"
console.log(taggedResult); // "6 + 9 = 15"
js

字符串原始值

console.log(`\u00A9`); // © 是版权符号
console.log(String.raw`\u00A9`); // \u00A9

console.log(`first line\nsecond line`); // 换行符示例
// first line
// second line
console.log(String.raw`first line\nsecond line`); // "firstline\nsecond line"

// 实际的换行符不行
console.log(String.raw`first line
second line`);
// first line
// second line
js

通过标签函数的第一个参数取得原始值:

function printRaw(strings) {
  console.log('字面量:');
  for (const string of strings) {
    console.log(string);
  }
  console.log('原始值:');
  for (const rawString of strings.raw) {
    console.log(rawString);
  }
}
printRaw`\u00A9${ 'and' }\n`;
// 字面量:
// ©
//(换行符)
// 原始值:
// \u00A9
// \n
js

识别查找

// 传入正则会抛出错误

includes() // 返回布尔值,判断是否找到参数字符串。
startsWith() // 返回布尔值,判断参数字符串是否在原字符串的头部。
endsWith() // 返回布尔值,判断参数字符串是否在原字符串的尾部。

let string = "apple,banana,orange";
string.includes("banana");   // true
string.startsWith("apple");  // true
string.endsWith("apple");   // false
string.startsWith("banana",6) // true
js

repeat()

repeat():返回新字符串,将字符串重复指定次数返回。

"Hello,".repeat(2); // "Hello,Hello,"
"Hello,".repeat(3.2); // "Hello,Hello,Hello," 向下取整
js

0 至 -1 之间的小数,取整得到 -0 ,等同于 repeat 零次

NaN,等同于 repeat 零次

负数或者 Infinity ,会报错:

"Hello,".repeat(-0.5); // "" 
"Hello,".repeat(NaN); // "" 
js

传入字符串会隐式转换数字

"Hello,".repeat("hh"); // ""
"Hello,".repeat("2"); // "Hello,Hello,"
js

字符串补全

padStart:返回新的字符串,从头部(左侧)补全。

padEnd:返回新的字符串,从尾部(右侧)补全。

"123".padStart(10,"0"); // "0000000123" 常用于补全位数
"h".padStart(5,"o"); // "ooooh"
"h".padEnd(5,"o");  // "hoooo"
"h".padStart(5);   // "  h" 没有第二个参数默认空格
"hello".padStart(5,"A"); // "hello" 小于或等于返回原字符
"hello".padEnd(10,",world!"); // "hello,worl" 截去超出位数
js

去除空格

trimStart():消除字符串头部的空格 trimEnd():消除尾部的空格

// 返回的都是新字符串,不会修改原始字符串
const s = ' abc ';
s.trim() // "abc"
s.trimStart() // "abc "
s.trimEnd() // " abc"
js

提示
浏览器还部署了额外的两个方法,trimLeft() 是 trimStart() 的别名,trimRight() 是 trimEnd() 的别名。

替换

matchAll():方法返回一个正则表达式在当前字符串的所有匹配

'aabbcc'.replaceAll('b', '_') // 'aa__cc'
// 返回一个新字符串,不会改变原字符串。 一次性替换所有匹配。
js

JavaScript ES6 Array 数组新增方法

Array.of()

将参数中所有值作为元素形成数组。

没有参数则返回空数组

Array.of(1, '2', true); // [1, '2', true]
js

Array.from()

将类数组对象或可迭代对象转化为数组。

Array.from([1, 2]); // [1, 2]
Array.from([1, , 3]); // [1, undefined, 3]
js

转换类数组

let arr = Array.from({
 0: '1',
 1: '2',
 2: 3,
 length: 3 // 必须有length属性,没有则返回空数组
});
// ['1', '2', 3]

// 属性名无法转换为数值,返回长度为 length 元素值为 undefined 的数组 
let array1 = Array.from({
 a: 1, // 下标 a,b 不能转换为 0 1 
 b: 2,
 length: 2
});// [undefined, undefined]
js

转换 map

let map = new Map();
map.set('key0', 'value0');
map.set('key1', 'value1');
Array.from(map); // [['key0', 'value0'],['key1','value1']]
js

转换set

let set = new Set([1, 2, 3]);
Array.from(set); // [1, 2, 3]
js

转换字符串

Array.from('abc'); // ["a", "b", "c"]
js

方法

find()查找

则返回符合条件的第一个元素。

let arr = Array.of(1, 2, 3, 4);
arr.find(item => item > 2); // 3
js

findIndex() 查找索引

则返回符合条件的第一个元素的索引。

let arr = Array.of(1, 2, 1, 3);
// 参数1:回调函数
// 参数2(可选):指定回调函数中的 this 值
arr.findIndex(item => item = 1); // 0
js

fill()填充

let arr = Array.of(1, 2, 3, 4);
// 参数1:用来填充的值
// 参数2:被填充的起始索引
// 参数3(可选):被填充的结束索引,默认为数组末尾
console.log(arr.fill('填充',1,2)); // [1, '填充', 3, 4]
js

copyWithin() 覆盖

  1. 开始覆盖的位置索引
  2. 复制起始位置
  3. (可选)复制结束位置,默认为结尾
var arr = ["a","b","c","d","e","f","g"]
arr.copyWithin(2,4,6)// ["a", "b", "e", "f", "g", "f", "g"]
js

entries() 遍历

// 遍历键值对。
for(let [key, value] of ['a', 'b'].entries()){
  console.log(key, value);
}
// 0 "a"
// 1 "b"
js

不使用 for… of 循环

let entries = ['a', 'b'].entries();
console.log(entries.next().value); // [0, "a"]
console.log(entries.next().value); // [1, "b"]
js

keys()遍历键名

for(let key of ['a', 'b'].keys()){
  console.log(key);
}
// 0
// 1
js

values()遍历键值

for(let value of ['a', 'b'].values()){
  console.log(value);
}
// "a"
// "b"
js

includes()查找

数组是否包含指定值

Set 的 has 方法用于查找值,Map 的 has 方法用于查找键名。

// 参数1:包含的指定值
[1, 2, 3].includes(1);  // true
// 参数2:可选,搜索的起始索引,默认为0
[1, 2, 3].includes(1, 2); // false
js

flat()嵌套数组转一维数

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

// 指定转换的嵌套层数
console.log([1, [2, [3, [4, 5]]]].flat(2)); // [1, 2, 3, [4, 5]]

// 不管嵌套多少层
console.log([1, [2, [3, [4, 5]]]].flat(Infinity)); // [1, 2, 3, 4, 5]

// 自动跳过空位
console.log([1, [2, , 3]].flat());// [1, 2, 3]
js

flatMap()

先遍历元素,再对数组执行 flat() 方法。

// 参数1:遍历函数,遍历函数可接受3个参数:当前元素、当前元素索引、原数组
// 参数2:指定遍历函数中 this 的指向
console.log([1, 2, 3].flatMap(n => [n * 2])); // [2, 4, 6]
js

JavaScript ES6 Function 箭头函数

传参

// 默认参数
// 使用默认参数时,不能有同名形参
function fn(name,age=17){
 console.log(name+","+age);
}

function f(...values){ // [1, 2]
  console.log(values.length); // 2
}
f(1,2); // 2
js

箭头函数

  • 与一般函数区别
    • 先声明后使用
    • 不能使用 arguments ,使用 …rest 剩余运算符解决
    • 不能 new 当做构造函数
  • 简写
    • 只有一个形参时可以省略圆括号
    • 只有一条语句,且把这条语句当做返回值时可以省略大括号
  • this 指向
    • this指向上一层函数的 this
    • 箭头函数的当前调用者不能使用call,apply、bind改变this指向
  • 不适用场景
    • 对象中的方法不适用箭头函数
    • DOM绑定事件不适用箭头函数

箭头函数不适用的场景:

  1. 对象中的方法不能用箭头函数;
  2. 给 DOM 绑定事件时不能用箭头函数;
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script>
        let obj = {
            "usr": "李四",
            test: () => {
                console.log(this.usr);
            },
            test2() {
                console.log(this.usr);
            }
        };

        obj.test(); //undefined
        obj.test2();
    </script>
</head>

<body>
    <button>测试</button>
    <script>
        let btn = document.getElementsByTagName('button')[0];
        // btn.addEventListener('click', function() {
        //     console.log(this);
        // });

        btn.addEventListener('click', () => {
            console.log(this);
        });
    </script>
</body>
html

JavaScript ES6 Iterator 迭代器

核心概念:

  1. 迭代器是一个统一的接口,作用是使各种数据结构可被便捷的访问
  2. 是 Symbol.iterator 下的方法实现。提供这种接口的有 Array、String、arguments、Map、Set、Dom 元素(正在进行中)。可以被 for…of 遍历
  3. Array 下有 Symbol 属性,所以 arrSymbol.iterator 调用,返回 Iterator对象
  4. iterator 对象下 next 方法单次调用方法 {value: ‘本次遍历的值’, done: 是否遍历结束,返回 true/false }
const items = ["zero", "one", "two"];
const it = items[Symbol.iterator](); 
it.next(); // {value: "zero", done: false}
it.next(); // {value: "one", done: false}
it.next(); // {value: "two", done: false}
it.next(); // {value: undefined, done: true}

// 正常运行
for (let item of Array.from({...})) {
 console.log(item);
}
// 部署一个Iterator接口
obj[Symbol.iterator] = function(){
  // ...
}
js

JavaScript ES6 Promise

Promise 状态

Promise 状态

  • pending(进行中) ----> fulfilled/resolved(成功)
  • pending(进行中) ----> rejected(失败)

只要处于 fulfilled/resolved [成功] 和 rejected [失败],状态就不会再变。

  1. 创建 Promise 参数内的函数会立即执行,并返回 Promise 对象
  2. 两个参数代表状态,resolve 成功调用,reject 失败调用
const p1 = new Promise((resolve,reject) =>{
  if(1){
    resolve('成功');
  }else{
    reject('失败');
  }
});
js
  1. 返回 Pormise 对象调用 then 方法,第一个参数对象 resolve 成功后的回调,第二个参数对应 reject 失败时回调。then 方法也会返回 Promise 对象
p1.then(value => {console.log(value)},err => {console.log(err)});
js
  1. then 方法执行成功的回调时,如果发生错误,不会被第二个参数对应 reject 失败时回调捕捉到。
  2. then 方法执行成功的回调发生错误是,链式调用 catch 方法可以捕捉前面 then 方法发生的错误
p1.then(val => {代码块有发生错误}).catch(e => {console.log(e);})
js

Promise.all()与Promise.race()

Promise.all([Promise 对象,Promise 对象...]) // 批量执行
js
  1. 传入数组中包含多个 Promise 实例,也可以是别的值,all 包装成一个新的 Promise
  2. 全部都成功后,返回每个 Promise 成功的值 [“resolve 成功值 1”, “resolve 成功值 1”]
  3. 任何一个失败,返回第一个失败的 Promise 结果
Promise.race([Promise 对象,Promise 对象...])
js

不管成功还是失败、哪个结果获得的快,就返回那个结果。

JavaScript ES6 Generator 函数

  • 执行机制
  1. function 后加 *,函数执行后返回 Iterator 对象
  2. 返回的对象调用 next 方法开始执行,遇到 yield 关键字会停止。
  3. 再次调用 next 方法会从上一次的结束的地方继续执行,直到 yield
  4. yield 后面的值会在 next 执行停止时返回
  5. next 传的参数会在函数内传给 yield
function* fnc(){
 console.log("开始");
 let a = yield '返回给next'; // next 没有传参 a 默认 undefined
 console.log(a,"结束");// next传入 '结束'
 return '2';
}
let f = fnc();
f.next('next传入');// {value: "返回给 next", done: false}
f.next();// {value: undefined, done: true}
js

JavaScript ES6 async 函数

  1. async 函数返回 Promise 对象,用同步流程来表达异步操作
  2. 虽然返回的是 Promise 对象,但不能在 async 函数中调用 resolve,reject 函数
  3. async 可以单独使用,await 只能在 async 函数中使用
  4. 调用 async 函数会立即执行,遇到 await 关键字会暂停执行,await 后的指向完成后,async 函数接着执行。
  5. 如果 await 后的异步需要时间,await 下一行会接着执行,导致 await 的结果比下一行代码后得到
  6. 解决异步需要时间的问题,await 等待的是 Promise 的结果。所以 await 后面配合 Promise 执行异步函数,但 await 不能处理 Promise 失败后的结果
  7. 解决失败结果方法一:await prm().catch(e => {}); 阅读不方便
  8. 解决方法二 : 在 prm() 结果中不管成功还是失败,都调用 resolve 方法,成功传[null,数据],失败传 [err]; await 执行后 [e,d]=await prm(); 结构判断 e 是否出错
async function fn(){
  let d = await 异步函数;
}

function ti(){
  setTimeout(() => {
    console.log('异步结果');
  },2000)
}

async function fn(){
  await ti();// 里面异步函数 2 秒后执行
  console.log('这里会比上面await先输出');
}

async function fn(){
  var [e,data] = await prm();
  if(e) return; // 发生了错误
  console.log(data,'promise执行完后才执行这行代码');
}

function prm(){
  return new Promise(resolve => {
    if('成功'){
      resolve([null,data]);
    }else{
      resolve(['失败了'])
    }
  })
}
js

JavaScript ES6 class 类

  1. constructor内外都可以定义属性,前面加static为静态属性
  2. 类里面,方法(){}为成员方法,加static为静态方法constructor内this.方法()为实例方法
  3. 继承父类方法使用extends,子类没有constructor时会默认调用父类的constructor
  4. 子类constructor内使用this之前必须调用super()方法把父类的this继承下来
  5. 成员属性、方法、静态属性、方法也会继承下来。子类使用父类方法可以super.方法名,也可以this.方法
  6. 子类用super.父类属性,也可以使用this来获取
  7. 静态方法不能访问成员属性,成员方法不能访问静态属性
class Person {
  name;// 成员属性
  static age = 2;// 静态属性
  constructor(name,age){
    this.name = name;
    this.age = age; // 这里 this.age 指向实例的 age
  }

  fn(){
    // 成员方法(原型上)
    console.log(this.age + Person.age);
  }
  static fn1(){
    // 静态方法 只能使用 Person.fn1()来调用
    console.log(this.age === Person.age); // true
  }
}

class Son extends Person{
  constructor(name,age,sex){
    super(name,age);// this之前调用super
    super.fn();// 调用父类方法
    this.sex = sex;
  }

  fn(){
    // 重写父类方法
  }
  static ff(){
    super.fn1();// 调用父类静态方法
  }
}

// 不可继承常规对象。
var Father = {
  // ...
}
class Child extends Father {
  // ...
}
// Uncaught TypeError: Class extends value #<Object> is not a constructor or null

// 解决方案
Object.setPrototypeOf(Child.prototype, Father);
js

JavaScript ES6 模块化

  1. es6 模块语法无法在浏览器,vscode 直接运行。
  2. 需要安装 npm 包:babel 将 es6 语法转换成 es5。browserify 后台代码编译成前端 npm包

单个暴露

export let/var 属性名=属性值
export function 方法名(){ }
js

批量暴露

let/var 属性名=值
function 方法名(){}
export {属性名,属性名 as 别名,方法名}
js

暴露所有

export default{}
js

使用模块:导入单个对应 export

import {属性名,方法名,属性名 as 别名} from '模块文件名'
js

引入所有:对应 export default

import 名字  from 路径
js


评论
来发一针见血的评论吧!
表情

快来发表评论吧~

推荐文章
  • 测试文章

    20点赞10评论

  • webpack5(一)

    20点赞10评论