数据类型
js常用数据类型分为基本类型和引用类型
- 基本类型:null、undefined、数值型、字符串型、布尔型
- 引用类型:数组、对象
内存空间
var a = [1, 2, 3]; var b = a; b[2] = 4; a; // ??
我们都知道结果是[1, 2, 4],因为b和a指向了同一个引用对象所以都可以改变该对象的值,我们用内存空间来深入理解一下。
我们知道在内存中存在两块区域,一个是栈stack,一个是堆heap。 通常我们的基本数据类型存储在栈中,而我们的引用数据类型存于堆中。栈中会有一个指针指向存在于堆中的数据以便于引用。
var arr = [1, 2, 3];
内存图应该是这样:
arr是一个基本类型的变量,该变量内部存储着数组的地址/指针,通过该地址可以找到存在于heap中的Array对象。通常我们说arr是一个引用类型我觉得是不严谨的,应该说:变量arr指向一个引用类型。
这时候会出现一个问题:允许两个指针指向同一个堆数据,这意味着通过其中一个指针篡改了数值那么会影响另一个指针。
var a = [1, 2, 3]; var b = a;
以上变量在内存中的图是这样的:
解决该问题的方法就是重新在heap中创建一个与a指向的引用对象一模一样的对象,然后让b指向它,
像这样:
这样一个b改变了就不会影响a了。
说个题外话:了解这一机制对理解prototype是有很大好处的
function Person(name) { this.name = name; } Person.prototype = { sayHi() { console.log("hi, " + this.name); } }; var p1 = new Person('lan'); p1.sayHi(); // 'hi, lan'
内存图如下,可以自己理解一下,找一找哪些存在于stack哪些存在于heap:
接上述,直接将a赋值为b这样为浅拷贝,而上上图中则为深拷贝。
js实现深拷贝思路:
- 先判断所赋值的类型,如果为基本类型则直接拷贝,若为对象类型则在heap中重新生成一个对象,再递归的将值赋值过去。
function deepCopy(obj) { var result = ''; // 为基本类型 if(obj == null || obj == undefined || typeof obj != 'object') return obj; // 为引用类型,判断为数组还是为对象 if(obj instanceof Array) result = []; else result = {}; for(var key in obj) { var current = obj[key]; if(current == null || current == undefined || typeof current != 'object') result[key] = current; else // 不直接调用deepCopy而用arguments.callee // 以避免函数被赋值给其他变量而出现错误 result[key] = arguments.callee(current); } return result; } var a = { name: 'lan', age: 20, birth: [1,2,3,4], like: { food: 'fruit', color: ['pink', 'blue'] } }; var b = deepCopy(a); b.birth[2] = 5; a.birth; // [1, 2, 3, 4]
以上为自己对于指针的一点思考,欢迎指正与扩展。