JavaScript中拦截对象属性赋值操作的方式有哪些
我在研究jsdom依赖的cssstyle包中CSSStyleDeclaration实现的属性赋值拦截逻辑,这类拦截一般用于属性值合法性校验。定位到代码中执行属性赋值的语句为:
this[lowercaseName] = value;
此前我只了解Proxy对象可以实现属性赋值动作的拦截,但经确认该场景下的CSSStyleDeclaration实例并不是Proxy对象,因此想确认:除Proxy外,还有哪些可以实现中间人式拦截对象属性赋值操作的方法?
可以通过以下代码验证该实现确实存在属性赋值拦截逻辑:
const {CSSStyleDeclaration} = require('cssstyle') const decl = new CSSStyleDeclaration() decl.setProperty('background-color', 'invalid-value') decl.getPropertyValue('background-color') // 传入非法值时返回空字符串'' decl.setProperty('background-color', '#333') decl.getPropertyValue('background-color') // 传入合法值时返回'#333'
除Proxy外,常见的属性赋值拦截实现有以下几类,cssstyle包实际使用的是第一类方案:
访问器属性(getter/setter)拦截
这是该库采用的核心实现:在CSSStyleDeclaration的原型对象上,已经为所有支持的CSS属性预先定义了对应的setter访问器。当执行this[lowercaseName] = value这类赋值操作时,并不会给实例挂载普通数据属性,而是会自动触发原型链上对应属性的setter函数。setter内部内置了完整的属性值合法性校验逻辑:如果传入值不符合CSS属性的语法规则,就直接丢弃值不做存储;如果校验通过,才会把合法值写入内部的私有存储结构。
你看到的赋值语句本身就是触发拦截逻辑的入口,不存在“赋值完成后再做校验”的流程。通过属性描述符API定义拦截逻辑
本质和访问器属性是同一套底层能力,通过Object.defineProperty或Object.defineProperties方法,手动给目标对象或其原型配置带set函数的属性描述符,所有对该属性的写入操作都会被自定义的set函数捕获,开发者可以在set函数中自由实现校验、值转换、日志上报等逻辑。类层面的setter语法拦截
在ES6类定义时,直接通过set关键字为类定义实例属性的写入拦截逻辑,这类语法本质是在类的原型上挂载访问器属性,效果和手动调用Object.defineProperty一致。方法层面的前置校验拦截
对会触发属性赋值的公开方法(比如示例中的setProperty方法)做逻辑重写,在方法内部先完成入参校验,再决定是否执行后续的属性赋值操作。这种方式只能拦截通过指定方法触发的赋值,无法拦截直接对属性做赋值的操作,拦截覆盖范围有限。
补充说明:示例中传入非法背景色值后读取到空字符串,本质就是setter校验不通过时没有把值存入内部存储,读取时找不到对应有效值就返回空串,和原生浏览器中CSSStyleDeclaration的行为完全一致。
内容的提问来源于stack exchange,提问作者Mooze

