基本数据类型是按值访问的,由于能够操做保存在变量中的实际的值;
引用类型的值是保存在内存中的对象,在操做对象时,其实是在操做对象的引用而不是实际的对象;javascript
若是一个变量存储的是值的自己那么就是一个值类型number / string / Boolean / Null / Undefined —值类型的变量自己就是含有赋予给它的数值的,它的变量自己及保存的数据都存储在栈的内存块当中,当声明一个值类型时,必须对它初始化(给变量赋值)才能使用java
var num1 = 123, num2 = num1; num1 = 456; console.log(num2);// 123
将值类型复制给另一个值时(num2=num1),也就是num2从新再栈上开辟了一块空间,而后将num1中的内容复制一份放在num2中,当改变其中一个变量的值时,不会影响另一个变量的值面试
若是一个变量存储的是引用(地址),那么就是一个引用类型object—引用类型的值的存储与值类型不一样,它分别存储在内存的堆和栈中,栈中存放的是指向堆中内容的地址,堆中存放的引用类型的地址(键值对)segmentfault
var obj1 = {name: "xyc"}; var obj2 = obj1; obj1.name = "lxy"; console.log(obj2.name); // "lxy"
obj2=obj1表示的是将栈上的地址复制一份给另外一个对象,他们同时指向堆中的内容,当修改内容时,两个对象中的值都会发生改变数组
var o = new Object(); function foo(obj) { obj.name = "xyc"; obj = new Object(); obj.name = "lxy"; } foo(o); console.log(o.name); // ???
图解:
(1)新建对象var o = new Object();
函数
(2)在foo的环境下执行obj.name = "xyc"
因为是参数传递,在局部做用域内至关于执行了obj = o
post
(3)在局部做用域内新建对象,并赋值相同的属性值性能
obj = new Object(); obj.name = "lxy";
(4)foo()执行完毕,局部做用域出栈,obj声明周期结束
此时,新建的对象依然存在,等待下一次内存自动回收机制将堆中的无引用对象销毁spa
什么是深浅拷贝?在mac电脑中咱们能够对某个文件夹建立替身或者复制粘贴某个文件/文件夹,这两种方式都实现了对某个文件/文件夹的拷贝,可是,前者在文件中修改文件内容时,源文件也会修改,然后者的操做在修改文件内容时不会对源文件有影响code
上面的例子,“建立替身” ==> 浅拷贝,“复制粘贴” ==> 深拷贝
从内存角度说明:浅拷贝只会在栈内存中开辟空间存放指向源文件的变量,而深拷贝会在堆内存也拷贝文件
深浅拷贝仅是针对于数组和对象而言,不严谨的说法,基本类型的拷贝都属于深拷贝
(1)浅拷贝(最为常见)
var obj1 = {name: "xyc"}; var obj2 = obj1; obj1.name = "lxy"; console.log(obj2.name); // "lxy"
仅将栈内存复制,堆内存中的指向依然相同,obj2对象改变后,会影响obj1对象
(2)拷贝对象及对象下一层的属性和方法
var shallowCopy = function(obj) { // 只拷贝对象 if (typeof obj !== 'object') return; // 根据obj的类型判断是新建一个数组仍是对象 var newObj = obj instanceof Array ? [] : {}; // 遍历obj,而且判断是obj的属性才拷贝 for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = obj[key]; } } return newObj; }
var obj1 = { name: "xyc", features: { say: "hello", eat: "something" } } var obj2 = shallowCopy(obj1); obj2.features.eat = "anything"; console.log(obj1.features.eat); // "anything" console.log(obj1 === obj2); // false console.log(obj1.features === obj2.features); // true
上面这个例子能够看出上述方式的复制在对象内嵌套对象是不可以实现“类深拷贝”的,下面有一个进阶型的
(3)递归调用对象中嵌套的对象
var deepCopy = function(obj) { // 只拷贝对象 if (typeof obj !== 'object') return; // 根据obj的类型判断是新建一个数组仍是对象 var newObj = obj instanceof Array ? [] : {}; // 遍历obj,而且判断是obj的属性才拷贝 for (var key in obj) { if (obj.hasOwnProperty(key)) { // 当obj中嵌套对象时,再次调用该方法 newObj[key] = typeof obj[key] !== 'object' ? obj[key] : deepCopy(obj[key]); } } return newObj; }
(4)一维数组技巧性的拷贝
经过slice()
和concat()
来实现
var arr = ['old', 1, true, null, undefined]; var new_arr = arr.concat(); // var new_arr = arr.slice(); new_arr[0] = 'new'; console.log(arr) // ["old", 1, true, null, undefined] console.log(new_arr) // ["new", 1, true, null, undefined]
一如上面的对象嵌套,多维数组使用上面的方式拷贝不完全
(5)粗暴的拷贝方式
经过JSON.parse( JSON.stringify() )
实现
var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}] var new_arr = JSON.parse( JSON.stringify(arr) ); console.log(new_arr === arr);// false console.log(new_arr[3] === arr[3]);// false
可是这种方式没法实现函数的拷贝
var arr = [function(){ console.log(a) }, { b: function(){ console.log(b) } }] var new_arr = JSON.parse(JSON.stringify(arr)); console.log(new_arr);// [null, object]
函数经过这个方式会被转换成null
(6)补全深拷贝
var deepCopy = function(obj){ var str, newobj = obj.constructor === Array ? [] : {}; if(typeof obj !== 'object'){ return; } else if(window.JSON){ str = JSON.stringify(obj), //系列化对象 newobj = JSON.parse(str); //还原 } else { for(var i in obj){ newobj[i] = typeof obj[i] === 'object' ? cloneObj(obj[i]) : obj[i]; } } return newobj; };
完全的深拷贝理论上是将对象的整个原型链拷贝(不管原型属性是否为enumerable均应拷贝),遍历的次数越多,性能消耗越大。所以,出于性能的考虑,在拷贝的方式选择上,应该结合具体的业务环境来进行选择