当前位置: 首页 > news >正文

JS元编程

 如果说常规编程是写代码去操作数据,那么元编程就是写代码去操作其他代码。

1 属性的特性

JS的属性有名字和值,但每个属性也有3个关联的特性:

可写(writable)特性指定是否可修改属性的值。

可枚举(enumerable)特性指定是否可以通过for/in循环和Object.keys()方法枚举属性。

可配置(configurable)特性指定是否可以删除属性,以及是否可以修改属性的特性。

这些特性允许库作者给原型对象添加方法,并让它们像内置方法一样不可枚举;允许作者“锁住”自己的对象,定义不能修改或删除的属性。

1.1 访问器属性和数值属性

访问器属性(get或set方法)没有value及writable特性,有4个特性:get、set、enumerable和configurable。

数值属性的4个特性是:value、writable、enumerable和configurable。

Object有以下方法来操作这些特性:

1)  Object.getOwnPropertyDescriptor() 获取特定对象某个属性的属性描述符。

2) Object.defineProperty() 创建属性并可定义属性描述符。Object.defineProperties() 则可同时定义多个属性及其描述符。

let obj = {
    get name() { return 'obj' },
    x: 1
}

Object.defineProperty(obj,"y",{
    value: 1,
    writable: false,
    enumerable: true,
    configurable: false
})

Object.defineProperties(obj,{
    z: {value: 1, writable: true},
    age: { get: () => 18  }
})

console.log(obj);
console.log(Object.getOwnPropertyDescriptor(obj,"name"));
console.log(Object.getOwnPropertyDescriptor(obj,"z"));
console.log(Object.getOwnPropertyDescriptor(obj,"age"));

// { name: [Getter], x: 1, y: 1 }
// {
//     get: [Function: get name],
//     set: undefined,
//         enumerable: true,
//     configurable: true
// }
// { value: 1, writable: true, enumerable: false, configurable: false }
// {
//     get: [Function: get],
//     set: undefined,
//         enumerable: false,
//     configurable: false
// }

1.2 自定义深度复制函数

Object.assign()只复制可枚举属性和属性值,但不复制属性特性。这意味着如果源对象有个访问属性,那么复制到目标对象的是获取函数的返回值。

let obj = {}

Object.defineProperties(obj,{
    x: { value: 1, enumerable: true ,writable: false},
    y: { value: 2, enumerable: false, writable: false},
    name: { get: () => "obj",enumerable: true }
})

let target = {}
Object.assign(target, obj);

console.log(target);
console.log(Object.getOwnPropertyDescriptor(target,"x"));
console.log(Object.getOwnPropertyDescriptor(target, "name"))

// { x: 1, name: 'obj' }
// { value: 1, writable: true, enumerable: true, configurable: true }
// { value: 'obj', writable: true, enumerable: true, configurable: true }

我们自定义个深度复制函数。从一个对象向另一个对象复制属性及它们的特性。

Object.defineProperty(Object, "assignDeep", {
    writable: false,
    enumerable: false,
    configurable: false,
    value: function (target, ...sourceList) {
        for (let source of sourceList) {
            for (let name of Object.getOwnPropertyNames(source)) {
                let desc = Object.getOwnPropertyDescriptor(source,name);
                Object.defineProperty(target,name,desc);
            }
            for (let symbol of Object.getOwnPropertySymbols(source)) {
                let desc = Object.getOwnPropertyDescriptor(source, name);
                Object.defineProperty(target,name,desc);
            }
        }
    }
})

let target = {};
let source = { x: 1};
Object.defineProperties(source, {
    y: { value: 2, enumerable: false},
    name: {get: () => "source"},
    z: { value: 3, enumerable: true}
});
Object.assignDeep(target,source);

console.log(target);
console.log(Object.getOwnPropertyDescriptor(target,"y"));
console.log(Object.getOwnPropertyDescriptor(target,"name"));

// { x: 1, z: 3 }
// { value: 2, writable: false, enumerable: false, configurable: false }
// {
//     get: [Function: get],
//     set: undefined,
//         enumerable: false,
//     configurable: false
// }

1.3 对象的可扩展能力

对象的可扩展特性控制是否可以给对象添加新属性,即是否可扩展。

Object.isExtensible() 判断一个对象是否可扩展,

Object.preventExtensions() 让对象不可扩展,只会影响对象本身,而不会影响其原型。

把对象改为不可扩展是不可逆的。

2 公认符号

是Symbol()工厂函数的一组属性,也就是一组符号值。通过这些符号值,我们可以控制js对象和类的某些底层行为。

