ES6-ES12

ES6

一、class定义类

构造函数形式创建 ,不仅仅和编写普通的函数过于相似,而且代码并不容易理解。 在ES6新的标准中使用了class关键字来直接定义类; 但是类本质上构造函数、原型链的语法糖而已;

1.类的声明

// 类声明
class Person {
}
// 类表达式
var Student = class {
}

// 相似的如:
// 函数声明
function Parent() {
}
// 函数表达式
var son = function() {
}

2.constructor

constructor方法(即构造方法)是类的默认方法,通过new生成对象时,自动调用该方法。

  • 每个类只能有一个构造函数,如果包含多个构造函数,那么会抛出异常;

  • 一个类必须有constructor方法,如果没有显示定义,一个空的constructor方法会被默认添加;

class People {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  eatting() {
    console.log(this.name + "吃!");
  }
  running() {
    console.log(this.name + "跑!");
  }
}

var p1 = new People('颤三',19)
p1.eatting() // 颤三吃!
p1.running() // 颤三跑!

3.实例方法

class中定义的方法,实际上都是定义在这个"类"的prototype上的。

class People {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  eatting() {
    console.log(this.name + "吃!");
  }
  running() {
    console.log(this.name + "跑!");
  }
}

console.log(Object.getOwnPropertyDescriptors(People)); // 输出如下:
// {
//   length: { value: 2, writable: false, enumerable: false, configurable: true },
//   name: {
//     value: 'People',
//     writable: false,
//     enumerable: false,
//     configurable: true
//   },
//   prototype: {
//     value: {},
//     writable: false,
//     enumerable: false,
//     configurable: false
//   }
// }
console.log(Object.getOwnPropertyDescriptors(People.prototype)); // 输出如下:
// {
//   constructor: {
//     value: [class People],
//     writable: true,
//     enumerable: false,
//     configurable: true
//   },
//   eatting: {
//     value: [Function: eatting],
//     writable: true,
//     enumerable: false,
//     configurable: true
//   },
//   running: {
//     value: [Function: running],
//     writable: true,
//     enumerable: false,
//     configurable: true
//   }
// }

4.类的访问器方法

当调用获取设置某个属性时,触发getset函数里的函数体代码。

class Abc {
  constructor(name) {
    this.name = name
  }

  set name(newName) {
    console.log("设置了这个属性name");
  }
  get name() {
    console.log("调用或获取了这个属性name");
    // return this.name
    return "获取" // get必须有返回值
  }
}
console.log(Abc.name); // Abc

5.类的静态方法

静态方法通常用于定义直接使用类来执行的方法,不需要有类的实例,使用static关键字来定义.

class Asd {
  constructor(asd) {
    this.asd = asd
  }
  
  haox() {
    console.log("不是静态方法不可以直接调用");
  }

  static create() {
    console.log("你调用了静态方法");
  }
}
Asd.create() // 你调用了静态方法
// Asd.haoX() // 报错,不是静态方法不能直接调用,需要new一个实例才能调用
var a1 = new Asd()
a1.haox() // 不是静态方法不可以直接调用

6.继承

通过extends关键字继承。

class A {

}
class B extends A {
  
}
  • 子类中新添加的方法是添加到了子类的原型对象上,并不是父类的远程对象上;
class A {
  constructor(name,age) {
    this.name = name
    this.age = age
  }
  eatting() {
    console.log(this.name + "吃!");
  }
  running() {
    console.log(this.name + "跑!");
  }
}
class B extends A {
  constructor(name,age,height) {
    // this.name = name
    // this.age = age
    super(name,age) // 这里必须调用父类的构造器否则报错(派生类的构造函数必须包含 "super" 调用。),同时可以避免代码冗余
    this.height = height

  }
  study() {
    console.log("study");
  }
}

// 子类中新添加的方法是添加到了子类的原型对象上,并不是父类的远程对象上
console.log(Object.getOwnPropertyDescriptors(B.prototype)); // 输出如下:
// {
//   constructor: {
//     value: [class B extends A],
//     writable: true,
//     enumerable: false,
//     configurable: true
//   },
//   study: {
//     value: [Function: study],
//     writable: true,
//     enumerable: false,
//     configurable: true
//   }
// }
console.log(Object.getOwnPropertyDescriptors(A.prototype));  // 输出如下:
// {
//   constructor: {
//     value: [class A],
//     writable: true,
//     enumerable: false,
//     configurable: true
//   },
//   eatting: {
//     value: [Function: eatting],
//     writable: true,
//     enumerable: false,
//     configurable: true
//   },
//   running: {
//     value: [Function: running],
//     writable: true,
//     enumerable: false,
//     configurable: true
//   }
// }

7.super关键字

  • 可调用父类的构造函数;
  • 可调用父类上的方法

