代理与Reflect反射

news2024/11/15 8:06:38

属性描述符

Proprety Descriptor 属性描述符 用于描述一个属性的相关信息

1.Object.getOwnPropertyDescriptor(对象,属性名)

可以得到一个对象的 某个属性的属性描述符 

  Object.getOwnPropertyDescriptors(对象)

 可以得到某个对象的所有属性描述符

如果需要为某个对象添加属性或修改属性时,配置其属性描述符,可以使用

Object.definePropertie(对象、属性、描述符 )

Object.definePropertie(obj, 'a', { configurable: false })

 存取器属性

  • 属性描述符中,如果配置了get  set中任何一个,则该属性不再是一个普通属性,而变成存储器属性
  • get、set配置均为函数,如果一个属性是存储器属性,则读取该属性时,会运行get方法,将get方法得到的返回值作为属性值
  • 如果给该属性赋值,则会运行set方法
  • 存取器属性最大的意义,在于可以控制属性的读取和赋值
const obj = {
    b: 1
}
Object.defineProperty(obj, 'a', {
    get () {
        console.log("运行了属性的get函数")
    }, 
    set (val) {
        console.log("运行了属性的set函数", val)
    }
})
obj.a = obj.a + 1 
//set(obj.a+1) ==>set(get()+1)  
//1.需要先读取a值,但get()没有返回值 因此是undefined 
//2.undefined+1=NaN 
console.log(obj.a)

 

 1.使用其他属性值赋值。也可新增属性  _a

const obj = {
    b: 1
}
Object.defineProperty(obj, 'a', {
    get () {
        console.log("运行了属性的get函数")
        return obj._a //使用其他属性值
    }, set (val) {

        console.log("运行了属性的set函数", val)
        obj._a = val
    }
})
obj.a = 10 
console.log(obj.a)

 2.也可对赋值做自定义规范

Object.defineProperty(obj, 'a', {
    get () {
        console.log("运行了属性的get函数")
        return obj._a
    }, set (val) {
        if (typeof val !== 'number') {
            throw new TypeError('必须是一个数字')
        }
        if (val > 200) {
            val = 200
        }
        console.log("运行了属性的set函数", val)
        obj._a = val
    }
})
obj.a = 10000

Reflect 

1.reflect是什么?

reflect是一个内置的JS对象,它提供了一系列方法,可以让开发者通过调用这些方法,访问一些JS底层功能

由于类似于其他语、言的【 反射】,因此取名为Reflect

2.可以做什么?

可以实现 属性的赋值与取值、调用普通函数、调用构造函数、判断属性是否存在于对象中.etc

3.为什么还需要Reflect实现一次已有功能?

ES5提出重要理念  “减少魔法、让代码更加纯粹”,其很大程度受函数式编程影响

ES6进一步贯彻了该理念,它认为,对属性内存的控制、原型链的修改、函数的调用等,都属于底层实现,属于一种魔法,因此需要将他们提取出来形成一个正常的API ,高聚合到某个对象中,于是,造就了Reflect对象

4.提供了哪些API?

代理:Proxy

代理:提供了修改底层实现的方式

new Proxy(target,handler)

  • 代理一个目标
  • target:目标对象
  • handler:是一个普通对象,其中可以重写底层实现
  • 返回一个代理对象
const obj = {
    a: 1,
    b: 2
}
const proxy = new Proxy(obj, {
    set (target, propertyKey, value) {
        Reflect.set(target, propertyKey, value)
    },
    get (target, propertyKey) {
        if (Reflect.has(target, propertyKey)) {
            return Reflect.get(target, propertyKey)
        } else {
            return -1
        }
    },
    has (target, propertyKey) {
        return false
    }
})
proxy.a = 10
console.log(proxy.a)
console.log(proxy.d)

proxy应用——观察者模式

有一个对象,是观察者,他用于观察另外一个对象的属性值变化,当属性值变化后会收到一个通知,可能会做一些事 

以下方式不太好

function observer (target) {
    const ob = {}
    const div = document.getElementById('container')
    const props = Object.keys(target)
    for (const prop of props) {
        Object.defineProperty(ob, prop, {
            // 读属性值时,把目标值给你
            get () {
                return target[prop]
            },
            // 赋值时,给目标对象赋值,再渲染一次
            set (val) {
                target[prop] = val
                render()
            },
            enumerable: true
        })
    }
    // console.log(Object.getOwnPropertyDescriptors(ob))
    render()
    function render () {
        let html = ''
        for (const prop of Object.keys(ob)) {
            html += `<p><span>${prop}:</span><span>${ob[prop]}<span/></p>`
        }
        div.innerHTML = html
    }
    return ob
}
const target = {
    a: 1, b: 2
}
let obj = observer(target)
obj.a = 3
obj.b = 4

 一开始打印一下 ob,其中是不可枚举,不可被遍历的,所以要在属性配置定义一下该属性

一开始,页面呈现 a 、b值 

重新赋值后,页面也一起变化

两个对象内容相等,也造成内存的浪费 

使用代理实现,不会再浪费一块内存