1)hasInstance,如果instanceof 的右侧是一个有[Symbol.hasInstance]方法的对象,那么就会以左侧的值作为参数来调用这个方法并返回这个方法的值。

let bigNum = {
    [Symbol.hasInstance](v) {
        return v > 100
    }
};

console.log(12 instanceof  bigNum); // false
console.log(124 instanceof bigNum); // true

2)toStringTag, 在调用对象的toString方法时,会查找自己的参数中是否有一个属性的符号名是Symbol.toStringTag,如果有,则使用这个属性的值作为蔬菜。

class People {
    get [Symbol.toStringTag]() {
        return "People 人";
    }
}

class Student {

}

console.log(Object.prototype.toString.call(new Student())); // [object Object]
console.log(new Student()); // {}
console.log(Object.prototype.toString.call(new People())); // [object People 人]
console.log(new People()); // People [People 人] {}

3) 模式匹配符号,match()、matchAll()、search()、replace()和split()这些字符串方法中的任意一个,都有一个与之相对应的公认符号。

class RegExtCustom {

    constructor(glob) {
        this.glob = glob;
    }

    [Symbol.search](s) {
        return s.search(this.glob);
    }

}

let reg = new RegExtCustom("[12]2");
console.log("1234".search(reg)); // 0
console.log("ad32".search(reg)); // -1

3 模版标签

位于反引号直接的字符串被称为“模版字面量”。如果一个函数的表达式后面跟着一个模版字面量,那就会转换为一个函数调用。

这个函数第一个参数是一个字符串数组,然后是0或多个额外任何类型的参数。如果模版字面量包含一个要插入的值,那么字符串数组就会收到2个参数,表示这个被插入值两边的字面量。(如果要插入2个值,则字符串数组会收到3个参数)。

function templateFun(strArr,...args) {
    console.log("字符串数组参数:",strArr);
    console.log("插入参数", args);
}

let num = 999;
let str = 'hello word';
templateFun`&${num}${str}&`;

// 字符串数组参数: [ '&', '', '&' ]
// 插入参数 [ 999, 'hello word' ]

4 反射与代理对象

ES6及之后版本中的Proxy类是JS中最强大的元编程特性。使用它可以修改JS对象的基础行为。

4.1 反射API

Reflect不是类而是对象,它的属性只是定义了一组相关函数。

1)apply(f,o,arg),将函数f作为o的方法进行调用(如果o是null,则调用函数f时没有this值),并传入args数组的值作为参数。

function fun(...args) {
    console.log(this.toString(),args);
}

let obj = {
    get [Symbol.toStringTag]() {
        return "自定义obj";
    }
}

Reflect.apply(fun,obj,["hello","js"]); // [object 自定义obj] [ 'hello', 'js' ]

2) getPrototypeof(o), 返回对象o的原型,如果没原型则返回null。

3) set(o,name,value,receiver), 根据指定的name将对象o的属性值指定为value,如果指定了receiver参数,则将设置方法作为receiver和非o对象的设置方法调用。

class Student {
    set name(val) {
        console.log("设置名字:",val)
    }
}

let student = new Student();
student.name = "hello student"; // 设置名字: hello student
Reflect.set(student,"name", "你好啊",(val) => {
    console.log("反射设置值",val);
}); // 设置名字: 你好啊

4.2 代理对象

let proxy = new Proxy(target,handlers); // target 目标对象 handlers处理器对象。 proxy代理对象。

代理对象没有自己的状态或行为,每次对它执行某个操作,它只会把相应的操作发送给处理器对象或目标对象。(如果处理器对象上存在对应方法,代理就调用该方法,否则执行目标对象对应的方法)。

function creteCustomProxy(target) {
    let handlers = {
        get(target,property, receiver) {
            console.log(`Handler GET target:${target} property: ${property}`);
            return Reflect.get(target,property,receiver);
        },
        set(target,prop, value, receiver) {
            console.log(`Handler SET target ${target} prop ${prop} value: ${value}`);
            if (prop === 'java') throw new Error("不是JAVA");
            Reflect.set(target,prop,value,receiver);
        }
    };
    return new Proxy(target,handlers);
}

let obj = {};
let proxy = creteCustomProxy(obj);
proxy.x = "hello";
console.log(proxy.x);
proxy.java = "haha";

// Handler SET target [object Object] prop x value: hello
// Handler GET target:[object Object] property: x
// hello
// Handler SET target [object Object] prop java value: haha
// /Users/huangzaizai/Desktop/个人/代码/js-study/day4/article/s10.js:9
// if (prop === 'java') throw new Error("不是JAVA");
// ^
//
// Error: 不是JAVA