二、字面量的增强

ES6中对 对象字面量 进行了增强;

1.属性的简写

2.方法的简写

3.计算属性名

var age = 18
var obj1 = {
  // age : age
  age,    // 1.属性的简写

  // 2.方法名简写
  // foo: function() {

  // }
  foo() {  // 简写

  },

  // 3.计算属性
  [age + 123]: 'hhhhh',
  [age + '123']: 'hhhhh'
}

console.log(obj1);  // { '141': 'hhhhh', '18123': 'hhhhh', age: 18, foo: [Function: foo] }

三、解构赋值

1.数组的解构

// 1.数组的解构
var names = ['agc', 'cag', 'asd']
var [item1, item2, item3] = names
console.log(item1,item2,item3); // agc cag asd

// 2.解构后面元素
var [, itema, itemb] = names
console.log(itema,itemb); // cag asd

// 3.解构出一个元素,后面的元素放到一个新数组中
var [itemx, ...newNames] = names
console.log(newNames); // [ 'cag', 'asd' ]

// 4.解构的默认值 (当解构的元素可能没有值时,可以设置一个默认值)
var [itema, itemb, itemc, itemd = 'aaa'] = names
console.log(itemd); // aaa

2.对象的解构

var obj = {
  name: 'tjx',
  age: 39,
  height: 12,
  a: 12
}

// 1.对象的解构
var { namea, age, height } = obj
console.log(namea, age, height); // tjx 39 12

var { a } = obj
console.log(a); // 12

// 2.解构重命名
var {name: newName} = obj
console.log(newName); // tjx

// 3.解构重命名并给默认值
var { address: newAddress = "广州" } = obj  
console.log(newAddress); // 广州

四、var、const、let

  • let和const,是目前开发中推荐使用的;

  • 优先推荐使用const,这样可以保证数据的安全性不会被随意的篡改;

  • 只有当明确知道一个变量后续会需要被重新赋值时,这个时候再使用let;

1.var

  • 作用域提升;
  • 函数有块级作用域,在函数中var变量属于该函数的局部变量;

2.const

const关键字是constant的单词的缩写,表示常量、衡量的意思;

它表示保存的数据一旦被赋值,就不能被修改;

但是如果赋值的是引用类型,那么可以通过引用找到对应的对象,修改对象的内容;

3.let

声明的变量具有块级作用域的特征;

五、模板字符串

1.基本使用

let agea = 9
const info = `age: ${agea *2}`
console.log(info); // age: 18

function doubleAge() {
  return agea * 2
}
const infoa = `age: ${doubleAge()}`
console.log(infoa); // age: 18

2.标签模板字符串

// 第一个参数依然是模板字符串中整个字符串,只是被切割成多块,放到了一个数组中
// 第二个参数是模块字符串中,第一个${},一次类推
function foo(m, n, x) {
  console.log(m,n, x, '..........');
}
const named = 'tjx'
const aged = 13

// 通过模板标签调用函数
foo`Hello${named}, world${aged},!`  //  [ 'Hello', ', world', ',!' ] tjx 13 ..........

六、函数的参数

1.函数的默认参数

// 1.函数默认参数
function foo(m = 'aaa', n = 'bb') {
  console.log(m,n);
}
foo() // aaa bb
foo('dfs','fds') // dfs fds

// 对象参数和默认参数以及解构
// 写法一:
function printInfo({name, age} = {name: 'tjc', age: 19}) {
  console.log(name,age);
}
printInfo() // tjc 19
printInfo({name: 'lobe', age: 90}) // lobe 90

// 写法二:
function printInfo({name = 'tjx', age = 19} = {}) {
  console.log(name,age);
}

2.函数的剩余参数

function foo(m,n,...args) {
  console.log(m,n); // 1 2
  console.log(args);  // [ 3, 4, 5, 6, 7 ]
  console.log(arguments);  //  [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6, '6': 7 }
}
foo(1,2,3,4,5,6,7)

3.箭头函数的补充

  • 箭头函数没有prototype,不能通过new关键字创建箭头函数;
  • 箭头函数没有this以及arguments

七、展开运算符

const nameg = ['asd', 'dfg', 'sdfg']
const nameh = 'tjx'
const infog = {name: 'tjx', age: 19}

// 1.函数调用时
function foo(x, y, z) {
  console.log(x,y,z);
}

foo(...nameg) // asd dfg sdfg
foo(...nameh) // t j x

// 2.构造数组时
const newNameg = [...nameg, ...nameh]
console.log(newNameg); // [ 'asd', 'dfg', 'sdfg', 't', 'j', 'x' ]

// 3.构造对象字面量时
const objh = {...infog, address: 'guand'}
console.log(objh); // { name: 'tjx', age: 19, address: 'guand' }