function observer (target) {
    const div = document.getElementById('container')
    const proxy = new Proxy(
        target, {
        set (target, prop, value) {
            Reflect.set(target, prop, value)
        },
        get () {
            return Reflect.get(target, prop, value)
        }
    }
    )
    render()
    function render () {
        let html = ''
        for (const prop of Object.keys(target)) {
            html += `<p><span>${prop}:</span><span>${target[prop]}<span/></p>`
        }
        div.innerHTML = html

    }
    return proxy
}
const target = {
    a: 1, b: 2
}
let obj = observer(target)

 proxy应用——构造函数

1.原本构造函数

class User {
    constructor(firstName,
        lastName,
        age) {
        this.firstName = firstName
        this.lastName = lastName
        this.age = age
    }
}

2.使用代理方式偷懒 可以这么写

class User { }
function ConstructorProxy (Class, ...propNames) {
    // 重写类的底层实现
    return new Proxy(Class, {
        construct (target, argumentsList) {
            const obj = Reflect.construct(target, argumentsList)
            // 给构造函数对象加上这些属性
            propNames.forEach((name, i) => {
                obj[name] = argumentsList[i]
            })
            console.log('构造函数被调用了')
            return obj
        },
    })
}
const UserProxy = ConstructorProxy(
    User,
    'firstName',
    'lastName',
    'age'
)
// 通过代理告诉 构造函数有三个属性
const obj = new UserProxy('张', '三', 17)
console.log('obj1:', obj)

3.再加一个类也一样

class Monster { }
const monstorProxy = ConstructorProxy(
    Monster,
    'attack',
    'defence',
    'hp',
    'rate',
    'name'
)
const obj2 = new monstorProxy(10, 100, 3, 4, 'monster')
console.log('obj2:', obj2)

proxy应用——可验证的函数参数 

function sum (a, b) {
    return a + b
}
function validatorFunction (func, ...types) {
    const proxy = new Proxy(func, {
        apply (target, thisArgument, argumentList) {
            types.forEach((t, i) => {
                console.log(t, i)
                const arg = argumentList[i]
                console.log(arg)
                if (typeof arg !== t) {
                    throw new TypeError(`第${i + 1}个参数${argumentList[i]}不满足参数类型`)
                }
            })
            Reflect.apply(target, thisArgument, argumentList)
        }
    })
    return proxy
}
const sumProxy = validatorFunction(sum, "number", "number")
console.log(sumProxy(1, "2"))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1436873.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库学习笔记2024/2/4

随笔 怎么学? 1、MySQL数据库就是存储和管理数据的一个大型软件,这个软件有一个专门的语言叫SQL,主要学的是SQL语言,但想要达到企业用人标准,就还得学会熟练使用MySQL这个软件。 2、学习分三阶段: 一. 基础篇 1. MySQL概述 1.1 数据库相关概念 数据库管理系统 -> MyS…

如何修改远程端服务器密钥

前言 一段时间没改密码后&#xff0c;远程就会自动提示CtrlAltEnd键修改密码。但我电脑是笔记本&#xff0c;没有end键。打开屏幕键盘按这三个键也没用。 解决方法 打开远程 1、远程端WINC 输入osk 可以发现打开了屏幕键盘 2、电脑键盘同时按住CtrlAlt&#xff08;若自身电…

【论文+代码】ZS-N2N实现小样本零网络图像去噪

01、引言 本文方法源于Youssef Mansour和Reinhard Heckel撰写的论文《Zero-Shot Noise2Noise: Efficient Image Denoising without any Data》&#xff0c;该文作者探索了一种不需要任何数据且高效的高效图像去噪方法。 该方法使用两个固定的内核对噪声图像进行卷积&#xff…

【开源】基于JAVA+Vue+SpringBoot的快乐贩卖馆管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 搞笑视频模块2.3 视频收藏模块2.4 视频评分模块2.5 视频交易模块2.6 视频好友模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 搞笑视频表3.2.2 视频收藏表3.2.3 视频评分表3.2.4 视频交易表 四、系…

c语言--指针运算

目录 一、指针-整数二、指针-指针2.1条件2.2两个指针指向同一块空间代码2.2.1运行结果 2.3两个指针指向不同块空间代码2.3.1运行结果 2.4总结 三、指针的关系运算3.1代码3.1.1运行结果3.1.2分析 一、指针整数 用数组举例&#xff1a; 因为数组在内存中是连续存放的&#xff0c…

C++:第十五讲高精度算法

