JavaScript ES6 let、cont、解构
let 和 const
- 遇到 {} 就形成作用域
- 同一作用域不能重复声明变量或函数 [如:let 声明过不能用 const 和 var 声明相同名字]
- 没有变量提升
- const 必须初始化赋值,不能被修改,而 let 声明的变量不用
- const 定义的对象或者数组,其实是可变的。
- 可以修改添加属性
const car = {color:"white"};
car.color = "red";// 修改属性:
car.owner = "Johnson";// 添加属性
js
- 不能赋值
const car = {};
car = {}; // 错误
js
数组同理
- 全局作用域 var 属于 window 对象。let 不属于 window 对象。
- let 有暂时性死区、要先声明再使用。
解构
[模式] = [数组];
{模式} = {对象};
- 完全解构
- 不完全解构 [,可以被忽略 为赋值成功为 undefined ]
- 嵌套
- 默认值 , undefined 不会被赋值,null 可以被赋值
数组解构
let [, [x, y], m = 88,...z] = [2, [3, 4], undefined,1,2]; // m 为 88
js
- , 会忽略
- [] 可以嵌套
- undefined 不会赋值上去, m = 88 默认值,null 会被赋值
- …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
声明
- 原始数据类型,不能使用new
- 括号内是备注的意思,独一无二的值,两个 Symbol 不相等
- typeof 检测为 Symbol
- 作为对象的键名独一无二、使用 [] 设置/获取,不能用.运算符获取设置
- 作为对象属性名是,是共有属性,不是私有属性,可以在类的外部访问
- 不能被枚举 for in、for of、Object.keys()、Object.getOwnPropertyNames() 返回
- 可以被 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
- 全局搜索是否存在该名称
- 有:返回本身,没有:新建一个返回
- 使两个 Symbol 类型的变量相等,生成同一个 Hash 值
Symbol("Yellow") === Symbol.for("Yellow"); // false
Symbol.for("Yellow") === Symbol.for("Yellow"); // true
js
Symbol.keyFor(sym)
- 查找键值的某个 Symbol
- 找到返回该 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
- ECMAScript 规范,这个符号作为一个属性表示“一个布尔值”
- 默认情况下则对象应该用Array.prototype.concat()展开数组元素
- 如果是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
- ECMAScript 规范,这个符号作为一个属性表示“一个方法,该方法返回对象默认的迭代器,由 for-of 语句使用”
- for-of 循环时,会调用以 Symbol.iterator 为键的函数,并默认这个函数会返回一个实现迭代器API 的对象。
- 很多时候,返回的对象是实现该 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
- ECMAScript 规范,这个符号作为一个属性表示“一个正则表达式方法,该方法用正则表达式去匹配字符串。由String.prototype.match()方法使用”
- String.prototype.match() 会调用以 Symbol.match 为键的函数来正则求值
- 正则表达式的原型上就有 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
- 这个符号作为一个属性表示“一个正则表达式方法,该方法替换一个字符串中匹配的子串。由String.prototype.replace()方法使用”
- String.prototype.replace() 会调用 Symbol.replace 为键的方法来正则求值
- 正则表达式原型上也有 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
- ECMAScript 规范,这个符号作为一个属性表示“一个正则表达式方法,该方法返回字符串中匹配正则表达式的索引。由String.prototype.search()方法使用”
- String.prototype.search() 会调用 Symbol.search 为键的方法来正则求值
- 正则表达式原型上也有 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
- MyArr 继承 Array,a 是 MyArr 构造的实例,b 又是 a 实例衍生的对象。
- 按道理说 b 应该是 Array 构造出来的实例,但实际上 MyArr 构造的实例(instanceof 返回 true 说明 b 原型链上存在 MyArr)
- 而 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() 覆盖
- 开始覆盖的位置索引
- 复制起始位置
- (可选)复制结束位置,默认为结尾
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绑定事件不适用箭头函数
箭头函数不适用的场景:
- 对象中的方法不能用箭头函数;
- 给 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 迭代器
核心概念:
- 迭代器是一个统一的接口,作用是使各种数据结构可被便捷的访问
- 是 Symbol.iterator 下的方法实现。提供这种接口的有 Array、String、arguments、Map、Set、Dom 元素(正在进行中)。可以被 for…of 遍历
- Array 下有 Symbol 属性,所以 arrSymbol.iterator 调用,返回 Iterator对象
- 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 [失败],状态就不会再变。
- 创建 Promise 参数内的函数会立即执行,并返回 Promise 对象
- 两个参数代表状态,resolve 成功调用,reject 失败调用
const p1 = new Promise((resolve,reject) =>{
if(1){
resolve('成功');
}else{
reject('失败');
}
});
js
- 返回 Pormise 对象调用 then 方法,第一个参数对象 resolve 成功后的回调,第二个参数对应 reject 失败时回调。then 方法也会返回 Promise 对象
p1.then(value => {console.log(value)},err => {console.log(err)});
js
- then 方法执行成功的回调时,如果发生错误,不会被第二个参数对应 reject 失败时回调捕捉到。
- then 方法执行成功的回调发生错误是,链式调用 catch 方法可以捕捉前面 then 方法发生的错误
p1.then(val => {代码块有发生错误}).catch(e => {console.log(e);})
js
Promise.all()与Promise.race()
Promise.all([Promise 对象,Promise 对象...]) // 批量执行
js
- 传入数组中包含多个 Promise 实例,也可以是别的值,all 包装成一个新的 Promise
- 全部都成功后,返回每个 Promise 成功的值 [“resolve 成功值 1”, “resolve 成功值 1”]
- 任何一个失败,返回第一个失败的 Promise 结果
Promise.race([Promise 对象,Promise 对象...])
js
不管成功还是失败、哪个结果获得的快,就返回那个结果。
JavaScript ES6 Generator 函数
- 执行机制
- function 后加 *,函数执行后返回 Iterator 对象
- 返回的对象调用 next 方法开始执行,遇到 yield 关键字会停止。
- 再次调用 next 方法会从上一次的结束的地方继续执行,直到 yield
- yield 后面的值会在 next 执行停止时返回
- 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 函数
- async 函数返回 Promise 对象,用同步流程来表达异步操作
- 虽然返回的是 Promise 对象,但不能在 async 函数中调用 resolve,reject 函数
- async 可以单独使用,await 只能在 async 函数中使用
- 调用 async 函数会立即执行,遇到 await 关键字会暂停执行,await 后的指向完成后,async 函数接着执行。
- 如果 await 后的异步需要时间,await 下一行会接着执行,导致 await 的结果比下一行代码后得到
- 解决异步需要时间的问题,await 等待的是 Promise 的结果。所以 await 后面配合 Promise 执行异步函数,但 await 不能处理 Promise 失败后的结果
- 解决失败结果方法一:await prm().catch(e => {}); 阅读不方便
- 解决方法二 : 在 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 类
- constructor内外都可以定义属性,前面加static为静态属性
- 类里面,方法(){}为成员方法,加static为静态方法constructor内this.方法()为实例方法
- 继承父类方法使用extends,子类没有constructor时会默认调用父类的constructor
- 子类constructor内使用this之前必须调用super()方法把父类的this继承下来
- 成员属性、方法、静态属性、方法也会继承下来。子类使用父类方法可以super.方法名,也可以this.方法
- 子类用super.父类属性,也可以使用this来获取
- 静态方法不能访问成员属性,成员方法不能访问静态属性
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 模块化
- es6 模块语法无法在浏览器,vscode 直接运行。
- 需要安装 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