Symbol
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值
let s1 = Symbol('aa');
let s2 = Symbol('aa');
console.log(s1 === s2); //false
let s3 = Symbol.for('aa'); // 声明全新的
let s4 = Symbol.for('aa') // 把之前声明的拿过来用
console.log(s3 === s4) //true
Symbol属性默认是不能枚举
let obj = {
name:'ccc',
age:111,
[s1]: 'ok'
}
for(let key in obj){
console.log(obj[key]); // "ccc" 111
}
// 获取所有symbol
console.log(Object.getOwnPropertySymbols(obj)); //[ Symbol(aa) ]
// 获取普通类型的key
console.log(Object.keys(obj)); //[ 'name', 'age' ]
元编程的能力
// 判断类型时 Object.prototype.toString.call()
let obj1 = {
[Symbol.toStringTag]:'hhhh'
}
console.log(Object.prototype.toString.call(obj1)) //[object hhhh]
// 隐式类型转化
let obj = {
[Symbol.toPrimitive](type){
return '123'
}
};
console.log(obj + '1');
let instance = {
[Symbol.hasInstance](value){
return 'name' in value
}
}
console.log({name:'aaaa'} instanceof instance); //true
Reflect
- Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法
- Reflect 不是一个构造函数,因此它是不能用 new 构造的
// ES6 后续新增的方法都放在Reflect上
let s1 = Symbol('aaa');
let obj = {
name:'n',
age:12,
[s1]: 'ok'
}
Reflect.ownKeys(obj).forEach(item=>{ // 获取所有的key属性
console.log(item) //name age Symbol(aaa)
})
const fn = (a,b) =>{
console.log('fn',a,b)
}
// 自定了一个apply 会默认调用他
fn.apply = function () { //
console.log('apply');
}
// 想调用函数本身的apply方法如何调用
Function.prototype.apply.call(fn,null,[1,2]);
Reflect.apply(fn,null,[1,2])
Reflect 有13种方法
Reflect.apply(target, thisArg, args)
Reflect.construct(target, args)
Reflect.get(target, name, receiver)
Reflect.set(target, name, value, receiver)
Reflect.defineProperty(target, name, desc)
Reflect.deleteProperty(target, name)
Reflect.has(target, name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
Set/Map
console.log(Object.prototype.toString.call(new Map())); //[object Map]
console.log(Object.prototype.toString.call(new Set())); //[object set]
let set = new Set([1, 2, 1, 1, 2, 1, 1, 3, 'a']);
set.add({ a: 1 });
set.add({ a: 1 }); // 两个对象不相同
console.log(set.entries(set));
console.log(set.has(5))// false
function union(arr1,arr2){
// 内部他有Symbol.iterator方法
let s = new Set([...arr1,...arr2]); // 集合 集合可以被迭代
return [...s]
}
console.log(union(arr1,arr2))
function intersection(arr1,arr2){
let s1 = new Set(arr1); // forEach
let s2 = new Set(arr2);
return [...s1].filter(item=>{
return s2.has(item)
})
}
console.log(intersection(arr1,arr2))
let map = new Map([ // 不能有重复的key,重复的会覆盖掉
['a', 1],
['v', 1],
['v', 1]
])
map.set({a:1},2);
// 他的key 可以使用对象类型
console.log(map)
weakMap 弱引用
class MyTest{};
let my = new MyTest(); // 对象
let map = new WeakMap(); // key 只能是对象
map.set(my,1);
my = null;
//当你给一个变量设置为null的时候 不会马上回收,会在合适的机会自己情况
map 引用的对象 不会被回收掉
weakMap引用的对象被置为null 时,后续会被清空
展开符/深拷贝
let o1 = {name:'zf'};
let o2 = {age:{n:12}};
//展开符是浅拷贝,和 Object.assign()类似
let assgin = {...o1,...o2};
o2.age.n = 13
console.log(assgin) //{ name: 'zf', age: { n: 13 } }
深拷贝
function deepClone(obj,hash = new WeakMap()){// vue3 记录拷贝前和拷贝后的对应关系
if(obj == null) return obj;
if(obj instanceof RegExp) return new RegExp(obj);
if(obj instanceof Date) return new Date(obj);
// .... 基本类型和函数
if(typeof obj !== 'object') return obj;
// 对象类型 obj 数组 :[] 和 对象: {}
//解决循环引用
if(hash.has(obj)) return hash.get(obj); // 返回上次拷贝的结果 不在递归了
const copy = new obj.constructor;
hash.set(obj,copy); // 引用类型
for(let key in obj){
if(obj.hasOwnProperty(key)){
copy[key] = deepClone(obj[key],hash)
}
}
return copy
}
reduce
是个收敛函数 可以把一个数组转化成其他格式
- 数组不能为空 ```javascript let r = ([1, 2, 3, 4, 5]).reduce(function(previousValue, currentValue, index, arrary) { console.log(previousValue, currentValue) return previousValue + currentValue }); / 1 2 3 3 6 4 10 5 / console.log(r);//15
let r = ([1, 2, 3, 4, 5]).reduce(function(previousValue, currentValue, index, arrary) { return previousValue + currentValue },5); console.log(r);//20
手写实现:
```javascript
Array.prototype.reduce = function(callback,prev){
for(let i = 0; i < this.length;i++){
if(!prev){
prev = callback(this[i],this[i+1],i+1,this);
i++; // 下次 从3开始
}else{
prev = callback(prev,this[i],i,this)
}
}
return prev;
}
实现 compose
组合函数
const compose = (...fns) => (...args) => {
let lastFn = fns.pop();
return fns.reduceRight((prev, current) => current(prev), lastFn(...args))
}
const compose = (...fns) => fns.reduce((a, b) => (...args) => a(b(...args)))
Proxy
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
// Vue2 中用的是defineProperty 他的特点就是给本来的属性可以用此方法来定义,
// 并且可以把值转化成 get和set
let obj = {};
// 使用defineProperty 需要定义第三方参数才能 控制set和 get
let _value;
Object.defineProperty(obj, 'a', {
// 描述符号
enumerable: true, // 遍历对象可以被遍历
configurable: true, // 可以被删除
writable:true,
get() {
return _value
},
set(newValue) {
_value = newValue
}
})
obj.a = 100;
console.log(obj.a)
// 把对象的属性 全部转化成 getter + setter,
// 遍历所有对象,用Object.defineProperty重新定义属性 性能不好
// 如果是数组 采用这种方式 性能很差
// 如果对象里面嵌套对象 我需要递归处理
// 没有对obj的属性进行重写,而且不需要递归
// 放访问到的属性是对象时在代理即可
let proxy = new Proxy(obj, {
get() { //proxy.xxx
},
set() { // proxy.xxx = 100
},
has() { // 'xxx' in proxy
},
deleteProperty(){ // 删除属性的时候后执行此方法
},
ownKeys(){ // Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols
}
}); // proxy 是es6 的api 不用改写原对象 ,但是兼容差