当前位置: 首页 > 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…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

PHP和Node.js哪个更爽?

先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

【单片机期末】单片机系统设计

主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...

爬虫基础学习day2

# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

高考志愿填报管理系统---开发介绍

高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发&#xff0c;采用现代化的Web技术&#xff0c;为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## &#x1f4cb; 系统概述 ### &#x1f3af; 系统定…...