八、数值的表示

const num1 = 100 // 十进制
const num2 = 0b100 // 二进制
const num3 = 0o100 // 八进制
const num4 = 0x100 // 十六进制

console.log(num1, num2, num3, num4); // 100 4 64 256

// 大的数值的连接符
const num = 10_000_000_000_000
console.log(num); // 10000000000000

九、Symbol

在ES6之前,对象的属性名都是字符串形式,那么很容易造成属性名的冲突; 比如原来有一个对象,我们希望在其中添加一个新的属性和值,但是我们在不确定它原来内部有什么内容的情况下, 很容易造成冲突,从而覆盖掉它内部的某个属性;Symbol就是为了解决上面的问题,用来生成一个独一无二的值。

1.Symbol的使用

  • Symbol值是通过Symbol函数来生成的,生成后可以作为属性名; 在ES6中,对象的属性名可以使用字符串,也可以使用Symbol值
  • Symbol即使多次创建值,它们也是不同的:Symbol函数执行后每次创建出来的值都是独一无二的;
  • 也可以在创建Symbol值的时候传入一个描述description:这个是(ES10)新增的特性;
// 1.Symbol的基本使用
const symbol1 = Symbol()
const symbol2 = Symbol()
console.log(symbol1 === symbol2); // false

// 2.description描述符
const symbol3 = Symbol('aaa')
console.log(symbol3.description); // aaa

// 3.Symbol值最为key
// 3.1 在定义对象字面量时使用
const obj = {
  [symbol1]: 'abc',
  [symbol2]: 'dfa'
}
// 3.2 新增属性
obj[symbol3] = 'nab'
// 3.3 Object.defineProperty方式

const symbol4 = Symbol()
Object.defineProperty(obj, symbol4, {
  enumerable: true,
  configurable: true,
  writable: true,
  value: 'mag'
})
console.log(obj[symbol1], obj[symbol2], obj[symbol3], obj[symbol4]); // abc dfa nab mag
// 注意:不能通过 . 的方式获取 结果:undefined
console.log(obj.symbol1); // undefined

// 4.使用Symbol作为key的属性名,在遍历/Object.keys等中是获取不到这些symbol值,需要Object.getOwnPropertySymbols来获取所有Symbol的key
console.log(Object.keys(obj)); // []
console.log(Object.getOwnPropertyNames(obj)); // []
console.log(Object.getOwnPropertySymbols(obj)); // [ Symbol(), Symbol(), Symbol(aaa), Symbol() ]
const keys = Object.getOwnPropertySymbols(obj)
for (const sKey of keys) {
  console.log(obj[sKey]); // 输出如下:
                          // abc
                          // dfa
                          // nab
                          // mag
}

// 5.创建相同的Symbol   Symbol.for(key)/Symbol.keyFor(symbol)
const a = Symbol.for('aaa')
const b = Symbol.for('aaa')
console.log(a); // Symbol(aaa)
console.log(a === b); // true

const keya = Symbol.keyFor(a) // 获取aaa用于通过for()创建相同的symbol
console.log(keya); // aaa
const c = Symbol.for(keya)
console.log(a === c); // true

十、Set

1.Set的基本使用

在ES6中新增了另外两种数据结构:Set、Map,以及它们的另外形式WeakSet、WeakMap。

  • Set是一个新增的数据结构,可以用来保存数据,类似于数组,但是和数组的区别是元素不能重复
  • 创建Set我们需要通过Set构造函数(暂时没有字面量创建的方式)
// 1.创建Set
const set = new Set();
set.add(10)
set.add(20)
set.add(30)
set.add({})
console.log(set); // Set(4) { 10, 20, 30, {} }  set中内容不能重复
set.add({})
console.log(set); // Set(6) { 10, 20, 30, {}, {} }  这里set存放的是地址,所以两个对象
var obj = {}
set.add(obj)
set.add(obj)
set.add(obj)
console.log(set); // Set(6) { 10, 20, 30, {}, {}, {} }

// 2.数组去重
const arr = [33, 10, 26, 30, 33, 26]
const arrSet = new Set(arr)
const newArr1 = Array.from(arrSet)
const newArr2 = [...arrSet] // set也支持展开运算符
console.log(newArr1); // [ 33, 10, 26, 30 ]
console.log(newArr2); // [ 33, 10, 26, 30 ]

2.Set的常见方法

Set常见属性:

  • size:返回Set中的元素的个数;

Set常见方法:

  • add(value):添加某个元素,返回Set对象本身;
  • delete(value):从set中删除和这个值相等的元素,返回boolean类型;
  • has(value):判断set中是否存在某个元素,返回boolean类型;
  • clear():清空set中所有的元素,没有返回值;
  • forEach(callback, [, thisArg]):通过forEach遍历set;

