1、引言
很多人都说,值类型无非就是JavaScript的五种基本数据类型- Undefined、 Null 、Boolean 、 Number、 String。引用类型就无非是Object、 Array。而它们就像你说的如此简单吗?
var a = 1;
a.name = 'mapbar_front';//这个会不会报错?
console.log(a.name);//结果undefined,那么这是为什么?
2、值类型和引用类型的特点
(1)、概念描述
JavaScript是这样定义值类型的:代表一些简单的数据段,可以直接操作保存在一个变量的具体数值。
相反,对于引用类型,JavaScript这样定义:它代表一个对象,你能操作的仅仅是这个对象的引用。
var a = 1;
var b = a;//这句代码的意思就是var b = 1;也就是说,b = a就代表着把a中代表的值赋给b。
b = 2;
console.log(a);//1 也就是给b赋值的时候,a的值不受影响。
本质上,值类型的赋值操作是重新创建了一个内存空间。这里代表a和b分别在内存中有两个值为 1 的内存空间。
var a = new Object();
a.name = 'mapbar_front';
var b = a;//这里赋值的是a的引用。
b.name = 'JavaScript';
console.log(a.name);//JavaScript,表示给b对象添加属性name的时候,a对象的name属性也变了
引用类型的本质,在赋值上是一个指向内存对象的一个指针。它在执行b = a的过程中,并没有创建新的内存空间。
(2)、值类型变量和引用类型变量的另一个重要特点
所有变量都可以给它添加属性和方法。但是只有引用类型的变量才起效果。
//引用上面的一个示例
var a = 1;
a.name = 'mapbar_front';//添加属性
console.log(a.name);//undefined。不起作用
//对于引用类型的变量
var b = new Object();
b.name = 'mapbar_front';
console.log(b.name);//'mapbar_front'。添加属性成功
3、参数的按值传递和按引用传递
(1)、描述
一定要切记-JavaScript的参数传递是按值传递
var a = 1;
function add(x){
return ++x;
}
var b = add(a);
console.log(a,b);//a为1不变,b为2.
所以说,虽然JavaScript看起来是这样,通过传递参数的方式,不改变这个传递的实参,即对外部变量不改变。但是有时候会发生下面的“奇怪”现象:
var obj = { name: 'mapbar_front'};
function getObject(obj){
obj.name = 'JavaScript';
return obj;
}
var obj2 = getObject(obj);
console.log(obj.name);//'JavaScript',是不是不科学????
所以,凡是引用类型的变量当做实参来传递给函数的时候,传递给的是一个对象的引用,在函数内部是可以改变外部的对象的!!
原因何在???因为内部的obj被赋值的是外部obj的一个引用,都指向同一个值为 { name: ‘mapbar_front’} 的内存地址。内部obj变量改变这个name,外部的也跟着改变。
这就引发了下面一个问题
(2)、参数传递的本质过程是什么?
参数传递的时候,函数会在其内部定义一个局部变量来接收传递过来的实参。示例如下。
var a = 1;
function add(a){
return ++a;
}
add(a);
相当于。。。
var a = 1;
add(a);//把1传入。
function add(){
var a = a;//把外部的a的值赋值给局部变量a。
++a;//其实操作的是自己的局部变量。
return a;
}
(3)、到底什么叫按引用传递?
按值传递的本质是重新定义一个局部变量来接收实参,而按引用传递是不重新定义局部变量,还是对原先传递的那个变量进行操作。
var obj = { name: 'mapbar_front'};
function add(obj){
obj.name = 'JavaScript';
obj = new Object();
obj.name = 'no JavaScript';
}
add(obj);
console.log(obj.name);//'JavaScript'。如果是按引用传递参数,这里打印出的应该是 'no JavaScript'。
这个也从侧面证明了,参数的传递是值传递,会在内部定义一个局部变量来接收参数。而不是按引用传递参数的。
(4)、这里给大家增加一个小小的面试题(对上面的小示例稍作修改)
var a = 1;
console.log(a);//1
function add(a){
var b = ++a;
console.log(a);//2,这个a是函数的局部变量
return a;
}
var c = add(a);
console.log(a);//1,a是全局变量
我的总结
1、JavaScript 的基本数据类型有五种- Undefined、 Null、 Numer、 String、 Boolean。
2、JavaScript 的引用数据类型有两种- Object、 Array。
3、基本数据类型的赋值是值传递,本质上是重新创建了一个新的内存空间,而引用数据类型的赋值是一个指向内存空间的指针,本质上还是原先的内存地址。
4、JavaScript的参数传递是按值传递。不是按引用传递。
5、由于JavaScript 的引用数据类型的特性,在编码中,如果我们需要在不改变原对象的基础上操作数据,可能就需要对对象进行clone。
一个关于对象 clone 的 JavaScript 函数封装:
//深度clone
function clone(obj) {
if(arguments.length !== 1){
throw new Error('parameter is undefined');
}
//如果是值类型,直接返回
if(typeof obj !== 'object'){
return obj;
}
//如果是Array类型
if(obj instanceof Array){
var arr = new Array();
for(var i = 0, len = obj.length; i < len; i++){
if(typeof obj[i] === 'object'){
arr.push(clone(obj[i]));
} else {
arr.push(obj[i]);
}
}
return arr;
}
//如果是Object类型
if(obj instanceof Object){
var current = new Object();
for(var i in obj){
if(typeof obj[i] === 'object'){
current[i] = clone(obj[i]);
} else {
current[i] = obj[i];
}
}
return current;
}
//如果是Null
if(obj === null){
return null
}
//如果是自己未知的类型
throw new Error('not supported this type data!');
}
//测试代码
var obj = {"name":'liwudi',"arr":[1,5,'s','ss',[5,9,null],9]};
var obj2 = clone(obj);
console.log(obj2);
obj['arr'][4][0] = 'change';
console.log(obj2);
//单层clone,或者说是浅克隆
function cloneOne(objValue) {
if(arguments.length !== 1){
throw new Error('parameter is undefined');
}
//如果是值类型,直接返回
if(typeof objValue !== 'object'){
return objValue;
}
//如果是Array类型
if(objValue instanceof Array){
var arr = new Array();
for(var i = 0, len = objValue.length; i < len; i++){
arr.push(objValue[i]);
}
return arr;
}
//如果是Object类型
if(objValue instanceof Object){
var obj = new Object();
for(var i in objValue){
obj[i] = objValue[i];
}
return obj;
}
//如果是Null
if(objValue === null){
return null
}
//如果是自己未知的类型
throw new Error('not supported this type data!');
}