如何让类方法返回对象的函数正确绑定自身this以修改属性?
问题描述
我有一个包含两个方法的类:
- 对象工厂方法
objFactory:返回包含三个属性(value1、value2、value3)和一个绑定函数的对象,示例如myObj = {value1: 1, value2: 2, value3: 3, func()} - 类方法
changeValueProperties:根据输入值为返回对象生成value1-3的值
要求用户不得直接修改返回对象的value1-3属性,必须通过调用内置方法更新,changeValueProperties对用户是黑盒,无需知晓计算逻辑。
核心问题是this指向:当前this指向MyTestClass实例,而我需要它指向返回的对象以修改其内部属性。我知道可以用call/apply/bind方法,但不清楚该场景下的具体操作,尤其是updateValues还要调用类自身的方法进行计算。
也接受架构层面的替代建议,我清楚当前实现可能不够规范,希望有更优方案。
附当前代码:
class MyTestClass { dummyData; constructor() { this.dummyData = 100; } changeValueProperties(value) { return { value1: value*1, value2: value*2, value3: value*3 } } objFactory(value, ...otherParameters) { let computedValues = this.changeValueProperties(value); let updateValues = (newValue) => { let computedValues = this.changeValueProperties(newValue); // 此处需要更新返回给用户的myReturnedObj里的value1-3 // this指向MyTestClass而非myReturnObj,因此下面的代码无效 this.value1 = computedValues.value1; this.value2 = computedValues.value2; this.value3 = computedValues.value3; } let myReturnObj = { value1: computedValues.value1, value2: computedValues.value2, value3: computedValues.value3, updateValues: updateValues } return myReturnObj } } const myClass = new MyTestClass(); let myObj = myClass.objFactory(10,...otherParameters for object generation); // 期望得到 {value1: 10, value2: 20, value3: 30, ...} myObj.updateValues(20); // 期望更新后得到 {value1: 20, value2: 40, value3: 60, ...}
解决方案
方案一:利用闭包直接修改返回对象
updateValues定义在objFactory内部,可通过闭包直接访问并修改返回对象的属性,无需处理this绑定问题:
class MyTestClass { dummyData; constructor() { this.dummyData = 100; } changeValueProperties(value) { return { value1: value*1, value2: value*2, value3: value*3 } } objFactory(value, ...otherParameters) { let computedValues = this.changeValueProperties(value); let myReturnObj = { value1: computedValues.value1, value2: computedValues.value2, value3: computedValues.value3, updateValues: (newValue) => { let newComputed = this.changeValueProperties(newValue); // 通过闭包直接修改返回对象的属性 myReturnObj.value1 = newComputed.value1; myReturnObj.value2 = newComputed.value2; myReturnObj.value3 = newComputed.value3; // 可选:返回更新后的对象 return myReturnObj; } } return myReturnObj } } const myClass = new MyTestClass(); let myObj = myClass.objFactory(10); console.log(myObj); // {value1:10, value2:20, value3:30, updateValues: ƒ} myObj.updateValues(20); console.log(myObj); // {value1:20, value2:40, value3:60, updateValues: ƒ}
如果需要严格限制用户直接修改属性,可通过Object.defineProperty将value1-3设为不可写:
let myReturnObj = {}; // 定义只读属性 Object.defineProperty(myReturnObj, 'value1', { value: computedValues.value1, writable: false, enumerable: true }); Object.defineProperty(myReturnObj, 'value2', { value: computedValues.value2, writable: false, enumerable: true }); Object.defineProperty(myReturnObj, 'value3', { value: computedValues.value3, writable: false, enumerable: true }); // updateValues中重新定义属性实现更新 updateValues: (newValue) => { let newComputed = this.changeValueProperties(newValue); Object.defineProperty(myReturnObj, 'value1', { value: newComputed.value1, writable: false, enumerable: true }); Object.defineProperty(myReturnObj, 'value2', { value: newComputed.value2, writable: false, enumerable: true }); Object.defineProperty(myReturnObj, 'value3', { value: newComputed.value3, writable: false, enumerable: true }); return myReturnObj; }
此时用户直接修改myObj.value1 = 100会失效,必须通过updateValues方法更新。
方案二:用内部类封装返回对象(更规范的架构)
若追求更严谨的面向对象设计,可将返回对象封装为内部类,this指向更清晰,封装性更强:
class MyTestClass { dummyData; constructor() { this.dummyData = 100; } // 私有内部类,用于创建返回对象 #InnerObj = class { #parent; // 持有外部类引用,用于调用changeValueProperties #value1; #value2; #value3; constructor(parent, initialValue) { this.#parent = parent; const computed = parent.changeValueProperties(initialValue); this.#value1 = computed.value1; this.#value2 = computed.value2; this.#value3 = computed.value3; } // 提供getter让用户读取属性,无法直接修改 get value1() { return this.#value1; } get value2() { return this.#value2; } get value3() { return this.#value3; } updateValues(newValue) { const computed = this.#parent.changeValueProperties(newValue); this.#value1 = computed.value1; this.#value2 = computed.value2; this.#value3 = computed.value3; return this; } } changeValueProperties(value) { return { value1: value*1, value2: value*2, value3: value*3 } } objFactory(value, ...otherParameters) { return new this.#InnerObj(this, value); } } const myClass = new MyTestClass(); let myObj = myClass.objFactory(10); console.log(myObj.value1); // 10 myObj.updateValues(20); console.log(myObj.value1); // 20
该方案优势:
updateValues中的this自然指向内部类实例(即返回给用户的对象)- 私有字段
#parent、#InnerObj、#value1-3隐藏内部细节,符合封装原则 - 用户只能通过getter读取属性,无法直接修改,完全满足需求
内容的提问来源于stack exchange,提问作者Collaptic
相关产品推荐
相关产品推荐