**另外:**Set是支持for of的遍历的。

const arr1 = [33, 10, 26, 30, 33, 26]
const arrSet1 = new Set(arr1)

// 1.set的属性 size
console.log(arrSet1.size); // 4

// 2.set的方法
arrSet1.add(1000)
console.log(arrSet1); // Set(5) { 33, 10, 26, 30, 1000 }

arrSet1.delete(1000) // 删除这里只能传入值,不是下标
console.log(arrSet1); // Set(4) { 33, 10, 26, 30 }

console.log(arrSet1.has(33)); // true 判断是否有这个值,返回布尔值

arrSet1.clear()
console.log(arrSet1); // Set(0) {}

// 遍历Set
const arr2 = [33, 10, 26, 30, 33, 26]
const arrSet2 = new Set(arr2)

arrSet2.forEach(item => {
  console.log(item);
})

for (const item of arrSet2) {
  console.log(item);
}

3.WeakSet

一般比较少用。

(1) Set与WeakSet的区别
  • 区别一:WeakSet中只能存放对象类型,不能存放基本数据类型;
  • 区别二:WeakSet对元素的引用是弱引用,如果没有其他引用对某个对象进行引用,那么GC可以对该对象进行回收;
// 区别一:只能存放对象类型
let weakSet = new WeakSet()
// weakSet.add(10) // 报错

// 区别二:对对象是一个弱引用
let obj3 = {
  name: 'tjx'
}
weakSet.add(obj3) // 弱引用
console.log(weakSet); // WeakSet { <items unknown> }

const set3 = new Set()
set.add(obj3) // 强引用
(2) WeakSet常见方法
  • add(value):添加某个元素,返回WeakSet对象本身;
  • delete(value):从WeakSet中删除和这个值相等的元素,返回boolean类型;
  • has(value):判断WeakSet中是否存在某个元素,返回boolean类型;

注意:WeakSet只是对对象的弱引用,如果我们遍历获取到其中的元素,那么有可能造成对象不能正常的销毁。 所以存储到WeakSet中的对象是没办法获取的;

十一、Map

数据结构是Map,用于存储映射关系

对象存储映射关系只能用字符串(ES6新增了Symbol)作为属性名(key);

某些情况下我们可能希望通过其他类型作为key,比如对象,这是我们可以使用Map

1.Map的常用方法

  • size:返回Map中元素的个数;
  • set(key, value):在Map中添加key、value,并且返回整个Map对象;
  • get(key):根据key获取Map中的value; phas(key):判断是否包括某一个key,返回Boolean类型;
  • delete(key):根据key删除一个键值对,返回Boolean类型;
  • clear():清空所有的元素;
  • forEach(callback, [, thisArg]):通过forEach遍历Map;

另外:

  • Map也可以通过for of进行遍历。
let obj4 = {
  name: 'tjx'
}, obj5 = {
  age: 12
}
// 1.map的创建
const map = new Map()
map.set(obj4, 'aaa')
map.set(obj5, 'bbb')
map.set(1, 'ccc')
console.log(map); // Map(3) { { name: 'tjx' } => 'aaa', { age: 12 } => 'bbb', 1 => 'ccc' }

// 2.map的常见属性和方法
console.log(map.size); // 3
map.set('tjx', 'ddd')
console.log(map); // 输出如下:
                              // Map(4) {
                              //   { name: 'tjx' } => 'aaa',
                              //   { age: 12 } => 'bbb',
                              //   1 => 'ccc',
                              //   'tjx' => 'ddd'
                              // }
console.log(map.get('tjx')); // ddd

console.log(map.has(1)); // true

map.delete('tjx')
console.log(map); // Map(3) { { name: 'tjx' } => 'aaa', { age: 12 } => 'bbb', 1 => 'ccc' }

// map.clear()
// console.log(map); // Map(0) {}

// 3.map的遍历
map.forEach((item, key) => {
  console.log(item, key); // 输出如下:
                          // aaa { name: 'tjx' }
                          // bbb { age: 12 }
                          // ccc 1
})

for(const item of map) {
  console.log(item);  // 输出如下:
                      // [ { name: 'tjx' }, 'aaa' ]
                      // [ { age: 12 }, 'bbb' ]
                      // [ 1, 'ccc' ]
}

for (const [key, value] of map) {
  console.log(key, value); // 输出如下:
                            // { name: 'tjx' } aaa
                            // { age: 12 } bbb
                            // 1 ccc
}

2.WeakMap

(1)Map与WeakMap的区别
  • 区别一:WeakMap的key只能使用对象,不接受其他的类型作为key;
  • 区别二:WeakMap的key对对象想的引用是弱引用,如果没有其他引用引用这个对象,那么GC可以回收该对象;
