跳到主要内容

一些语法

  • symbol bigInt
  • Map Set WeakMap WeakSet WeakRef
  • const/let
  • 扩展运算符,剩余参数 ...
  • class
  • Promise async await
  • generator iterator
  • Proxy/Reflect

扩展运算符

拷贝可遍历的属性,浅拷贝。

let bar = { a: 1, b: 2 };
let baz = { ...bar }; // { a: 1, b: 2 } 与Object.assign 一致

解构赋值

const { a, b: y } = { a: 3, b: 4 };
//a :3 y:4 y 是对 b 的重命名。外边声明的是y,取值取 b 的值。

let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
foo; // "aaa"
bar; // "bbb"

提取高度嵌套对象里的指定属性:

const school = {
classes: {
stu: {
name: 'Bob',
age: 24,
},
},
};

const {
classes: {
stu: { name },
},
} = school;
console.log(name); // 'Bob'

箭头函数

  • 简洁
  • 没有自己的 this,继承上层作用域的 this,永远不会改变。不能通过 call,apply,bind 等去改变。
  • 不能作为构造函数,因为 this 的问题。

Array API

  • 扩展运算符。 是浅拷贝
  • Array.from
  • array.find array.findIndex
  • array.fill(value,[from],[to])
  • array.includes
  • array.flat 默认只拍平一层
  • array.flatMap
  • array.at(index)
实现 flat
//flat 实现 不带层级
function flatDeep(arr) {
return arr.reduce(
(acc, curr) =>
Array.isArray(curr) ? [...acc, ...flatDeep(curr)] : [...acc, curr],
[],
);
}

//flat 带层级展开
//1 借助外部函数递归
const flat = (arr, level = 1) => {
return helper(arr, level, []);
};
const helper = (arr, level = 1, res = []) => {
for (let item of arr) {
if (Array.isArray(item) && level > 0) {
res = [...res, ...flat(item, level - 1)];
} else {
res = [...res, item];
}
}
return res;
};
console.log(flat([1, 2, 3, [1, 2, [1, 2, 3]]], 2));

//2 reduce 递归
function flat(arr, level = 1) {
return arr.reduce(
(acc, curr) =>
Array.isArray(curr) && level > 0
? [...acc, ...flat(curr, level - 1)]
: [...acc, curr],
[],
);
}

Object API

  • Object.is

    使用同值相等的算法。基本与严格相等===一致。

    Object.is('foo', 'foo'); //true
    Object.is({}, {}); //false

    //与严格相等不同:
    Object.is(+0, -0); // false
    Object.is(NaN, NaN); // true
  • Object.assign

    对象合并,将源对象的所有可枚举属性复制到目标对象。返回的是目标对象本身。

    const target = { a: 1 };

    const source1 = { b: 2 };
    const source2 = { c: 3 };

    Object.assign(target, source1, source2);

    该方法是浅拷贝。复制到目标对象上的属性如果是一个对象,是对原来对象的引用。

  • 遍历相关 Object.keys,Object.values ,Object.entries

    以上都是遍历对象自身(不含继承的),所有可遍历属性。

    const obj = { foo: 'bar', baz: 42 };
    Object.entries(obj);
    // [ ["foo", "bar"], ["baz", 42] ]

    Object.fromEntries()方法是 Object.entries()的逆操作.

  • Object.setPrototypeOf

    Object.setPrototypeOf 方法的作用与proto相同,用来设置一个对象的原型对象(prototype),返回参数对象本身

  • Object.getPrototypeOf

Class

//es5
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
var p = new Point(1, 2);

//es6
class Point {
constructor(x, y) {
//构造方法
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
const p = new Point(1, 2);
Point === Point.prototype.constructor; // true
p.constructor === Point.prototype.constructor; // true

类的底层还是通过构造函数实现的。

//上面的类等同于
Point.prototype = {
constructor() {},
toString() {},
};

类的内部所有定义的方法,都是不可枚举的(non-enumerable),而 es5 的构造函数上定义的方法可枚举。

Object.keys(Point.prototype); //[]

getter setter

对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

getter setter 必须同时出现。

class MyClass {
constructor() {
// ...
}
get a() {
return 'getter';
}
set a(value) {
console.log('setter: ' + value);
// this.a = a; // 自身递归调用 ❌❌❌ 不能这样写!
}
}

let inst = new MyClass();

inst.a = 123;
// setter: 123

inst.a;
// 'getter'

static

静态方法,不能在类的实例上调用静态方法,而应该通过类本身调用,静态方法不会被实例继承。

class Foo {
static classMethod() {
return 'hello';
}
}

Foo.classMethod(); // 'hello'

var foo = new Foo();
foo.classMethod();
// TypeError: foo.classMethod is not a function

如果静态方法包含 this 关键字,这个 this 指的是类,而不是实例。

父类的静态方法,可以被子类继承。

extends

super 用来访问和调用一个对象的父对象上的函数。

super 必须在 this 关键字之前使用。

class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class Child extends Father {
// 继承父类所有属性和方法
constructor(a) {
super(x, y);
this.a = a;
}
}
let test = new Child();

Proxy

修改某些操作的默认行为,在语言层面的修改,属于元编程。

在目标对象前的拦截,可以外界访问目标对象时进行过滤或改写。支持 13 种拦截操作。

  • 代理属性时要考虑该属性是否 configurable ,是否 writable。

Proxy.revocable() 方法返回一个对象,该对象的 proxy 属性是 Proxy 实例,revoke 属性是一个函数,可以取消 Proxy 实例。

let target = {};
let handler = {};

let { proxy, revoke } = Proxy.revocable(target, handler);

proxy.foo = 123;
proxy.foo; // 123

revoke();
proxy.foo; // TypeError: Cannot perform 'get' on a proxy that has been revoked
target; //{foo: 123}

ES6 常用但被忽略的方法(第四弹 Proxy 和 Reflect) - 掘金

Reflect

Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就能在 Reflect 对象上找到对应的方法。每一个 Proxy 对象的拦截操作(get、delete、has...),内部都调用对应的 Reflect 方法。

让 Object 操作都变成函数行为。

// 老写法
'assign' in Object; // true

// 新写法
Reflect.has(Object, 'assign'); // true

Proxy and Reflect

  • 代理对象的属性

    const jay = {
    name: 'jay',
    phone: '188888888',
    age: '30',
    };

    const handler = {
    get(target, key, receiver) {
    console.log('hh', target, key, receiver);
    if (key === 'phone') {
    return '代理的电话12222';
    }
    //return target[key] //有可能还是被代理了,代理陷阱
    return Reflect.get(target, key, receiver);
    },
    };
    const proxy = new Proxy(jay, handler);

    proxy.phone; //代理的电话12222
    proxy.name; // ’jay‘
  • 使用 Proxy Reflect 实现通过复数数组下标访问数组元素

    let arr = [1, 2, 3, 4];

    arr = new Proxy(arr, {
    get: function (target, key, receiver) {
    let idx = Number(key);
    if (key < 0) {
    return target[target.length + idx];
    }
    return Reflect.get(target, key, receiver);
    },
    });

    arr[-1];

Reference