JavaScript如何正确访问对象上下文?寻求替代传参的更优方案
解决JavaScript中处理函数上下文绑定的优雅方案
你遇到的是典型的函数上下文丢失问题——当_notifHandlers里的方法被单独调用时,它们的this并不会自动指向外层的obj对象。除了手动传this参数,还有几种更优雅的实现方式,我给你逐一拆解:
方法1:使用ES6箭头函数(最简洁)
箭头函数不会绑定自身的this,它会继承外层作用域的this。直接把_notifHandlers里的函数换成箭头函数就能解决问题:
var obj = { handleNotification: function(managedObj) { if (this._notifHandlers.hasOwnProperty(managedObj.id)) { this._notifHandlers[managedObj.id](managedObj); } else { this._notifHandlers.typeGeneric(managedObj); } }, _notifHandlers: { typeA: (managedObj) => { console.info("_notifHandlers " + managedObj.id + " selected."); var model = "modelA"; this.showUI(managedObj, model); // 这里的this会指向obj }, typeB: (managedObj) => { console.info("_notifHandlers " + managedObj.id + " selected."); var model = "modelB"; this.showUI(managedObj, model); }, typeGeneric: (managedObj) => { console.info("_notifHandlers " + managedObj.id + " selected."); var model = "typeGeneric"; this.showUI(managedObj, model); } }, showUI: function(managedObj, model) { console.log("In showUI(): model -> " + model); } }; var managedObject = { id: "typeB" }; obj.handleNotification(managedObject);
优点:代码改动最小,逻辑直观;注意:箭头函数不支持arguments对象,也不能作为构造函数使用,不过这里完全适用。
方法2:调用处理函数时用call/apply指定上下文
在handleNotification里调用处理函数时,通过call或apply把当前的this(即obj)传递进去:
var obj = { handleNotification: function(managedObj) { const handler = this._notifHandlers[managedObj.id] || this._notifHandlers.typeGeneric; handler.call(this, managedObj); // 这里用call指定this为obj }, _notifHandlers: { typeA: function(managedObj) { console.info("_notifHandlers " + managedObj.id + " selected."); var model = "modelA"; this.showUI(managedObj, model); // this现在指向obj }, typeB: function(managedObj) { console.info("_notifHandlers " + managedObj.id + " selected."); var model = "modelB"; this.showUI(managedObj, model); }, typeGeneric: function(managedObj) { console.info("_notifHandlers " + managedObj.id + " selected."); var model = "typeGeneric"; this.showUI(managedObj, model); } }, showUI: function(managedObj, model) { console.log("In showUI(): model -> " + model); } }; var managedObject = { id: "typeB" }; obj.handleNotification(managedObject);
优点:不需要修改处理函数的定义,灵活性高;如果后续需要切换上下文,直接修改call的第一个参数即可。
方法3:初始化时绑定处理函数的上下文
在obj初始化的时候,用bind把每个处理函数的this绑定到obj上,这样每次调用时this都是正确的:
var obj = { handleNotification: function(managedObj) { if (this._notifHandlers.hasOwnProperty(managedObj.id)) { this._notifHandlers[managedObj.id](managedObj); } else { this._notifHandlers.typeGeneric(managedObj); } }, _notifHandlers: {}, showUI: function(managedObj, model) { console.log("In showUI(): model -> " + model); } }; // 初始化时绑定上下文 obj._notifHandlers.typeA = function(managedObj) { console.info("_notifHandlers " + managedObj.id + " selected."); var model = "modelA"; this.showUI(managedObj, model); }.bind(obj); obj._notifHandlers.typeB = function(managedObj) { console.info("_notifHandlers " + managedObj.id + " selected."); var model = "modelB"; this.showUI(managedObj, model); }.bind(obj); obj._notifHandlers.typeGeneric = function(managedObj) { console.info("_notifHandlers " + managedObj.id + " selected."); var model = "typeGeneric"; this.showUI(managedObj, model); }.bind(obj); var managedObject = { id: "typeB" }; obj.handleNotification(managedObject);
优点:一劳永逸,后续调用处理函数时不需要再关注上下文;注意:bind会返回新的函数,不能再修改这些函数的上下文了。
方法4:用ES6 Class重构(最适合复杂场景)
如果你的代码后续会扩展,用Class来组织会更清晰,类中的方法默认会绑定到实例(只要不是箭头函数,调用时注意不要丢失上下文即可):
class NotificationHandler { constructor() { this._notifHandlers = { typeA: this.typeAHandler, typeB: this.typeBHandler, typeGeneric: this.typeGenericHandler }; } handleNotification(managedObj) { const handler = this._notifHandlers[managedObj.id] || this._notifHandlers.typeGeneric; handler.call(this, managedObj); // 或者把handler定义成箭头函数,就不用call了 } typeAHandler(managedObj) { console.info("_notifHandlers " + managedObj.id + " selected."); const model = "modelA"; this.showUI(managedObj, model); } typeBHandler(managedObj) { console.info("_notifHandlers " + managedObj.id + " selected."); const model = "modelB"; this.showUI(managedObj, model); } typeGenericHandler(managedObj) { console.info("_notifHandlers " + managedObj.id + " selected."); const model = "typeGeneric"; this.showUI(managedObj, model); } showUI(managedObj, model) { console.log("In showUI(): model -> " + model); } } const obj = new NotificationHandler(); const managedObject = { id: "typeB" }; obj.handleNotification(managedObject);
优点:代码结构更清晰,便于维护和扩展;如果把类里的处理方法改成箭头函数,连call都不需要了。
内容的提问来源于stack exchange,提问作者Kiran Mohan
相关产品推荐
相关产品推荐

