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.类的访问器方法
当调用获取或设置某个属性时,触发get或set函数里的函数体代码。
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个常用捕获器。
- handler.getPrototypeOf()
- Object.getPrototypeOf 方法的捕捉器。
- handler.setPrototypeOf()
- Object.setPrototypeOf 方法的捕捉器。
- handler.isExtensible()
- Object.isExtensible 方法的捕捉器。
- handler.preventExtensions()
- Object.preventExtensions 方法的捕捉器。
- handler.getOwnPropertyDescriptor()
- Object.getOwnPropertyDescriptor 方法的捕捉器。
- handler.defineProperty()
- Object.defineProperty 方法的捕捉器。
- handler.ownKeys()
- Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。
- handler.has()
- in 操作符的捕捉器。
- handler.get()
- 属性读取操作的捕捉器。
- handler.set()
- 属性设置操作的捕捉器。
- handler.deleteProperty()
- delete 操作符的捕捉器。
- handler.apply()
- 函数调用操作的捕捉器。
- 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
- Reflect也是ES6新增的一个API,它是一个对象,字面的意思是反射。
- 那么这个Reflect有什么用呢?
- 它主要提供了很多操作JavaScript对象的方法,有点像Object中操作对象的方法;
- 比如Reflect.getPrototypeOf(target)类似于Object.getPrototypeOf();
- 比如Reflect.defineProperty(target, propertyKey, attributes)类似于Object.defineProperty() ;
- 如果我们有Object可以做这些操作,那么为什么还需要有Reflect这样的新增对象呢?
- 这是因为在早期的ECMA规范中没有考虑到这种对 对象本身 的操作如何设计会更加规范,所以将这些API放到了Object上面;
- 但是Object作为一个构造函数,这些操作实际上放到它身上并不合适;
- 另外还包含一些类似于 in、delete操作符,让JS看起来是会有一些奇怪的;
- 所以在ES6中新增了Reflect,让我们这些操作都集中到了Reflect对象上;
- 那么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的常见方法
- Reflect.getPrototypeOf(target)
- 类似于 Object.getPrototypeOf()。
- Reflect.setPrototypeOf(target, prototype)
- 设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返 回true。
- Reflect.isExtensible(target)
- 类似于 Object.isExtensible()
- Reflect.preventExtensions(target)
- 类似于 Object.preventExtensions()。返回一个Boolean。
- Reflect.getOwnPropertyDescriptor(target, propertyKey)
- 类似于 Object.getOwnPropertyDescriptor()。如果对象中存在 该属性,则返回对应的属性描述符, 否则返回 undefined.
- Reflect.defineProperty(target, propertyKey, attributes)
- 和 Object.defineProperty() 类似。如果设置成功就会返回 true
- Reflect.ownKeys(target)
- 返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable影响).
- Reflect.has(target, propertyKey)
- 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。
- Reflect.get(target, propertyKey[, receiver])
- 获取对象身上某个属性的值,类似于 target[name]。
- Reflect.set(target, propertyKey, value[, receiver])
- 将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。
- Reflect.deleteProperty(target, propertyKey)
- 作为函数的delete操作符,相当于执行 delete target[name]。
- Reflect.apply(target, thisArgument, argumentsList)
- 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。
- 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中新增一个特性,主要作用是让我们的代码在进行null和undefined判断时更加清晰和简洁
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)