JavaScript 的 Array 无法清空的奇怪问题

clear = function (ary) {
  ary = [];
};

a = [1, 2, 3];

clear(a);

console.log(a);

在调用 clear(a) 的时候,不是已经把进行了赋值 a = []
为什么 console.log(a) 还是输出 [1, 2, 3]

阅读 8.3k
13 个回答

清空函数体用ary.length = 0,手机不方便,过会给你解释原因。

原因来了:
传参时, ary[1, 2, 3]的引用(类似于指针);
改变ary的内容会反映到外面, 如arr.push(4);
但是直接覆盖ary[]则相当于ary指向新的对象, 不再指向原来的[1, 2, 3], 但是[1, 2, 3]还在那, 即你没有对其做任何改动.

额...

1. clear = function (ary) {
ary = [];
};

你要明白,传参数传的是数值,不是参数名!这里传的是参数值[1,2,3],不是参数名 a ,会把[1,2,3]赋值给ary,所以此时 ary = [1,2,3] ,然后给ary赋值为空数组,ary = []。a 还是原来的值,并没有改变

2.console.log(a); 这里的 a 依然还是全局变量中的 a

可以F12 运行下下面的代码:

var clear = function (ary) {
    console.log(ary); //输出:[1,2,3]     
    ary = [];
    console.log(ary); //输出:[]  
    console.log(a); //输出:[1,2,3]  
};
var a = [1, 2, 3];
clear(a);
console.log(a); //输出:[1,2,3]  

解释为什么修改成 ary.length = 0;后数组a 也会变成空数组,而 ary = [ ]不会有影响。

上面也通俗的讲了传参的是数值,准确地讲是JS中所有的函数都是按值传递的,不论这个参数是基本类型的还是引用类型的。
基本类型的参数比较好理解,相当于函数的局部变量,在函数体内修改参数不会影响函数外部的变量。
如果是引用类型,此题就是,这个数组对象 a 被传递到clear函数中之后复制给了ary,在这个函数的内部,arya 引用的都是同一个对象,所以在函数内部改变这个引用对象的属性的时候,函数外部的a也会相应的改变。
好,现在要捋一捋ary.length = 0;ary = [ ] 这两种清空数组的方式的区别了,简单来说,前者保留了数组对象的其他属性,而后者没有,因为其实它并不能说是严格意义的清空数组,只是将ary重新赋值为空数组,与 a 没有半毛钱关系了,所以如果之前的数组没有引用在指向它,将等待垃圾回收。
所以,如果是ary = [ ],此时arya引用了不同的对象,则对 数组 a 没有影响,如果是ary.length = 0;因为arya依然引用同一对象,所以修改了ary后也会反应到a上。

不知道有没解释清楚。

传入 ary 的并不是 全局数组 a 的值哦!如果学过 c 的话就是类似于 c 的指针,如果没学过就当我没说 XD

将你的全局数组传入到 局部变量 ary 后,就可以通过 ary 修改 那个数组 a 的属性
a.length 啦、a.push() 啦等等等等...

但是你如果直接 ary = [],你就断开了 ary 与你的数组 a 的连接,此时的 ary 变成了新的数组。

一句话 ary.length = 0 可以清空的原因是修改了属性,直接 ary = [] 是断开了连接

我想到了个比较清晰的说法,如下

邻居家有一美女,你看到好喜欢哦(此时美女是邻居家的)
你追上了邻居家的美女,她是你的女朋友了(她依旧是邻居家的,不过此时也是你家的)
女友有个衣服的数组(她身上穿了很多衣服)
你给美女买了衣服(修改了女朋友的属性 nvYou.yiFu.push('名牌内衣'))
晚上要做羞羞的事情了(nvYou.yiFu.length = 0)脱衣服你懂得
所以数组可以被清空

但是!!!有一天,你移情别恋了直接换了女友
你.女友 = 新女友(就是 ary = [])
那么请问你脱新女友的衣服,原来女友的衣服会被脱掉吗?
就酱 XD,还不懂你就是猪猪侠!!!

// 再次补充,你的代码应该翻译成这样
var 人 = 邻居的女儿;
var 邻居女儿的衣服 = [];

// 你给邻居女儿买衣服
邻居女儿的衣服.push('外套', '裤子', '鞋子');

// 重点来了! 你没有清空数组哦,你是换了女友
function 换女友(我的女友) {
  我的女友 = [新女友];
}

// 清空数组应该这样写
function 脱衣服(我的女友) {
  我的女友.衣服的数量 = 0;
}

// 想用 ary = [] 这样的写法,直接操作数组就可以,详细的如下

你并没有修改到那个数组,而是将函数中的局部变量 ary 重新赋值为一个新的数组 []

正常的代码应该是更有逻辑性的。下面只是个小例子,只是让你了解下是怎么回事

// 我的数组在某个对象中
var oData = {
  aArray1: [],
  aArray2: []
};

// 赋值
oData.aArray1.push(1);
console.log(oData.aArray1);

clear(oData);
console.log(oData.aArray1);

function clear(oData) {
  oData.aArray1 = []; // 此时修改的才是那个数组
}

在你的代码中数组和那个清空函数是有关联的,一般是写在一起比较好。建议看下这个回答
http://segmentfault.com/q/1010000001818410/a-1020000001920291

// 这个类似于其他语言中的类
var MyApp= function() {
    function MyApp() {
        this.aData = [];
        // ... 其他的属性
    }

    MyApp.prototype.clear = function() {
        this.aData = [];
    }

    return MyApp;
}();

// 接下来是使用了
var oApp = new MyApp();
oApp.aData.push(1);
console.log(oApp.aData);
oApp.clear();
console.log(oApp.aData);

// ------ 输出 ------
[1] 
[] 

一旦用了=给ary赋值,这就是一个新的数组了(对象也是同理)
要清空数组,
你可以通过改变数组的length属性,如ary.length = 0,
或则使用操纵当前数组的方法,如ary.splice(0,ary.length)
总之不要产生新的数组,

其实就是JS对象的基本知识,传值传的是对象的引用,而JS中数组也是一个对象。
所以,ary.length = 0 修改的是[1,2,3]这个数组的长度,因此能起到作用;
ary = [] 则是将[]的引用赋值给ary ,ary因此指向了[],现在你还能指望ary修改[1,2,3]?
所以a引用的[1,2,3]并没有变化

进入函数:
ary=a;//参数值传递
这个时候:
因为a->[1,2,3];//->表示指向
所以ary->[1,2,3];
ary=[];//函数体执行
这个时候:
ary->[];
但是a->[1,2,3];//a没有改变;

太多太大段的描述,看的好累。我来精简一下:

数字,字符串等都是值类型,而 plain object,array 等都是引用类型。

给你举个栗子你立马能明白:

jsvar a = [1,2,3];
var b = a;

b.push(4);
console.log(a); // [1,2,3,4]

var num = 123;
var _num = num;
_num = 456;

console.log(num); // 123

虽不是就题论题,但思路很重要。

function clear(arry){
arry.length = 0;
return arry;

arry.length = 0 修改的还是原来数组
arry =[] []的引用赋值给arry,而arry原来指向的数组并没有改变

aarr都只是保存了一个地址,这个地址指向堆中的数据[1, 2, 3],调用clear时,只是把a的地址拷贝给arr,在clear函数里面,arr = []指向了另外一个地址,但并不影响外面的a,类似的情况,请参考nodejs中module.exportsexports的区别!

终于遇到了一个人人都回答得起的问题了。好开心。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