面试官问你JS基本类型时他想知道什么?
面试的时候我们经常会被问答js的数据类型。大部分情况我们会这样回答包括:
1.基本类型(值类型或者原始类型): Number、Boolean、String、NULL、Undefined以及ES6的Symbol
2.引用类型:Object、Array、Function、Date等
作者曾经也是这样回答的,并且一直觉得没有什么问题。那么面试官问你Js数据类型时,他想知道什么呢?
1 、在内存中的位置不同
基本类型: 占用空间固定,保存在栈中;
引用类型:占用空间不固定,保存在堆中;
栈(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高级程序设计看到的,还不是很清楚)
4、判断方法
基本类型用typeof
,引用类型用instanceof
特别注意
typeof null
是"object"
,null instanceof Object
是false;
- 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、总结
本文原载于SegmentFault专栏"前端小将"
面试官问你JS基本类型时他想知道什么?的更多相关文章
- 面试官问:JS的this指向
前言 面试官出很多考题,基本都会变着方式来考察this指向,看候选人对JS基础知识是否扎实.读者可以先拉到底部看总结,再谷歌(或各技术平台)搜索几篇类似文章,看笔者写的文章和别人有什么不同(欢迎在评论 ...
- [每日一题]面试官问:谈谈你对ES6的proxy的理解?
[每日一题]面试官问:谈谈你对ES6的proxy的理解? 关注「松宝写代码」,精选好文,每日一题 作者:saucxs | songEagle 一.前言 2020.12.23 日刚立的 flag,每日一 ...
- 每日一问:面试结束时面试官问"你有什么问题需要问我呢",该如何回答?
面试结束时面试官问"你有什么问题需要问我呢",该如何回答?
- 当面试官问我ArrayList和LinkedList哪个更占空间时,我这么答让他眼前一亮
前言 今天介绍一下Java的两个集合类,ArrayList和LinkedList,这两个集合的知识点几乎可以说面试必问的. 对于这两个集合类,相信大家都不陌生,ArrayList可以说是日常开发中用的 ...
- 「每日一题」有人上次在dy面试,面试官问我:vue数据绑定的实现原理。你说我该如何回答?
关注「松宝写代码」,精选好文,每日一题 时间永远是自己的 每分每秒也都是为自己的将来铺垫和增值 作者:saucxs | songEagle 来源:原创 一.前言 文章首发在「松宝写代码」 2020. ...
- [每日一题]面试官问:Async/Await 如何通过同步的方式实现异步?
关注「松宝写代码」,精选好文,每日一题 时间永远是自己的 每分每秒也都是为自己的将来铺垫和增值 作者:saucxs | songEagle 一.前言 2020.12.23 日刚立的 flag,每日一 ...
- 面试官问,说一个你在工作非常有价值的bug
如果你去参考面试,做足了准备,面对面试官员从容不迫,吐沫横飞的大谈自己的工作经历.突然,面试官横插一句:说一个你在工作非常有价值的bug.顿时,整个空气都仿佛都凝固了!“What?”... 我想没几个 ...
- 【Java8新特性】面试官问我:Java8中创建Stream流有哪几种方式?
写在前面 先说点题外话:不少读者工作几年后,仍然在使用Java7之前版本的方法,对于Java8版本的新特性,甚至是Java7的新特性几乎没有接触过.真心想对这些读者说:你真的需要了解下Java8甚至以 ...
- 「干货」面试官问我如何快速搜索10万个矩形?——我说RBush
「干货」面试官问我如何快速搜索10万个矩形?--我说RBUSH 前言 亲爱的coder们,我又来了,一个喜欢图形的程序员,前几篇文章一直都在教大家怎么画地图.画折线图.画烟花,难道图形就是这样嘛,当 ...
随机推荐
- chrome:插件、跨域、调试....
chrome 调试小技巧 ctrl+shift+c 打开chrome的控制台选中一个元素,然后在控制台输入$0即可获取选中的元素,就可以对其进行操作了. $0.addEventListener(... ...
- MongoDB Limit/限制记录
Limit() 方法 要限制 MongoDB 中的记录,需要使用 limit() 方法. limit() 方法接受一个数字型的参数,这是要显示的文档数. 语法: limit() 方法的基本语法如下 & ...
- PHP 中提示undefined index如何解决(多种方法)
PHP 中提示undefined index如何解决(多种方法) 这篇文章主要介绍了PHP 中提示undefined index如何解决(多种方法)的相关资料,需要的朋友可以参考下 一.相关信息 平时 ...
- vue2.0中的计算属性
计算属性是一个很邪门的东西,只要在它的函数里引用了data中的某个属性,当这个属性发生变化的时候,函数仿佛可以嗅探到这个变化,并自动重新执行. 上代码会源源不断的打印出a的值.如果希望b依赖data中 ...
- MYSQL LOGBIN 数据日志恢复数据库随笔
查看指定的二进制日志中的事件(MYSQL命令行) mysql> show binlog events in 'binlogfullpath'; 查看二进制日志中的事件(MYSQL命令行) mys ...
- Frequently Used Algo
1. 链表 链表逆转 class Solution { public: ListNode* reverseList(ListNode* head) { ListNode* prev = NULL; w ...
- 关于webWorker的理解和简单例子
一.理解 当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成. web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能.您可以继续做任何愿 ...
- python面向对象编程(2)—— 实例属性,类属性,类方法,静态方法
1 实例属性和类属性 类和实例都是名字空间,类是类属性的名字空间,实例则是实例属性的名字空间. 类属性可通过类或实例来访问.只有通过类名访问时,才能修改类属性的值. 例外的一种情况是,当类属性是一个 ...
- Ajax学习---Ajax基础学习 180128
AJAX AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”.即使用Javascript语言与服务器进行异步交互,传输的数据为 ...
- Connection to linux server with ORACLE SQL DEVELOPER
1.Link name is random 2.username and password is database account 3.host name is ip address ifconf ...