JavaScript中嵌套在其他对象的方法如何存储?覆写原方法后仍可执行原因
问题解答
为什么修改fn.getN1后obj[1]()仍然能执行旧方法?
这是JS中引用类型的赋值规则决定的:
- 函数属于引用类型,你执行
let obj = {1: fn.getN1}时,是把fn.getN1指向的函数对象的内存地址复制给了obj[1],此时obj[1]和fn.getN1是两个独立的属性,只是指向同一个函数对象而已。 - 后续执行
fn.getN1 = "test",只是把fn对象的getN1属性的指向,从原来的函数地址修改为了字符串test的地址,这个操作不会影响obj[1]的指向。 - 原有的
getN1函数对象因为仍然被obj[1]引用,不会被垃圾回收机制清理,所以调用obj[1]()依然可以正常执行。你打印fn看不到旧方法,只是因为fn的getN1属性已经不再指向这个函数对象,不等于函数对象本身从内存中被清除了。
你提供的代码执行逻辑也符合这个规则:
function Factory() { let n = 0; function getN1() { console.log(n); } function getN2() { console.log(n); } function incrN() { n++ } return {getN1, getN2, incrN} } let fn = Factory(); let obj = {1: fn.getN1, 2: fn.incrN} fn.getN1 = "test"; console.log(fn); obj["2"](); // 闭包变量n变为1 obj[1](); // 输出1 fn.getN2(); // 输出1 console.log(fn.getN1); // 输出test
这种写法是否属于不良实践?
不存在绝对的好坏,分场景判断:
- 如果你是故意要缓存原始的方法引用、避免原对象属性修改带来的影响,这种写法是完全合理的。
- 如果你是误以为修改原对象的属性会同步更新所有之前的赋值引用,那这个特性就是容易出bug的坑点。实际开发中如果要做关联更新,不要直接赋值函数引用,可以改成让
obj存储方法的键名:let obj = {1: 'getN1', 2: 'incrN'},调用时写fn[obj[1]](),就能同步fn的属性修改。 - 额外注意:如果方法内部用到了
this,直接把方法赋值给其他对象调用时,this的指向会变成调用它的新对象,会出现预期外的问题。这种场景下如果要保留原this指向,需要用bind方法绑定:let obj = {1: fn.getN1.bind(fn)}。
内容的提问来源于stack exchange,提问作者Mohemi
相关产品推荐
相关产品推荐

