* */
console.log(func)
console.log(thisArg)
console.log(argumentsList)
return func.apply(thisArg, argumentsList);
}
}
let proxy = new Proxy(sum,handler)
console.log(proxy(1, 2));
### 三. Proxy与Reflect基本使用
/*
- Proxy作用: 监控对象操作
- Reflect作用: 执行原始操作
- */
let symbol = Symbol(123)
// 定义一个需要被代理的对象
let user = {
“name”:“小明”,
1:2,
[symbol]:“symbol111”
}
// 参数1: 需要代理的对象或函数等
// 参数2: 拦截器
let proxy = new Proxy(user,{
get:function (target, prop, receiver) {
/*
* target: 原始对象
* prop: 属性名,只有两种类型,string|symbol,其他传入的类型都会转为string类型
* receiver: 被代理后的对象
* */
// 直接通过原始对象target[prop]获取属性值
// 不会递归
// let result = target[prop]
// 通过Reflect.get(target, prop, receiver)获取属性值
// 可以递归
let result = Reflect.get(target,prop,receiver)
console.log(`get方法 对象:${JSON.stringify(target)} 属性:${prop.toString()} 返回值:${result}`)
// if (typeof prop=== 'string'){
// console.log(`get方法 对象:${JSON.stringify(target)} 属性:${prop} 返回值:${result}`)
// }else if (typeof prop === "symbol"){
// // symbol不能使用模板字符串,不然会报错,所以这里做了一层判断
// console.log("正在获取: ",prop)
// }
return result
},
set:function (target, prop, newValue, receiver) {
/*
* target: 原始对象
* prop: 属性名
* newValue: 修改的值
* receiver: 被代理后的对象
* */
console.log(`set方法 对象:${JSON.stringify(target)} 属性:${prop} 修改的值:${newValue}`)
target[prop] = newValue
}
})
// 使用name属性时触发get方法
console.log(proxy.name);
// 设置name属性时触发set方法
proxy.name = “雷点1”
// ===================================
console.log(proxy[symbol])
// ===================================
console.log(proxy[1])
// ===================================
// 输出undefined,因为当前代理的对象中不存在这个属性,
// 在补环境中使用代理时,当一个属性或者方法被调用得到的结果是undefined时就需要去进行补环境
console.log(proxy.age)
### 四. 简单模拟proxy应用场景
let window = {
}
// 代理,window
window = new Proxy(window,{
get(target, p, receiver) {
let result = Reflect.get(target,p,receiver)
console.log(get方法 对象:${JSON.stringify(target)} 属性:${p.toString()} 返回值:${result}
)
return result
},
set:function (target, p, newValue, receiver) {
console.log(set方法 对象:${JSON.stringify(target)} 属性:${p.toString} 修改的值:${newValue}
)
target[p] = newValue
}
})
// 假设以下是抠出来的代码,然后调用href,但是href我们在window中没有定义,那么就会输出 返回值undefined,这时候我们就要去补这个href属性
// 根据调用的属性去当前目标网站根据控制台输出,拿到结果然后补到window身上,也可以使用脱环境的方式去拿到浏览器中的环境
console.log(window.href);
这时候window.href属性是undefined,我们就要去当前调试的目标网站将href属性抠出来补到window身上
**注:如果还不了解的话到后面会有实战案例,尽情期待!**
### 五. Proxy封装-1
function BrokerProxy(obj,objName){
/*
* obj: 需要代理的对象
* objName: 代理对象的名字
* 疑问点: 为什么还要传入对象的名字,因为不能直接通过对象来获取对象的名字,所以需要传进来
* /
return new Proxy(
obj,
{ // 拦截器方法
get(target, prop, receiver){
/
* target: 目标对象
* prop: 被获取的属性名
* receiver: 被代理后的对象
* /
let result = Reflect.get(target, prop, receiver) // 反射,执行原始的操作
console.debug(get方法: 对象: ${objName} 属性名: ${prop.toString()} 属性值: ${result}
)
return result
},
set(target, prop, value){
/
* target: 目标对象
* prop: 被修改的属性名
* value: 新属性值
* */
console.debug(set方法: 对象: ${objName} 设置的属性名: ${prop.toString()} 设置的属性值: ${value}
)
target[prop] = value
}
}
)
}
let symbol = Symbol(123)
let window = {
name:“雷点”,
true:2,
[symbol]:“symbol123”,
age:123
}
window = BrokerProxy(window,‘window’)
console.log(window.name);
window.name = “javascript”
代码解读:
1. 首先定义一个函数:BrokerProxy
2. BrokerProxy函数接收两个参数:obj,objName
1. obj: 需要被代理的对象
2. objName: 被代理对象的名称
3. 进入函数内部后,return new Proxy(obj,{get,set}) 返回代理后的对象
1. new Proxy(obj,{get,set})
1. obj:当前被代理的对象
2. get 拦截器方法:调用被代理对象的属性时触发
3. set 拦截器方法:设置被代理对象的属性时触发
4. 在get拦截器方法中接收三个参数:target,prop,receiver
1. target:目标对象
2. prop: 被获取的属性名
3. receiver:被代理后的对象
5. 在get拦截器中的操作:
1. **通过Reflect.get() 执行原始操作,返回被获取的属性**
1. let result = Reflect.get(target,prop,receiver)
2. 日志输出:详细输出了,被调用的对象名,调用的属性名,属性值
1. console.debug(`get方法: 对象: ${objName} 属性名: ${prop.toString()} 属性值: ${result}`)
6. 在set拦截器方法中接收三个参数:target,prop,value
1. target:目标对象
2. prop: 被设置的属性名
3. value:新属性值你
7. 在set拦截器中的操作
1. 日志输出:详细输出了,被设置的对象名,设置的属性名,设置的值
* console.debug(`set方法: 对象: ${objName} 设置的属性名: ${prop.toString()} 设置的属性值: ${value}`)
8. 走到函数外部:
* 定义了一个window对象:window = BrokerProxy(window,'window')
* **在window对象加载后使用自定义封装的BrokerProxy函数对window进行代理,返回被代理后的window对象**
* **通过返回被代理的window对象**获取name操作:console.log(window.name); 这时候就会触发 拦截器方法get方法,并且执行get方法里面的操作
* 通过返回被代理的window对象设置name操作:window.name = "javascript"; 这时候就会触发 拦截器方法set,并且执行set方法里面的操作
### 六. Proxy封装-2
关于proxy封装的第二篇不在做多余的讲解,**详细的注释已经在代码中写上**,如果还不了解可以前往:[Proxy() 构造函数 - JavaScript | MDN]( ) 构造函数 - JavaScript | MDN") 进一步详细的了解
/*
- 代理器方法封装
- */
function getType(obj){
return Object.prototype.toString.call(obj)
}
function BrokerProxy(obj,objName){
/*
* obj: 需要代理的对象
* objName: 代理对象的名字
* 疑问点: 为什么还要传入对象的名字,因为不能直接通过对象来获取对象的名字,所以需要传进来
* /
return new Proxy(
obj,{
// 用于拦截对象的 获取值的 操作
get(target, prop, receiver){
/
* target: 目标对象
* prop: 被获取的属性名
* receiver: 被代理后的对象
* */
let result;
try{
result = Reflect.get(target, prop, receiver) // 反射,执行原始的操作
let type = getType(result)
if(result instanceof Object){
console.debug(get方法[获取]: ${objName}.${prop.toString()} [属性值]: ${JSON.stringify(result)} [类型]:${type}
)
// 如果属性是一个对象的话就进行递归代理
result = BrokerProxy(result,${objName}.${prop.toString()}
)
}else if(typeof result === “symbol”){
console.debug(get方法[获取]: ${objName}.${prop.toString()} [属性值]: ${result.toString()}
)
}else{
console.debug(get方法[获取]: ${objName}.${prop.toString()} [属性值]: ${result}
)
}
}catch (e) {
console.error(get方法[获取]: 对象: ${objName} [属性]:${prop.toString()} [错误信息]: ${e.message}
)
}
return result
},
// 用于拦截对象的 设置值的 操作
set(target, prop, value){
/*
* target: 目标对象
* prop: 被修改的属性名
* value: 新属性值
* */
// 使用 Reflect.set
try{
let success = Reflect.set(target, prop, value);
let type = getType(value)
if(value instanceof Object){
console.debug(`set方法[设置]: ${objName}.${prop.toString()} [设置的属性值]: ${JSON.stringify(value)} [类型]: ${type}`)
}else if (typeof value === "symbol"){
console.debug(`set方法[设置]: ${objName}.${prop.toString()} [设置的属性值]: ${value.toString()}`)
}else{
console.debug(`set方法[设置]: ${objName}.${prop.toString()} [设置的属性值]: ${value}`)
}
}catch (e) {
console.error(`设置属性失败: ${objName}.${prop.toString()}`);
}
},
// 用于拦截对象的 Object.getOwnPropertyDescriptor() 的操作
// Object.getOwnPropertyDescriptor() 方法,用于获取属性描述符信息
getOwnPropertyDescriptor(target, prop) {
/*
* target: 目标对象
* prop: 需要返回属性名称的描述符的,属性名
* getOwnPropertyDescriptor 方法必须返回一个 object 或 undefined。
* */
try {
result = Reflect.getOwnPropertyDescriptor(target, prop)
if (typeof result !== "undefined"){
console.debug(`getOwnPropertyDescriptor方法[获取属性描述符]: ${objName}.${prop.toString()} [获取属性描述符]: ${JSON.stringify(result)}`)
// result = BrokerProxy(result,`${objName}.${prop.toString()}`)
}else{
console.debug(`getOwnPropertyDescriptor方法[获取属性描述符]: ${objName}.${prop.toString()} [获取属性描述符]: ${JSON.stringify(result)} [提示]: ${prop.toString()}属性不存在`)
}
}catch (e) {
console.error(`getOwnPropertyDescriptor方法[获取属性描述符]: 对象: ${objName} [获取属性描述符]:${prop.toString()} [错误信息]: ${e.message}`)
}
return result
},
// 用于拦截对象的 Object.defineProperty() 操作
// Object.defineProperty() 方法,用于设置属性描述符信息
defineProperty(target, prop, descriptor) {
/*
* target: 目标对象
* prop: 需要修改属性描述符的,属性名称
* descriptor: 需要重新定义属性描述符对象
* */
try {
let result = Reflect.defineProperty(target, prop, descriptor);
console.debug(`defineProperty方法[设置属性描述符]: ${objName}.${prop.toString()} [设置的属性描述符]: ${JSON.stringify(descriptor)}`)
}catch (e) {
console.error(`defineProperty方法[获取属性描述符]: 对象: ${objName} [设置属性描述符]:${prop.toString()} [错误信息]: ${e.message}`)
}
return result
},
// 当代理的对象是一个函数时,返回的函数对象触发apply方法
// 或当调用对象中的属性值是一个函数时,触发apply方法
apply(target, thisArg, argArray) {
/*
* target: 目标对象
* thisArg: 被调用时的上下文对象
* argArray: 被调用时的参数数组,例如:[2,3]
* */
let result
try {
result = Reflect.apply(target, thisArg, argArray)
// console.debug(`apply方法[调用函数]: ${objName} [函数参数]: ${argArray} [返回结果]: ${JSON.stringify(result)}`)
if (result instanceof Object){
console.debug(`apply方法[调用函数]: ${objName} [返回结果]: ${JSON.stringify(result)}`)
}else if (typeof result === "symbol"){
console.debug(`apply方法[调用函数]: ${objName} [返回结果]: ${result.toString()}`)
}else{
console.debug(`apply方法[调用函数]: ${objName} [返回结果]: ${result}`)
}
}catch (e) {
console.error(`apply方法[调用函数]: ${objName} [函数参数]: ${argArray} [错误信息]: ${e.message}`)
}
return result
},
// construct 方法用于拦截 new 操作符
construct(target, argArray, newTarget) {
/*
* target: 目标对象
* argArray: constructor 的参数列表。
* newTarget: 代理后的对象
* */
let result
try {
result = Reflect.construct(target, argArray, newTarget)
let type = getType(result)
console.debug(`construct方法[new ${objName}]: ${objName} [类型]: ${type}`)
}catch (e) {
console.error(`construct方法[new ${objName}]: ${objName} [错误信息]: ${e.message}`)
}
return result
},
// 拦截对象自身属性的 delete 操作
// 当对对象某个属性进行删除: delete user.name 时触发
deleteProperty(target, prop) {
/*
* target: 目标对象
* prop: 要进行delete操作的属性名
* */
let result = Reflect.deleteProperty(target,prop)
console.debug(`deleteProperty方法[删除]: delete ${objName}.${prop} [是否可删除]: ${result}`)
return result
},
// 当对自身属性进行遍历时触发
ownKeys(target) {
/*
* target: 目标对象
* */
let result = Reflect.ownKeys(target)
console.debug(`ownKeys方法[正在属性遍历的对象]: ${objName}`)
return result
},
// 当获取代理对象的原型时,触发
// 对象.__proto__时触发
// 或 Object.getPrototypeOf 时触发
getPrototypeOf(target) {
/*
* target: 目标对象
* */
let result = Reflect.getPrototypeOf(target)
console.debug(`getPrototypeOf方法[获取原型对象] ${objName}.__proto__`)
return result
},
// 当Object.setPrototypeOf设置对象原型时,触发
setPrototypeOf(target, value) {
/*
* target: 目标对象
* value: 要设置的值
* */
let result = Reflect.setPrototypeOf(target,value)
console.debug(`setPrototypeOf方法 [设置原型对象] ${objName}.__proto__ = [设置内容]: ${value}`)
return result
}
}
)
}
let window = {
name:“雷点”,
info:{
name:“小红”,
age:10,
info2:{
name:“红”,
age:10,
}
},
add:function (a, b) {
return a + b
}
}
// 代理的使用: 需要在window对象加载后使用
window = BrokerProxy(window,‘window’)
console.log(“get方法=====”)
window.name
window.info.name
window.info.info2.name
console.log(“set方法=====”)
window.name = “javascript”
window.clas = {name:“小星星”}
window.clas
window.clas.name
console.log(“getOwnPropertyDescriptor方法=====”)
// 获取window对象的name属性操作符时,触发getOwnPropertyDescriptor拦截器
// 当获取的是一个不存在的属性时,得到的属性描述符结果是一个: undefined
// Object.getOwnPropertyDescriptor(window,“aaaaaaaaaaa”)
// 当获取的是存在的属性时,得到的属性描述符结果是对应的描述符信息: {“value”:“javascript”,“writable”:true,“enumerable”:true,“configurable”:true}
Object.getOwnPropertyDescriptor(window,“name”)
console.log(“defineProperty方法=====”)
// 当window给name属性设置属性描述符的时候触发 defineProperty拦截器
Object.defineProperty(window,“name”,{
writable:false,
configurable:true,
value:“设置属性值”
})
console.log(“apply方法=====”)
// 情况1: 当调用的是window对象中的函数时,触发apply方法
window.add(10,20)
// 情况2: 单独代理的是一个函数时,调用函数,触发apply方法
function add2(a,b) {
return a * b
}
add2 = BrokerProxy(add2,“add2”)
add2(100,200)
function add3(a,b) {
return a
}
add3 = BrokerProxy(add3,“add3”)
add3({“name”:“小红”},200)
add3(Symbol(),200)
最后
小编综合了阿里的面试题做了一份前端面试题PDF文档,里面有面试题的详细解析
虽只说了一个公司的面试,但我们可以知道大厂关注的东西并举一反三,通过一个知识点延伸到另一个知识点,这是我们要掌握的学习方法,小伙伴们在这篇有学到的请评论点赞转发告诉小编哦,谢谢大家的支持!