(2)WeakMap的常用方法
  • set(key, value):在Map中添加key、value,并且返回整个Map对象;
  • get(key):根据key获取Map中的value;
  • has(key):判断是否包括某一个key,返回Boolean类型;
  • delete(key):根据key删除一个键值对,返回Boolean类型;
(3)WeakMap的应用

vue3响应式原理。

// 引用场景(vue3响应式原理)
const obj1 = {
  name: 'tjx',
  age: 19
}
function obj1NameFn1() {
  console.log("obj1NameFn1被执行");
}
function obj1NameFn2() {
  console.log("obj1NameFn2被执行");
}
function obj1AgeFn1() {
  console.log("obj1AgeFn1被执行");
}
function obj1AgeFn2() {
  console.log("obj1AgeFn2被执行");
}

const obj3 = {
  name: 'aaa',
  age: 09
}
function obj3NameFn1() {
  console.log("obj3NameFn1被执行");
}
function obj3NameFn2() {
  console.log("obj3NameFn2被执行");
}

// 1.创建WealMap
const weakMap = new WeakMap()

// 2.收集依赖结构
// 2.1 对obj1收集的数据结构
const obj1Map = new Map()
obj1Map.set('name', [obj1NameFn1, obj1NameFn2])
obj1Map.set('age', [obj1AgeFn1, obj1AgeFn2])
weakMap.set(obj1, obj1Map)

// 3.如果obj1.name发生了变化
// Proxy/Object.definePoroperty
obj1.name = 'java'
const targetWeakMap = weakMap.get(obj1)
const fns = targetWeakMap.get('name')
fns.forEach(item => item())

十二、Porxy

1.监听对象的方式

通过Object.defineProperty的get和set监听对象属性的变化。

const obj = {
  name: 'tjx',
  age: 19
}

Object.keys(obj).forEach(key => {
  let value = obj[key]

  Object.defineProperty(obj, key, {
    get: function() {
      console.log(`监听到obj对象的${key}属性被访问`);
      return value
    },
    set: function(newValue) {
      console.log(`监听到obj对象的${key}属性被修改`);
      value = newValue
    }
  })
})
obj.name = 'kis'
obj.age = 30

console.log(obj.name);
console.log(obj.age);
// 传统监听方法的弊端,不能监听新的属性的添加
obj.height = 1.88

缺点:如果监听更加丰富的操作,比如新增属性、删除属性,那么 Object.defineProperty是无能为力的。所以可以通过Proxy类

2.Proxy的基本使用

在ES6中,新增了一个Proxy类,这个类用于帮助我们创建一个代理。先创建一个代理对象(Proxy对象);之后对该对象的所有操作,都通过代理对象来完成,代理对象可以监听我们想要对原对象进行哪些操作;

  • 首先new Proxy对象,传入需要侦听的对象以及一个处理对象
  • 在处理对象中进行监听,之后的操作都是直接对Proxy的操作,而不是原有的对象;
const obj = {
  name: 'tjx',
  age: 19
}

const objProxy = new Proxy(obj, {
  // 获取值时的捕获器
  // target为obj对象,key为变化的属性名
  get: function(target, key) {
    console.log(`监听到对象的${key}属性被访问了`, target);
    return target[key]
  },
  // 设置值时的捕获器
  set: function(target, key, newValue) {
    console.log(`监听到对象的${key}属性被设置值`, target);
    target[key] = newValue
  }
})

console.log(objProxy.name);
console.log(objProxy.age);

objProxy.name = 'xja'
objProxy.age = 39

console.log(obj.name); // xja
console.log(obj.age); // 39

3.Proxy的捕获器

13个常用捕获器。

    1. handler.getPrototypeOf()
    • Object.getPrototypeOf 方法的捕捉器。
    1. handler.setPrototypeOf()
    • Object.setPrototypeOf 方法的捕捉器。
    1. handler.isExtensible()
    • Object.isExtensible 方法的捕捉器。
    1. handler.preventExtensions()
    • Object.preventExtensions 方法的捕捉器。
    1. handler.getOwnPropertyDescriptor()
    • Object.getOwnPropertyDescriptor 方法的捕捉器。
    1. handler.defineProperty()
    • Object.defineProperty 方法的捕捉器。
    1. handler.ownKeys()
    • Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。
    1. handler.has()
    • in 操作符的捕捉器。
    1. handler.get()
    • 属性读取操作的捕捉器。
    1. handler.set()
    • 属性设置操作的捕捉器。
    1. handler.deleteProperty()
    • delete 操作符的捕捉器。
    1. handler.apply()
    • 函数调用操作的捕捉器。
    1. handler.construct()
    • new 操作符的捕捉器。
const obj = {
  name: "why", // 数据属性描述符
  age: 18
}

