如何正确传递JavaScript属性,使其可被函数正常修改
原理说明
首先纠正一个常见误区:JavaScript 中不存在严格的「按引用传递」,所有参数传递都是按值传递,只是对象类型的参数传递的值是「对象的内存地址拷贝」,这种传递规则也被称为「按共享传递」。
你提供的代码不生效的原因
当你调用this.change(this.letters, this.numbers)时,实际发生的操作是:
- 把
this.letters存储的「指向["A", "B"]数组的内存地址」拷贝一份,赋值给形参_letters - 把
this.numbers存储的「指向[1,2,3]数组的内存地址」拷贝一份,赋值给形参_numbers
此时形参和实例属性指向同一个内存中的对象,但形参本身是独立的局部变量。你在函数中写的l = []、n = 6属于给局部变量重新赋值,只是把局部变量存储的地址替换成了新对象/新值的地址,完全不会修改原来的内存对象,也不会影响实例上的属性,所以自然看不到变化。
什么时候会修改到原对象
如果你没有给局部变量重新赋值,而是通过局部变量存储的地址,修改指向的原对象本身,就会影响到外部的源属性,比如写l.push("C")、l[0] = "Z"、l.length = 0这类操作,都是直接操作内存中同一个数组,实例属性因为指向同一个数组,就会同步看到修改。
这也是为什么你需要Array.from这类拷贝操作的场景:如果你不想修改原对象,就先通过拷贝生成一个新的数组,让局部变量指向新数组的地址,后续的修改就只会作用在新数组上,不会影响源对象。
实现修改多个属性的方案
你想要在单次函数调用中修改多个关联属性、返回值留作调试用,可以用两种方案实现:
方案1:直接修改原对象本身,不重新赋值局部变量
如果要修改的是对象/数组的内容,不需要重新赋值局部变量,直接操作原对象即可:
class Thing { constructor( ) { this.letters = [ "A", "B" ]; this.numbers = [ 1, 2, 3 ]; const debugInfo = this.change(this.letters, this.numbers); console.log(debugInfo); // 输出调试信息 } change(_letters, _numbers) { // 清空原数组,而不是给局部变量赋值新数组 _letters.length = 0; // 替换原数组的所有内容 _numbers.splice(0, _numbers.length, 4, 5, 6); // 返回调试信息 return { lettersNowLength: _letters.length, numbersFirstItem: _numbers[0] } } } var t = new Thing( ); console.log( t ); // letters是空数组,numbers是[4,5,6]
方案2:直接在实例方法中操作this属性
因为change本身是类的实例方法,可以直接访问实例的this,不需要传参就能修改多个属性,写法更简洁:
class Thing { constructor( ) { this.letters = [ "A", "B" ]; this.numbers = [ 1, 2, 3 ]; const debugInfo = this.change(); console.log(debugInfo); } change() { // 直接修改实例属性,支持替换成任意类型的值 this.letters = []; this.numbers = 6; // 返回调试信息 return { letters: this.letters, numbers: this.numbers } } } var t = new Thing( ); console.log( t ); // letters是空数组,numbers是6
内容的提问来源于stack exchange,提问作者POD
相关产品推荐
相关产品推荐

