之前介绍了js中的函数式编程,面向对象编程,异步编程
,今天在学习一个新的元编程(Metaprogramming)
。
一:函数式编程
函数式编程是一种编程范式,它把计算过程尽量写成一系列嵌套的函数调用。在函数式编程中,函数被认为是第一类公民,可以被赋值给变量,也可以作为参数或返回值。函数式编程强调纯函数和不可变性,避免副作用和共享状态。
js三座大山之函数1
js三座大山之函数-静态词法作用域
js三座大山之函数-运行时this的四种指向
二:面向对象编程
面向对象编程是一种编程范式,它使用类和对象来组织代码。在面向对象编程中,对象是一组属性和方法的集合,类是创建对象的模板。面向对象编程强调封装、继承和多态。
js三座大山之对象,继承,类,原型链
三:异步编程
异步编程是一种编程风格,它用于处理非阻塞操作,如网络请求、文件 I/O 等。在异步编程中,当一个操作被调用时,程序不会等待这个操作完成,而是立即继续执行下一行代码。当这个操作完成时,会通过回调函数、Promise 或 async/await 来获取结果
js异步编程专栏
四:元编程
js元编程: Symbol&Reflect&Proxy
js基石之Symbol值
js元编程:妙用proxy实现add[1][2][3] + 4
什么是元编程:
元编程是指编写可以操作或改变其他程序的程序。元编程可以改变 JavaScript 的一些基本操作的行为。
主要与这三个对象有关。
Symbol:通过内置Symbol值复写 js语言中的基本操作
。
Reflect:可以获取语言内部的基本操作
。
Proxy:通过钩子函数 拦截&改变 js语言的基本操作
。
什么是基本操作
基本操作包括属性访问和赋值
、属性删除
、枚举
、函数调用
、对象构造
等等
属性访问和赋值:我们可以使用 . 或 [] 来访问对象的属性,使用 = 来给属性赋值。
let obj = {a: 1};
console.log(obj.a); // 1
obj.b = 2;
console.log(obj['b']); // 2
属性删除
var myObj = { foo: 'bar' };
delete myObj.foo;
枚举
var obj = {
name: 'test',
age: 18
}
for(p in obj){
console.log(obj[p])
}
函数调用
function add(a, b) {
return a + b;
}
const result = add(1, 2);
对象创建
function Person(name, age) {
this.name = name;
this.age = age;
}
const alice = new Person('Alice', 25);
等等还有很多 这里只是举出一些例子,帮助大家理解什么是基本操作方法。
Symbol
通过内置Symbol值复写 js语言中的内部实现 例如常见的类型判断。
Number.prototype[Symbol.toStringTag] = 'String';
let a = 12;
Object.prototype.toString.call(a) // '[object String]'成功的将数字改变为字符串
reflect
获取语言的底层实现。
function Fn(...args){
console.log('test fn', args)
}
Reflect.apply(Fn,null,[1,2,3]) // 调用函数
function Fn(...args){
console.log('test fn', args)
}
Reflect.construct(Fn,[1,2,3]) // 类似new 创建一个实例
为什么不把Reflect的方法直接放在Object上
Reflect 拥有的方法不仅针对于 Object,还可能针对于函数,例如Reflect.apply
,调用 Object.apply(Fn)
看起来太怪了
为什么不使用操作符实现反射的逻辑
typeof
、instanceof
以及 delete
已经作为反射运算符存在了 —— 为此添加同样功能的新关键字将会加重开发者的负担,同时,对于向后兼容性也是一个梦魇,并且会让 JavaScript 中的保留字数量急速膨胀。
proxy
拦截js的基本操作。
- 拦截函数执行&实例化
function Fn(){
console.log('test fn')
return 1;
}
const P = new Proxy(Fn,{
apply(target, object, args){ // 拦截函数执行
console.log('test apply proxy ');
return Reflect.apply(target, object, args);
},
construct(target, args){ // 拦截构造
console.log('test construct proxy ');
return Reflect.construct(target, args);
}
})
P()
// test apply proxy
// test fn
// 1
// 调用构造函数
const p = new P();
// test construct proxy
// test fn
proxy & Object.defineProperty
Object.defineProperty是作用对象的属性的,允许你在一个对象上定义一个新属性或者修改一个已有属性。通过这个方法你可以精确地定义属性的特征,比如它是否可写、可枚举、可配置等。该方法的使用场景通常是需要在一个对象上创建一个属性,然后控制这个属性的行为.
const obj = {};
let v;
Object.defineProperty(obj,'name',{
get(){
console.log('get name', v)
return v;
},
set(value){
console.log('set name', value)
v = value;
}
})
obj.name = 'hhh'
console.log(obj.name)
// set name hhh
// get name hhh
// hhh
proxy用来代理一个对象,但是相比于 Object.defineProperty
,它提供了更加强大的功能。使用 Proxy
可以截获并重定义对象的基本操作,比如访问属性、赋值、函数调用等等。在这些操作被执行之前,可以通过拦截器函数对这些操作进行拦截和修改。因此,通过 Proxy
,你可以完全重写一个对象的默认行为。该方法的使用场景通常是需要对一个对象的行为进行定制化,或者需要在对象上添加额外的功能
const obj = {};
const proxyObj = new Proxy(obj,{
get(target, property, receiver){
console.log(`get ${property}`, target[property])
return Reflect.get(target, property, receiver)
},
set(target, property, value, receiver){
console.log(`set ${property}`, value)
return Reflect.set(target, property, value, receiver);
},
deleteProperty(target, property){ // 拦截属性删除
console.log(`delete ${property}`)
return Reflect.deleteProperty(target, property);
}
})
proxyObj.name = 'test'
console.log(proxyObj.name)
delete proxyObj.name;
console.log(proxyObj.name)
// 结果
set name test
get name test
test
delete name
get name undefined
undefined
区别:
- proxy代理对象 Object.defineProperty拦截对象的属性
- 对象上定义新属性或者删除属性时,Proxy可以监听到,Object.defineProperty监听不到。
- 如果对象内部要全部递归代理,则Proxy可以只在调用时递归,而Object.defineProperty需要在一开始就全部递归。