// 变成一个访问属性描述符
// Object.defineProperty(obj, "name", {

// })

const objProxy = new Proxy(obj, {
  // 获取值时的捕获器
  get: function(target, key) {
    console.log(`监听到对象的${key}属性被访问了`, target)
    return target[key]
  },

  // 设置值时的捕获器
  set: function(target, key, newValue) {
    console.log(`监听到对象的${key}属性被设置值`, target)
    target[key] = newValue
  },

  // 监听in的捕获器
  has: function(target, key) {
    console.log(`监听到对象的${key}属性in操作`, target)
    return key in target
  },

  // 监听delete的捕获器
  deleteProperty: function(target, key) {
    console.log(`监听到对象的${key}属性in操作`, target)
    delete target[key]
  }
})


// in操作符
// console.log("name" in objProxy)

// delete操作
delete objProxy.name

对函数对象的监听:

function foo() {

}
const fooProxy = new Proxy(foo, {
  apply: function(target, thisArg, argArray) {
    console.log("对foo函数进行了apply调用");
    return target.apply(thisArg, argArray)
  },
  construct: function(target, argArray, newTarget) {
    console.log("对foo函数进行了new调用");
    return new target(...argArray)
  }
})
fooProxy.apply({}, ['a','b'])
new fooProxy('a','b')

十三、Reflect

    1. Reflect也是ES6新增的一个API,它是一个对象,字面的意思是反射。
    1. 那么这个Reflect有什么用呢?
    • 它主要提供了很多操作JavaScript对象的方法,有点像Object中操作对象的方法;
    • 比如Reflect.getPrototypeOf(target)类似于Object.getPrototypeOf();
    • 比如Reflect.defineProperty(target, propertyKey, attributes)类似于Object.defineProperty() ;
    1. 如果我们有Object可以做这些操作,那么为什么还需要有Reflect这样的新增对象呢?
    • 这是因为在早期的ECMA规范中没有考虑到这种对 对象本身 的操作如何设计会更加规范,所以将这些API放到了Object上面;
    • 但是Object作为一个构造函数,这些操作实际上放到它身上并不合适;
    • 另外还包含一些类似于 in、delete操作符,让JS看起来是会有一些奇怪的;
    • 所以在ES6中新增了Reflect,让我们这些操作都集中到了Reflect对象上;
    1. 那么Object和Reflect对象之间的API关系,可以参考MDN文档: 比较 Reflect 和 Object 方法 - JavaScript | MDN (mozilla.org)

1.Reflect的基本使用

const obj = {
  name: 'tjx',
  age: 19
}

const objProxy = new Proxy(obj, {
  get: function(target, key, reciver) {
    console.log("get");
    return Reflect.get(target, key)
  },
  set: function(target, key, newValue, receiver) {
    console.log("set");
    target[key] = newValue
    // target[key] = newValue 与通过Reflect.set的区别是:后者有返回布尔值,代表是设置成功
    const result = Reflect.set(target, key, newValue)
    if(result) {0} else {}
  }
})
objProxy.name = 'dfs'
console.log(objProxy.name);

2.Reflect的常见方法

    1. Reflect.getPrototypeOf(target)
    • 类似于 Object.getPrototypeOf()。
    1. Reflect.setPrototypeOf(target, prototype)
    • 设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返 回true。
    1. Reflect.isExtensible(target)
    • 类似于 Object.isExtensible()
    1. Reflect.preventExtensions(target)
    • 类似于 Object.preventExtensions()。返回一个Boolean。
    1. Reflect.getOwnPropertyDescriptor(target, propertyKey)
    • 类似于 Object.getOwnPropertyDescriptor()。如果对象中存在 该属性,则返回对应的属性描述符, 否则返回 undefined.
    1. Reflect.defineProperty(target, propertyKey, attributes)
    • 和 Object.defineProperty() 类似。如果设置成功就会返回 true
    1. Reflect.ownKeys(target)
    • 返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable影响).
    1. Reflect.has(target, propertyKey)
    • 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。
    1. Reflect.get(target, propertyKey[, receiver])
    • 获取对象身上某个属性的值,类似于 target[name]。
    1. Reflect.set(target, propertyKey, value[, receiver])
    • 将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。
    1. Reflect.deleteProperty(target, propertyKey)
    • 作为函数的delete操作符,相当于执行 delete target[name]。
    1. Reflect.apply(target, thisArgument, argumentsList)
    • 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。
    1. Reflect.construct(target, argumentsList[, newTarget])
    • 对构造函数进行 new 操作,相当于执行 new target(…args)。

3.Reflect中的construct

