两大类基本类型
-
原始类型
Null
Undefined
Boolean
Number
String
Symbol
BigInt(ES10 Chrome已支持) -
对象类型
Object
首先明确一点,ECMAScript中所有的函数的参数都是按值传递的。
三个例子带你搞懂为什么都是按值传递的
let foo = 'foo';
function update(foo) {
foo = 'bar';
}
update(foo);
console.log(foo); // 'foo'
运行一下就知道上面这个例子最终打印的是'foo'
,为什么?因为函数参数仅仅是被传入变量复制给了的一个局部变量,在函数内部改变这个变量不会对外部产生影响。
再看下面这个:
let foo = { name: 'bar' };
function update(foo) {
foo.name = 'opt';
}
update(foo);
console.log(foo.name); // 'opt'
这个结果会让你疑惑,是不是参数是引用类型的就是引用传递?
其实并不是,当函数参数是引用类型的时候,我们同样的将参数复制了一个副本到局部变量,但是这个复制的副本其实是一个指向堆内存中的地址,也就是说我们在函数内部通过引用对被引用的对象的属性进行了重新赋值的操作,实际上和在外部变量指向堆内存中的值相同,但这并不是引用传递。
最后一个例子:
let foo = {};
function update(foo) {
foo.name = 'bar';
foo = { name: 'opt' };
}
update(foo);
console.log(foo.name); // 'bar'
不要晕,根据第二个例子我们知道传进到这个函数中的'foo'
其实是一个指向外面声明的那个空对象的一个新的引用,函数内第一步'foo.name = 'bar';
这一步其实是在外面声明空对象中创建了一个'name'
属性并赋值'bar'
。但是第二步'foo = { name: 'opt' }'
其实是改变了这个引用指向的对象,也就是说这个函数内部第一行原来指向外部声明的那个{}
的引用在第二行指向了新对象,因而这个新的引用没有改变外面foo
引用指向的对象,所以最终打印出了'bar'