4.2.1 防御性编程

Proxy.revocable() 函数返回一个对象,包含代理对象和一个revoke()函数,一旦调用revoke(),代理立即失效。

let obj = {}
let handle = {}

let { proxy, revoke } = Proxy.revocable(obj,handle);
proxy.x = 2;
console.log(obj); // { x: 2 }
revoke();
proxy.y = 2; // 报错 TypeError: Cannot perform 'set' on a proxy that has been revoked

这个函数的用处是:如果必须向一个不受自己控制的库传一个函数,则可以给它传一个可撤销代理,在使用完这个库之后撤销代理,这样可以防止第三方库持有对你函数的引用。

相关文章:

JS元编程

如果说常规编程是写代码去操作数据,那么元编程就是写代码去操作其他代码。 1 属性的特性 JS的属性有名字和值,但每个属性也有3个关联的特性: 可写(writable)特性指定是否可修改属性的值。 可枚举(enume…...

通过Gunicorn、Supervisor和Nginx更好地运行Django

文章目录 通过runserver运行Django通过Gunicorn运行Django通过Nginx来做反向代理通过Supervisor来托管gunicorn和nginx 同步发布在个人站点:https://panzhixiang.cn 通过runserver运行Django 相信用过Django做开发的人对于python manage.py runserver 这个命令一定…...

[SQL] union all

UNION ALL 是一个用于合并多个查询结果集的操作符。它将多个 SELECT 查询的结果合并成一个结果集,并且保留所有的行,包括重复的行。 具体语法如下: SELECT column1, column2, ... FROM table1 UNION ALL SELECT column1, column2, ... FROM…...

Filebeat+Kafka+ELK日志分析架构

目录 一、zookeeper: 1. zookeeper 定义: 2. Zookeeper 工作机制: 3. Zookeeper 特点: 4. Zookeeper 数据结构: 5. Zookeeper 应用场景: 5.1 统一命名服务: 5.2 统一配置管理: 5.3 统一集群管理: 5.4 服务器动态上下线: 5.5 软负载均衡: 6. Zookeeper 选…...

RK3568驱动指南|第六篇-平台总线-第55章 初识设备树

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工…...

【ELK 使用指南 1】ELK + Filebeat 分布式日志管理平台部署

ELK和EFLK 一、前言1.1 日志分析的作用1.2 需要收集的日志1.3 完整日志系统的基本特征 二、ELK概述2.1 ELK简介2.2 为什么要用ELK?2.3 ELK的组件 三、ELK组件详解3.1 Logstash3.1.1 简介3.1.2 Logstash命令常用选项3.1.3 Logstash 的输入和输出流3.1.4 Logstash配置文件 3.2 E…...

Springboot高频应用注解

本文旨在记录开发中遇到的SpringBoot高频注解,并针对其具体应用记录。 一、LOMBOK相关注解 Slf4j 目的在于使用Log的日志功能,可以在JAVA中自动生成日志记录器!使用时在类上添加Slf4j注解后即可以在类中调用log方法如 可以 调用 log.info …...

面试总结分享:25道数据库测试题

1)什么是数据库测试? 数据库测试也称为后端测试。数据库测试分为四个不同的类别。数据完整性测试 数据有效性测试 数据库相关的性能 测试功能,程序和触发器 2)在数据库测试中,我们需要正常检查什么? 通常&a…...

和硕首次参加展OCP 峰会,将发布多项AI合作项目产品 | 百能云芯

电子代工大厂和硕联合科技宣布,将参与今年的 OCP 全球峰会 (OCP Global Summit),展示与英伟达 (NVIDIA) 合作成果,包含使用英伟达 GH200 Grace Hopper 超级芯片的 MGX AI 服务器,以及搭载 A100、L40 等服务器产品。 OCP 峰会于 10…...

FPGA基于1G/2.5G Ethernet PCS/PMA or SGMII实现 UDP 网络视频传输,提供工程和QT上位机源码加技术支持

目录 1、前言版本更新说明免责声明 2、我这里已有的以太网方案3、设计思路框架视频源选择OV5640摄像头配置及采集动态彩条UDP协议栈UDP视频数据组包UDP协议栈数据发送UDP协议栈数据缓冲IP地址、端口号的修改Tri Mode Ethernet MAC1G/2.5G Ethernet PCS/PMA or SGMIIQT上位机和源…...

小程序setData动态传递key

