摘要:前言面试的时候我们经常会被问答的数据类型。大部分情况我们会这样回答包括基本类型值类型或者原始类型以及的引用类型等我曾经也是这样回答的,并且一直觉得没有什么问题。
前言: 面试的时候我们经常会被问答js的数据类型。大部分情况我们会这样回答包括1 、在内存中的位置不同
1、基本类型(值类型或者原始类型): Number、Boolean、String、NULL、Undefined以及ES6的Symbol
2、引用类型:Object、Array、Function、Date等
我曾经也是这样回答的,并且一直觉得没有什么问题。
基本类型: 占用空间固定,保存在栈中;
引用类型:占用空间不固定,保存在堆中;
栈(stack)为自动分配的内存空间,它由系统自动释放;使用一级缓存,被调用时通常处于存储空间中,调用后被立即释放。
堆(heap)则是动态分配的内存,大小不定也不会自动释放。使用二级缓存,生命周期与虚拟机的GC算法有关
当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;栈中存储的是基础变量以及一些对象的引用变量,基础变量的值是存储在栈中,而引用变量存储在栈中的是指向堆中的数组或者对象的地址,这就是为何修改引用类型总会影响到其他指向这个地址的引用变量。
当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。
2、赋值、浅拷贝、深拷贝对于基本类型值,赋值、浅拷贝、深拷贝时都是复制基本类型的值给新的变量,之后二个变量之间操作不在相互影响。
对于引用类型值,
赋值后二个变量指向同一个地址,一个变量改变时,另一个也同样改变;
浅拷贝后得到一个新的变量,这个与之前的已经不是指向同一个变量,改变时不会使原数据中的基本类型一同改变,但会改变会原数据中的引用类型数据
深拷贝后得到的是一个新的变量,她的改变不会影响元数据
- | 和原数据是否指向同一对象 | 第一层数据为基本数据类型 | 原数据中包含子对象 |
---|---|---|---|
赋值 | 是 | 改变会使原数据一同改变 | 改变会使原数据一同改变 |
浅拷贝 | 否 | 改变不会使原数据一同改变 | 改变会使原数据一同改变 |
深拷贝 | 否 | 改变不会使原数据一同改变 | 改变不会使原数据一同改变 |
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,["二","三"],["四","五"]], //};2.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; }2.2、深拷贝
比较简单粗暴的的做法是使用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%的使用场景了。
下面我们自己来实现一个
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; }3、参数的传递
所有的函数参数都是按值传递。也就是说把函数外面的值赋值给函数内部的参数,就和把一个值从一个变量赋值给另一个一样;
基本类型
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)有兴趣的同学可以看一下这个是干什么的5、总结
如果有错误或者不严谨的地方,请务必给予指正,十分感谢。如果喜欢或者有所启发,欢迎star对作者也是一种鼓励。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/103146.html
摘要:在反射方面来说,从运行时返回一个的实例时不需要经过强制转换然后则需要经过转换才能得到。如果对数据的数量大小已知,操作也非常简单,也不需要中的大部分方法,也是可以直接使用数组的。 showImg(https://segmentfault.com/img/bVbruTp?w=1920&h=1080); 我在想每个人在面试的时候都会被问到集合相关的问题,有好大一部分人在回答的时候并没有那么多...
摘要:背景在开发好页面后,如何让页面更快更好的运行,是区分一个程序猿技术水平和视野的一个重要指标。在对这些环节进行优化之前,我们需要知道如何监控这些环节花费了多少时间。为了优化链接的环节,前端这里还需要对资源使用,雪碧图,代码合并等手段。 背景 在开发好页面后,如何让页面更快更好的运行,是区分一个程序猿技术水平和视野的一个重要指标。所以面试时,面试官总会问你一个问题,如何进行性能优化呢? 如...
摘要:所谓高并发,就是同一时间有很多流量通常指用户访问程序的接口页面及其他资源,解决高并发就是当流量峰值到来时保证程序的稳定性。索引多主多从分布式数据库缓存连接池消息队列等是从数据库方便考虑如何优化性能。 所谓高并发,就是同一时间有很多流量(通常指用户)访问程序的接口、页面及其他资源,解决高并发就是当流量峰值到来时保证程序的稳定性。 我们一般用QPS(每秒查询数,又叫每秒请求数)来衡量程序的...
阅读 2021·2021-11-24 10:34
阅读 3026·2021-11-22 11:58
阅读 3676·2021-09-28 09:35
阅读 1684·2019-08-30 15:53
阅读 2694·2019-08-30 14:11
阅读 1496·2019-08-29 17:31
阅读 516·2019-08-26 13:53
阅读 2119·2019-08-26 13:45