前言: 面试的时候咱们常常会被问答js的数据类型。大部分状况咱们会这样回答包括
一、基本类型(值类型或者原始类型): Number、Boolean、String、NULL、Undefined以及ES6的Symbol
二、引用类型:Object、Array、Function、Date等
我曾经也是这样回答的,而且一直以为没有什么问题。
栈(stack)为自动分配的内存空间,它由系统自动释放;使用一级缓存,被调用时一般处于存储空间中,调用后被当即释放。
堆(heap)则是动态分配的内存,大小不定也不会自动释放。使用二级缓存,生命周期与虚拟机的GC算法有关
当一个方法执行时,每一个方法都会创建本身的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将天然销毁了。所以,全部在方法中定义的变量都是放在栈内存中的;栈中存储的是基础变量以及一些对象的引用变量,基础变量的值是存储在栈中,而引用变量存储在栈中的是指向堆中的数组或者对象的地址,这就是为什么修改引用类型总会影响到其余指向这个地址的引用变量。git
当咱们在程序中建立一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(由于对象的建立成本一般较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即便方法结束后,这个对象还可能被另外一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。es6
对于引用类型值,github
- | 和原数据是否指向同一对象 | 第一层数据为基本数据类型 | 原数据中包含子对象 |
---|---|---|---|
赋值 | 是 | 改变会使原数据一同改变 | 改变会使原数据一同改变 |
浅拷贝 | 否 | 改变不会使原数据一同改变 | 改变会使原数据一同改变 |
深拷贝 | 否 | 改变不会使原数据一同改变 | 改变不会使原数据一同改变 |
var obj1 = { 'name' : 'zhangsan', 'age' : '18', 'language' : [1,[2,3],[4,5]], }; var obj2 = obj1; var obj3 = shallowCopy(obj1); function shallowCopy(src) { var dst = {}; for (var prop in src) { if (src.hasOwnProperty(prop)) { dst[prop] = src[prop]; } } return dst; } obj2.name = "lisi"; obj3.age = "20"; obj2.language[1] = ["二","三"]; obj3.language[2] = ["四","五"]; console.log(obj1); //obj1 = { // 'name' : 'lisi', // 'age' : '18', // 'language' : [1,["二","三"],["四","五"]], //}; console.log(obj2); //obj2 = { // 'name' : 'lisi', // 'age' : '18', // 'language' : [1,["二","三"],["四","五"]], //}; console.log(obj3); //obj3 = { // 'name' : 'zhangsan', // 'age' : '20', // 'language' : [1,["二","三"],["四","五"]], //};
数组经常使用的浅拷贝方法有slice
,concat
,Array.from()
,以及es6的析构面试
var arr1 = [1, 2,{a:1,b:2,c:3,d:4}]; var arr2 = arr1.slice(); var arr3 = arr1.concat(); var arr4 = Array.from(arr1); var arr5 = [...arr1]; arr2[0]=2; arr2[2].a=2; arr3[0]=3; arr3[2].b=3; arr4[0]=4; arr4[2].c=4; arr5[0]=5; arr5[2].d=5; // arr1[1,2,{a:2,b:3,c:4,d:5}] // arr2[2,2,{a:2,b:3,c:4,d:5}] // arr3[3,2,{a:2,b:3,c:4,d:5}] // arr4[4,2,{a:2,b:3,c:4,d:5}] // arr5[5,2,{a:2,b:3,c:4,d:5}]
对象经常使用的浅拷贝方法Object.assign()
,es6析构算法
var obj1 = { x: 1, y: { m: 1 } }; var obj2 = Object.assign({}, obj1); console.log(obj1) //{x: 1, y: {m: 1}} console.log(obj2) //{x: 1, y: {m: 1}} obj2.x=2; obj2.y.m = 2; //修改obj2.y.m console.log(obj1) //{x: 1, y: {m: 2}} console.log(obj2) //{x: 2, y: {m: 2}}
咱们本身实现一个浅拷贝数组
var obj = { a:1, arr: [2,3] }; var shallowObj = shallowCopy(obj); 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; }
比较简单粗暴的的作法是使用JSON.parse(JSON.stringify(obj))
缓存
var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}] var new_arr = JSON.parse( JSON.stringify(arr) ); new_arr[4].old=4; console.log(arr); //['old', 1, true, ['old1', 'old2'], {old: 1}] console.log(new_arr); //['old', 1, true, ['old1', 'old2'], {old: 4}]
JSON.parse(JSON.stringify(obj))
看起来很不错,不过MDN文档 的描述有句话写的很清楚:函数
undefined、任意的函数以及 symbol 值,在序列化过程当中会被忽略(出如今非数组对象的属性值中时)或者被转换成 null(出如今数组中时)。
可是在平时的开发中JSON.parse(JSON.stringify(obj))
已经知足90%的使用场景了。
下面咱们本身来实现一个spa
var deepCopy = function(obj) { if (typeof obj !== 'object') return; var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return newObj; }
全部的函数参数都是按值传递。也就是说把函数外面的值赋值给函数内部的参数,就和把一个值从一个变量赋值给另外一个同样;prototype
var a = 2; function add(x) { return x = x + 2; } var result = add(a); console.log(a, result); // 2 4
引用类型
function setName(obj) { obj.name = 'laowang'; obj = new Object(); obj.name = 'Tom'; } var person = new Object(); setName(person); console.log(person.name); //laowang
不少人错误地觉得在局部做用域中修改的对象在全局做用域中反映出来就是说明参数是按引用传递的。
可是经过上面的例子能够看出若是person是按引用传递的最终的person.name应该是Tom。
实际上当函数内部重写obj时,这个变量引用的就是一个局部变量了。而这个变量会在函数执行结束后销毁。(这是是在js高级程序设计看到的,还不是很清楚)
基本类型用typeof
,引用类型用instanceof
特别注意typeof null
是"object"
,null instanceof Object
是true
;
console.log(typeof "Nicholas"); // "string" console.log(typeof 10); // "number" console.log(typeof true); // "boolean" console.log(typeof undefined); // "undefined" console.log(typeof null); // "object" var items = []; var obj = {}; function reflect(value){ return value; } console.log(items instanceof Array); // true; console.log(obj instanceof Object); // true; console.log(reflect instanceof Function); // true;
Object.prototype.toString.call([]).slice(8, -1)
有兴趣的同窗能够看一下这个是干什么的
若是有错误或者不严谨的地方,请务必给予指正,十分感谢。若是喜欢或者有所启发,欢迎 star对做者也是一种鼓励。