// 作用:创建的一个对象,但是这个对象的方式是另一个对象的方法
function Students(name, age) {
  this.name = name
  this.age = age
}
function Teachers() {

}
// 执行Students函数中的内容,但是创建出的对象是Teacher对象
const teachers = Reflect.construct(Students, ['saf', 'sf'], Teachers)
console.log(teachers); // Teachers { name: 'saf', age: 'sf' }
console.log(teachers.__proto__ === Teachers.prototype); // true

4.receiver的作用

如果我们的源对象(obj)有setter、getter的访问器属性,那么可以通过receiver来改变里面的this;

const obj = {
  _name: 'ujx',
  get name() {
    return this._name // 传递了reveiver后这里的this指向objProxy
  },
  set name(newValue) {
    this._name = newValue
  }
}

const objProxy = new Proxy(obj, {
  get: function(target, key, receiver) {
    // reveiver 是创建出来的代理对象
    console.log("get方法别访问---", key, receiver); 
    return Reflect.get(target, key, receiver)
  },
  set: function(target, key, newValue, receiver) {
    console.log("set方法被访问------",key);
    Reflect.set(target, key, newValue, receiver)
  }
})
objProxy.name = 'ksafa'  // 返回了两次 set方法被调用---- 但是两次key值不同,第一次为name,第二次为_name

ES7

1.数组includes方法

在ES7之前,如果我们想判断一个数组中是否包含某个元素,需要通过 indexOf 获取结果,并且判断是否为 -1。

在ES7中,我们可以通过includes来判断一个数组中是否包含一个指定的元素,根据情况,如果包含则返回 true, 否则返回false。

const arrays = ['aba', 'cag', 'aba', 'dca', NaN]
if (arrays.indexOf('aba') !== -1) {
  console.log("包含aba"); // 包含aba
}

// ES7
console.log(arrays.includes('aba')); // true
console.log(arrays.includes('aba', 3)); // false  第二个参数表示从第几个开始查找是否包含该元素

// includes与indexof的区别: indexOf不能查找NaN
console.log(arrays.indexOf(NaN)); // -1
console.log(arrays.includes(NaN)); // true

2.指数运算符

const result = Math.pow(3,3)
const result2 = 3 ** 3
console.log(result, result2); // 27 27

ES8

1. Object values

之前我们可以通过 Object.keys 获取一个对象所有的key,在ES8中提供了 Object.values 来获取所有的value值。

const obj = {
  name: 'tjx',
  age: 18
}

console.log(Object.keys(obj)); // [ 'name', 'age' ]
console.log(Object.values(obj)); // [ 'tjx', 18 ]
// 用到非常少
console.log(Object.values(['abc', 'dxa', 'dsx'])); // [ 'abc', 'dxa', 'dsx' ]
console.log(Object.values("abx")); // [ 'a', 'b', 'x' ]

2. Object entries

通过Object.entries 可以获取到一个数组,数组中会存放可枚举属性的键值对数组

const obj = {
  name: 'tjx',
  age: 18
}

console.log(Object.entries(obj)); // [ [ 'name', 'tjx' ], [ 'age', 18 ] ]
const objEntries = Object.entries(obj)
objEntries.forEach(item => {
  console.log(item[0], item[1]);
})

console.log(Object.entries(['abc', 'sgc', 'aej'])); // [ [ '0', 'abc' ], [ '1', 'sgc' ], [ '2', 'aej' ] ]
console.log(Object.entries('adc')); // [ [ '0', 'a' ], [ '1', 'd' ], [ '2', 'c' ] ]

3.String Padding

ES8中增加了 padStart 和 padEnd 方法,分 别是对字符串的首尾进行填充的。

const message = "hello world"

const newMessage = message.padStart(15, '*').padEnd(20, '-')
console.log(newMessage); // ****hello world-----

// 案例:银行卡位数的隐藏
const cardNumber = "52208420390323232"
const lastFourCard = cardNumber.slice(-4)
const finalCard = lastFourCard.padStart(cardNumber.length, '*')
console.log(finalCard); // *************3232

4.Trailing Commas

在ES8中,我们允许在函数定义和调用时多加一个逗号。

function foo(a,b,) {
  console.log(a,b);
}
foo(10,20,)

5.Object Descriptors

n ES8中增加了另一个对对象的操作是 Object.getOwnPropertyDescriptors

ES9

一、迭代器iterators

二、promise的finally

ES10

1.flat与flatMap

(1)flat

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返 回。

(2)flatMap

flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。

即:先进行map操作,再进行flat操作。

// 1.flat的使用
const nums = [10, 20, [2, 3], 90, [1, 4, [2, 4]], 90]
const newNums = nums.flat()
console.log(newNums); // 默认进行一次降维  [ 10, 20, 2, 3, 90, 1, 4, [ 2, 4 ], 90 ]  
const newNums2 = nums.flat(2)  // 进行两次降维
console.log(newNums2);  // [ 10, 20, 2, 3, 90, 1,  4, 2, 4, 90 ]