每日C知识 system("color xx);是改变字体及背景颜色&#xff0c;前一个x代表一个数字&#xff0c;可以改变背景颜色&#xff0c;后一个x代表一个数字&#xff0c;可以改变字体颜色 &#xff0c;但都是根据颜色表来的。 记住&#xff1a;要加头文件&#xff1a;#include&l…

详细关于如何解决mfc140.dll丢失的步骤,有效修复mfc140.dll文件丢失的问题。

mfc140.dll文件是Microsoft Visual Studio 2015程序集之一&#xff0c;它包含用于支持多种功能的代码和库。当这个mfc140.dll文件丢失时&#xff0c;可能会导致相关程序运行出错甚至无法运行。很多用户可能会遇到mfc140.dll丢失的问题&#xff0c;但是这并不是不可解决的困难。…

【自然语言处理-工具篇】spaCy<1>--介绍及安装指南

目录 前言 安装指南 pip conda spaCy升级 总结 前言 spaCy是一个开源的自然语言处理库,用于处理和分析文本数据。它提供了许多功能,包括分词、词性标注

cs50x 2024 -Lecture 0

cs50x 2024 -Lecture 0 01:43 哈佛大学CS50课程介绍 01:43CS50是哈佛大学的计算机科学和编程入门课程。 05:17计算机科学是一种通用的问题解决方式&#xff0c;适用于各个领域。 06:32课程将教授C、Python、SQL和JavaScript等编程语言。 08:18 计算机科学的重要性和二进制表…

【CSS】css如何实现字体大小小于12px?

【CSS】css如何实现字体大小小于12px? 问题解决方案transform: scale(0.5)&#xff08;常用&#xff09;SVG 矢量图设置text 问题 文字需要显示为12px&#xff0c;但是小于12px的&#xff0c;浏览器是显示不来的 解决方案 transform: scale(0.5)&#xff08;常用&#xff0…

Bert下载和使用(以bert-base-uncased为例)

Bert官方github地址&#xff1a;https://github.com/google-research/bert?tabreadme-ov-file 【hugging face无法加载预训练模型】OSError&#xff1a;Can‘t load config for ‘./bert-base-uncased‘. If you‘re trying 如何下载和在本地使用Bert预训练模型 以bert-base-u…

should be also和should also be

will also be 是正确的 但老师和新概念的两个说法都没有错. will also be 是固定搭配.就好像will not be一样, 限定词加在be前.老师说的是陈述之类的句型 Nbe动词alson/adj/动词短语.例&#xff1a;He is also good at physic. should be also还是should also be also应该插在…

期权定价模型系列【14】期权复制—Delta动态复制误差计算

动态复制 直接使用期权进行对冲存在的问题之一是成本较高。假设我们多头一个看涨期权进行对冲,除本 身的交易费用之外,更多的隐性成本来自于期权的时间价值——期权价值等于内在价值与时间价值之 和,但在时间逐渐临近到期日的过程中,时间价值不断损耗,直至到期日为 0,如…

C++泛编程(4)

类模板高级&#xff08;1&#xff09; 1.类模板具体化部分具体化完全具体化 2.类模板与继承2.1模板类继承普通类2.2普通类继承模板类的实例化版本2.3普通类继承类模板2.4模板类继承模板类2.5模板类继承模板参数给出的类 1.类模板具体化 有了函数模板具体化的基础&#xff0c;学…

STL算法(中)

常用排序算法 sort 功能描述&#xff1a; 对容器内元素进行排序 函数原型&#xff1a; sort(iterator beg, iterator end, _Pred) ; // 按值查找元素&#xff0c;找到返回指定位置迭代器&#xff0c;找不到返回结束迭代器位置 // beg 开始迭代器 // end 结束迭代器 …

1-3 mininet中使用python API直接拓扑定义以及启动方式对比

作为SDN网络中搭建拓扑非常重要的仿真平台&#xff0c;我们可以使用mininet默认的库内拓扑文件&#xff0c;也可以使用python语言进行自定义拓扑。使用python进行拓扑定义时&#xff0c;不同的定义方式将导致其启动的方式由所不同。 一、采用最原始的命令启动方式&#xff1a; …

Redis——事件

Redis服务器是一个事件驱动程序&#xff0c;服务器需要处理以下两种事件&#xff1a; 文件事件(file event)&#xff1a;Redis服务器通过套接字与客户端(或者其他Redis服务器)进行连接&#xff0c;而文件事件就是服务器对套接字操作的抽象(linux下一切皆文件&#xff0c;返回的…

TP框架 之think-auth权限认证

一、安装think-auth composer require 5ini99/think-auth二、数据表 -- ---------------------------- -- think_auth_rule&#xff0c;规则表&#xff0c; -- id:主键&#xff0c;name&#xff1a;规则唯一标识, title&#xff1a;规则中文名称 status 状态&#xff1a;为1正常…

【日志记录】——单片机可执行文件合并

一&#xff1a;需求场景 现在有一片单片机&#xff0c;执行程序包括自定义boot和应用程序app, 在将打包好的固件给到生产时有以下问题&#xff0c;由于要通过jlink烧录boot&#xff0c;然后上电启动boot&#xff0c;通过boot烧录初始化程序&#xff0c;过程过于复杂&#xff0…

基于ESP-WROOM-32的双串口通信并显示到OLED显示屏上

目录 开发板引脚图 Arduino环境配置1.ESP32开发版下载2.Arduino开发板选择 -> ESP32 Dev Module3.安装驱动库 接线图Arduino代码现象演示 开发板 ESP-WROOM-32 引脚图 Arduino环境配置 1.ESP32开发版下载 选择 esp32 by Espressif Systems 2.Arduino开发板选择 -> E…