有些时候可能需要根据key是个变量 比如 let keyName "name" this.setData({keyName :"张三" })本来想将keyName替换为name的,但是小程序只会在data中定义一个key为keyName ,value为“张三”的一条数据。 正确写法为: let keyNam…...

boost Geometry

boost::Geometry boost作为C中最常用的第三方库,Geometry库里面拥有大量的开源算法。 函数作用get获取几何图形(通常为点)的坐标值get (with index)获取框或段的坐标值set设置几何图形(通常为点)的坐标值set (with i…...

凉鞋的 Unity 笔记 201. 第三轮循环:引入变量

201. 第三轮循环:引入变量 在这一篇,我们进行第三轮 编辑-测试 循环。 在之前我们编写了 输出 Hello Unity 的脚本,如下: using System.Collections; using System.Collections.Generic; using UnityEngine;public class FirstGameObject …...

小魔推短视频裂变工具,如何帮助实体行业降本增效?

在如今的互联网时代,大多数的实体老板都在寻找不同的宣传方法来吸引客户,现在短视频平台已经成为重中之重的获客渠道之一,而如何在这个日活用户超7亿的平台获取客户,让更多人知道自己的门店、自己的品牌,泽成为了不少老…...

VBA技术资料MF71:查找所有空格并替换为固定字符

我给VBA的定义:VBA是个人小型自动化处理的有效工具。利用好了,可以大大提高自己的工作效率,而且可以提高数据的准确度。我的教程一共九套,分为初级、中级、高级三大部分。是对VBA的系统讲解,从简单的入门,到…...

c++小知识

内联函数 inline 用来替换宏函数 不能分文件编辑 在c语言中#define NULL 0在c中使用nullptr表示空指针class内存的大小计算规则使用的是内存对齐 没有成员,但是还有1个字节,我们使用这个来标记他是个类 类成员函数不存在于类中 为什么每个对象使用的…...

C#上位机序列9: 批量读写+事件广播

1. 读取配置文件及创建变量信息(点位名称,地址,数据类型(bool/short/int/float/long/double)) 2. 读任务&写任务,数据有变化时事件广播通知 using HslCommunication; using HslCommunication.Core; usi…...

ARM +FPGA GPIB IP核实现

目前在数据发生其技术上居领先的是美国的 Tektronix 公司和 Agilent 公司。 Agilent 公司的台式脉冲 / 数据发生器家族的最高时钟频率达 3GHz (定 时发生器),数据发生器 E81200 在通道数为 8CH 时数据速率为 660Mb/s, 即可以产…...

有消息称苹果Vision Pro会有廉价版

据外媒爆料,苹果公司苹果正在研发的头显产品Vision Pro,将会有廉价版。据透露,这款产品预计售价在1500美元至2500美元之间,虽然仍不算低,但较现有的Vision Pro 3499美元的起售价,还是有明显降低。 透露廉价…...

jenkins整合gerrit

背景 公司项目之前使用jenkins整合了gitlab,后面代码迁移到gerrit,所以需要修改jenkins配置。下面就简单的介绍一下jenkins如何整合gerrit。 环境 服务器:linux 环境:docker、jenkins 代码仓库:gerrit 前提 docke…...

Map相关知识

数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

大数据学习(132)-HIve数据分析

​​​​🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言&#x1f4…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

面试高频问题

文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...

flow_controllers

关键点: 流控制器类型: 同步(Sync):发布操作会阻塞,直到数据被确认发送。异步(Async):发布操作非阻塞,数据发送由后台线程处理。纯同步(PureSync…...

PH热榜 | 2025-06-08

1. Thiings 标语:一套超过1900个免费AI生成的3D图标集合 介绍:Thiings是一个不断扩展的免费AI生成3D图标库,目前已有超过1900个图标。你可以按照主题浏览,生成自己的图标,或者下载整个图标集。所有图标都可以在个人或…...

Appium下载安装配置保姆教程(图文详解)

目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...

Linux入门(十五)安装java安装tomcat安装dotnet安装mysql

安装java yum install java-17-openjdk-devel查找安装地址 update-alternatives --config java设置环境变量 vi /etc/profile #在文档后面追加 JAVA_HOME"通过查找安装地址命令显示的路径" #注意一定要加$PATH不然路径就只剩下新加的路径了,系统很多命…...

SpringCloud优势

目录 完善的微服务支持 高可用性和容错性 灵活的配置管理 强大的服务网关 分布式追踪能力 丰富的社区生态 易于与其他技术栈集成 完善的微服务支持 Spring Cloud 提供了一整套工具和组件来支持微服务架构的开发,包括服务注册与发现、负载均衡、断路器、配置管理等功能…...