// 2.flatMap的使用
const nums2 = [10, 38, 40]
const newNums3 = nums2.flatMap(item => {
  return item * 2
})
const newNums4 = nums2.map(item => {
  return item * 2
}) 
console.log(newNums3); // [ 20, 76, 80 ]
console.log(newNums4); // [ 20, 76, 80 ]

// 3.flatMap的应用
const messaes = ['hello world', 'hello lyh', 'my name is loc']
const words = messaes.flatMap(item => {
  return item.split(' ')
})
console.log(words);  // 输出如下:
                      // [
                      //   'hello', 'world',
                      //   'hello', 'lyh',
                      //   'my',    'name',
                      //   'is',    'loc'
                      // ]
const words2 = messaes.map(item => {
  return item.split(' ') 
})
console.log(words2); // 输出如下:
                      // [
                      //   [ 'hello', 'world' ],
                      //   [ 'hello', 'lyh' ],
                      //   [ 'my', 'name', 'is', 'loc' ]
                      // ]
// 区别:flatMap是先进行map操作,再进行flat操作

2.entries转对象

const obj = {
  name: 'tjx',
  age: 19,
  height: 1.88
}
const entries = Object.entries(obj)
console.log(entries); // [ [ 'name', 'tjx' ], [ 'age', 19 ], [ 'height', 1.88 ] ]
// 传统方法
const newObj1 = {}
for (const entry of entries) {
  newObj1[entry[0]] = entry[1]
}
console.log(newObj1); // { name: 'tjx', age: 19, height: 1.88 }
// 使用ES10新增方法
const newObj2 = Object.fromEntries(entries)
console.log(newObj2); // { name: 'tjx', age: 19, height: 1.88 }
console.log("(((((((((");
// 应用
const queryString = 'name=tjx&age=19&height=1.88'
const queryParams = new URLSearchParams(queryString)
console.log(queryParams); // URLSearchParams { 'name' => 'tjx', 'age' => '19', 'height' => '1.88' }
for (const param of queryParams) {
  console.log(param); // 输出如下:
                                // [ 'name', 'tjx' ]
                                // [ 'age', '19' ]
                                // [ 'height', '1.88' ]
}
const paramObj = Object.fromEntries(queryParams)
console.log(paramObj); // { name: 'tjx', age: '19', height: '1.88' }

3.trimStart与trimEnd

trimStart 去除字符串前面的空格,trimEnd去除字符串后面的空格。

4. Symbol description

5. Optional catch binding

ES11

1.BigInt

数据类型BigInt,用于表示大的整数

const maxInt = Number.MAX_SAFE_INTEGER
console.log(maxInt); // 9007199254740991

const bigInt = 9007199254740991100n;  // 使用n表示BigInt
// console.log(bigInt + 10); // 报错,只能同类型运算
console.log(bigInt + 10n); // 9007199254740991110n

const num = 100
console.log(bigInt + BigInt(num)); // 9007199254740991000

const smallNum = Number(bigInt)
console.log(smallNum); // 9007199254740991000

2.空值合并运算符

const fooa = ''
const result1 = fooa || '默认值'
const result2 = fooa ?? '默认值'
console.log(result1); // 默认值
console.log(result2); // ''
// 作用:有时为了那个值就是空字符串,使用 || 会出现不想要的错误,所以使用 ?? 

3.可选链

可选链也是ES11中新增一个特性,主要作用是让我们的代码在进行nullundefined判断时更加清晰和简洁

const obj = {
  friend: {
    girlFriend: {
      name: 'luck'
    }
  }
}
// 传统方式需要一个一个判断
if (obj.friend && obj.friend.girlFriend) {
  console.log(obj.friend.girlFriend.name);
}
// 可选链
console.log(obj.friend?.girlFriend?.name);

4.globalThis

浏览器中globalThis指向window,在node中globalThis指向node的全局变量

ES12

1.FinalizationRegistry

FinalizationRegistry 对象可以让你在对象被垃圾回收时请求一个回调。

FinalizationRegistry 提供了这样的一种方法:当一个在注册表中注册的对象被回收时,请求在某个时间点上调 用一个清理回调。(清理回调有时被称为 finalizer ); 你可以通过调用register方法,注册任何你想要清理回调的对象,传入该对象和所含的值;

let objp = {
  name: 'tjx'
}
const registry = new FinalizationRegistry(value => {
  console.log("对象被销毁了", value);
})
registry.register(objp, 'obj')

objp = null

2.WeakRefs

如果我们默认将一个对象赋值给另外一个引用,那么这个引用是一个强引用: 如果我们希望是一个弱引用的话,可以使用WeakRef

let obj = { name: 'tjx'}
let inof = new WeakRef(obj)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值