银行前端面试高频基础问题——varlet和const到底有哪些区别?讲不清楚当场发感谢信!?

Posted 普通网友

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了银行前端面试高频基础问题——varlet和const到底有哪些区别?讲不清楚当场发感谢信!?相关的知识,希望对你有一定的参考价值。

银行前端面试高频基础问题——var、let和const到底有哪些区别?讲不清楚当场发感谢信!?

  • 面试官:知道const、let和var吧,说说他们的区别吧

  • 我:… …

前言

可以说这是银行我们面试遇到的最高频的一个问题,也是一个很基础的问题了。所以,我们定然在这个问题上,不能讲不清楚,那就当场发感谢信了。当然,除此之外深入的还需要去了解一下const和let的实现原理。

let

let 关键字用来声明变量,使用 let 声明的变量有几个特点:

  1. 不允许重复声明
  2. 块儿级作用域(局部变量)
  3. 不存在变量提升
  4. 不影响作用域链
示例
// let关键字使用示例:
let a; // 单个声明
let b,c,d; // 批量声明
let e = 100 ; // 单个声明并赋值
let f = 521 , g = 'iloveyou', h = []; // 批量声明并赋值
不允许重复声明
let dog = "狗";
let dog = "狗";
// 报错:Uncaught SyntaxError: Identifier 'dog' has already been
declared
块儿级作用域(局部变量)

let cat = "猫";
console.log(cat);

console.log(cat);
// 报错:Uncaught ReferenceError: cat is not defined
不存在变量提升
// 什么是变量提升:就是在变量创建之前使用(比如输出:输出的是默认值),let不存在,var存在;

console.log(people1); // 可输出默认值
console.log(people2); // 报错:Uncaught ReferenceError: people2 is not defined
var people1 = "大哥"; // 存在变量提升
let people2 = "二哥"; // 不存在变量提升
不影响作用域链:
// 什么是作用域链:很简单,就是代码块内有代码块,跟常规编程语言一样,上级代码块中的局部变量下级可用
		
            let p = "大哥";
			var s;               //p的作用域是块(花括号内)级的,而s的作用域为函数作用域全局
            function fn() 
                console.log(p); // 这里是可以使用的
            
            fn();
        
应用场景

以后声明变量使用 let 就对了

let案例:点击div更改颜色
 // 获取div元素对象
        let items = document.getElementsByClassName('item');

        //ES5错误解法
        for (var i = 0; i < items.length; i++) 
            items[i].onclick = function () 
                items[i].style.background = 'pink'; 
                alert('点击了' + i + '个')
            
            // 由于点击事件是异步的,i又是全局的,所以每个item用的i都是同一个i。

        
        //ES5解法1
        for (var i = 0; i < items.length; i++) 
            items[i].onclick = function () 
                this.style.background = 'pink'; // 写法一:常规写法一般无异常
                alert('点击了' + i + '个')
            
        
        //ES5解法1
        for (var i = 0; i < items.length; i++) 
            //使用闭包,控制作用域
            (function (i) 
                items[i].onclick = function () 
                    items[i].style.background = 'pink'; // 写法二
                    alert('点击了' + i + '个')
                
            )(i)
        

        // ES6解法
        for (let i = 0; i < items.length; i++) 

            items[i].onclick = function () 
                // 修改当前元素的背景颜色
                // this.style.background = 'pink'; // 写法一:常规写法一般无异常
                items[i].style.background = 'pink'; // 写法二
                // 写法二:需要注意的是for循环内的i必须使用let声明 //点击事件是异步的
                // 如果使用var就会报错,因为var是全局变量,
                // 经过循环之后i的值会变成3,items[i]就会下标越界
                // let是局部变量
                // 我们要明白的是当我们点击的时候,这个i是哪个值
                // 下面的声明会将上面的覆盖掉,所以点击事件每次找到的都是3
                // 由于let声明的是局部变量,每一个保持着原来的值
                // 点击事件调用的时候拿到的是对应的i
            

        

const

const 关键字用来声明 常量 ,const 声明有以下特点:

  1. 声明必须赋初始值·;
  2. 标识符一般为大写(习惯);
  3. 不允许重复声明
  4. 值不允许修改
  5. 块儿级作用域(局部变量)

注:当常量为对象时,值不允许修改含义是指向的对象不能修改,但是可以改变对象内部的属性

示例
        // const声明常量
        const DOG = "旺财";
        console.log(DOG);
        // 1\\. 声明必须赋初始值;
        // const CAT;
        // 报错:Uncaught SyntaxError: Missing initializer in const  declaration

        // 2\\. 标识符一般为大写(习惯);
        // const dog = "旺财"; // 小写也不错

        // 3\\. 不允许重复声明;
        // const CAT = "喵喵";
        // const CAT = "喵喵";
        // 报错:Uncaught SyntaxError: Identifier 'CAT' has already been declared

       //  注意:对数组元素的修改和对对象内部的修改是可以的(数组和对象存的是引用地址);
        // 4\\. 值不允许修改;
        // const CAT = "喵喵";
        // CAT = "咪咪";
        // 特例:const arr = ['55','66'];
        // arr.push('77');  //arr的中已经接上了‘77’ 这是因为arr指向的地址并没有发生改变
        // 报错:Uncaught TypeError: Assignment to constant variable.

        // 5\\. 块儿级作用域(局部变量);
        // 
        // const CAT = "喵喵";
        // console.log(CAT);
        // 
        // console.log(CAT);
        // 报错:Uncaught ReferenceError: CAT is not defined

应用场景:

声明对象类型使用 const,非对象类型声明选择 let;

var、let和const的区别

  1. var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问. 并不是全局作用域

  2. let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。

  3. const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。

  4. var声明的变量存在变量提升现象,let和const声明的变量不存在变量提升现象(遵循:“先声明,后使用”的原则,否则会报错)。

  5. var不存在暂时性死区,let和const存在暂时性死区。

    解析:只要块级作用域内存在let命令,那么它所声明的变量就会绑定到这个区域,不再受外部的影响。

  6. var允许重复声明同一个变量,let和const在同一作用域下不允许重复声明同一个变量。

    解析:var允许重复声明变量,后一个变量会覆盖前一个变量,const和let会报错。

HTML

浏览器页面有哪三层构成,分别是什么,作用是什么?
html5的优点与缺点?
Doctype作用? 严格模式与混杂模式如何区分?它们有何意义?
HTML5有哪些新特性、移除了哪些元素?
你做的网页在哪些浏览器测试过,这些浏览器的内核分别是什么?
每个HTML文件里开头都有个很重要的东西,Doctype,知道这是干什么的吗?
说说你对HTML5认识?(是什么,为什么)
对WEB标准以及W3C的理解与认识?
HTML全局属性(global attribute)有哪些?
说说超链接target属性的取值和作用?
iframe有哪些缺点?
Label的作用是什么,是怎么用的?
如何实现浏览器内多个标签页之间的通信?
谈谈你对canvas的理解?

CSS

解释一下CSS的盒子模型?
请你说说CSS选择器的类型有哪些,并举几个例子说明其用法?
请你说说CSS有什么特殊性?(优先级、计算特殊值)
常见浏览器兼容性问题与解决方案?
列出display的值并说明他们的作用?
如何居中div, 如何居中一个浮动元素?
请列举几种清除浮动的方法(至少两种)?
block,inline和inlinke-block细节对比?
什么叫优雅降级和渐进增强?
说说浮动元素会引起的问题和你的解决办法
你有哪些性能优化的方法?
为什么要初始化CSS样式?
CSS样式表根据所在网页的位置,可分为哪几种样式表?
请你说说em与rem的区别?

JavaScript

js的各种位置,比如clientHeight,scrollHeight,offsetHeight ,以及scrollTop, offsetTop,clientTop的区别?
js拖拽功能的实现
异步加载js的方法
js的防抖与节流
说一下闭包
说说你对作用域链的理解
javascript原型,原型链 ? 有什么特点?
请解释什么是事件委托/事件代理
Javascript如何实现继承?
函数执行改变this
babel编译原理
函数柯里化
说一下类的创建和继承
说说前端中的事件流
如何让事件先冒泡后捕获
说一下图片的懒加载和预加载
js的new操作符做了哪些事情
改变函数内部this指针的指向函数(bind,apply,call的区别)
Ajax解决浏览器缓存问题

如果你需要这份完整版的面试笔记,可以点击这里直达领取方式。

Vue

Vue中 key 值的作用
Vue 组件中 data 为什么必须是函数?
vuex的State特性是?
介绍一下Vue的响应式系统
computed与watch的区别
介绍一下Vue的生命周期
为什么组件的data必须是一个函数
组件之间是怎么通信的
Vue.cli中怎样使用自定义的组件?有遇到过哪些问题吗?
Vue如何实现按需加载配合webpack设置
简单描述每个周期具体适合哪些场景
scss是什么?在Vue.cli中的安装使用步骤是?有哪几大特性?
聊聊你对Vue.js的template编译的理解?
Vue 路由跳转的几种方式
Vue如何实现按需加载配合webpack设置?
Vue的路由实现:hash模式和history模式
Vue与Angular以及React的区别?
Vue路由的钩子函数
什么是Vue的计算属性?

React

React单项数据流
react生命周期函数和react组件的生命周期
react和Vue的原理,区别,亮点,作用
reactJs的组件交流
有了解过react的虚拟DOM吗,虚拟DOM是怎么对比的呢
项目里用到了react,为什么要选择react,react有哪些好处
怎么获取真正的dom
选择react的原因
react的生命周期函数
setState之后的流程
react高阶组件知道吗?
React的jsx,函数式编程
react的组件是通过什么去判断是否刷新的
如何配置React-Router
路由的动态加载模块
Redux中间件是什么东西,接受几个参数
redux请求中间件如何处理并发

浏览器

跨标签页通讯
浏览器架构
浏览器下事件循环(Event Loop)
从输入 url 到展示的过程
重绘与回流
存储
Web Worker
V8垃圾回收机制
内存泄露
reflow(回流)和repaint(重绘)优化
如何减少重绘和回流?
一个页面从输入 URL 到页面加载显示完成,这个过程中都发生了什么?
localStorage 与 sessionStorage 与cookie的区别总结
http多路复用
cookie可设置哪些属性?
正向代理和反向代理

服务端与网络

HTTPS和HTTP的区别
HTTP版本
从输入URL到页面呈现发生了什么?
HTTP缓存
缓存位置
强缓存
协商缓存
缓存的资源在那里
用户行为对浏览器缓存的影响
缓存的优点
不同刷新的请求执行过程
为什么会有跨域问题
如何解决跨域
访问控制场景(简单请求与非简单请求)
withCredentials 属性
服务器如何设置CORS
URL类中的常用方法

算法与数据结构

二叉树层序遍历
B树的特性,B树和B+树的区别
尾递归
如何写一个大数阶乘?递归的方法会出现什么问题?
把多维数组变成一维数组的方法
说一下冒泡快排的原理
Heap排序方法的原理?复杂度?
几种常见的排序算法,手写
数组的去重,尽可能写出多个方法
如果有一个大的数组,都是整型,怎么找出最大的前10个数

最后

跳槽是升职涨薪最直接有效的方式,备战2021金九银十,各位做好面试造火箭,工作拧螺丝的准备了吗?

掌握了这些知识点,面试时在激烈竞争中又可以夺目不少。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。

如果你需要这份完整版的面试笔记,可以点击这里直达领取方式!

Java后端面试高频问题:HashMap的底层原理

1.HashMap底层实现

JDK1.8中HashMap的put()和get()操作的过程

put操作:

①首先判断数组是否为空,如果数组为空则进行第一次扩容(resize)

②根据key计算hash值并与上数组的长度-1(int index = key.hashCode()&(length-1))得到键值对在数组中的索引。

③如果该位置为null,则直接插入

④如果该位置不为null,则判断key是否一样(hashCode和equals),如果一样则直接覆盖value

⑤如果key不一样,则判断该元素是否为红黑树的节点,如果是,则直接在红黑树中插入键值对

⑥如果不是红黑树的节点,则就是链表,遍历这个链表执行插入操作,如果遍历过程中若发现key已存在,直接覆盖value即可。

如果链表的长度大于等于8且数组中元素数量大于等于阈值64,则将链表转化为红黑树,(先在链表中插入再进行判断)

如果链表的长度大于等于8且数组中元素数量小于阈值64,则先对数组进行扩容,不转化为红黑树

⑦插入成功后,判断数组中元素的个数是否大于阈值64(threshold),超过了就对数组进行扩容操作。

get操作:

①计算key的hashCode的值,找到key在数组中的位置

②如果该位置为null,就直接返回null

③否则,根据equals()判断key与当前位置的值是否相等,如果相等就直接返回。

④如果不等,再判断当前元素是否为树节点,如果是树节点就按红黑树进行查找。

⑤否则,按照链表的方式进行查找。

2.HashMap的扩容机制

3.HashMap的初始容量为什么是16?

1.减少hash碰撞 (2n ,16=24)

2.需要在效率和内存使用上做一个权衡。这个值既不能太小,也不能太大。

3.防止分配过小频繁扩容

4.防止分配过大浪费资源

4.HashMap为什么每次扩容都以2的整数次幂进行扩容?

因为Hashmap计算存储位置时,使用了(n - 1) & hash。只有当容量n为2的幂次方,n-1的二进制会全为1,位运算时可以充分散列,避免不必要的哈希冲突,所以扩容必须2倍就是为了维持容量始终为2的幂次方。

5.HashMap的扩容因子为什么是0.75?

当负载因子为1.0时,意味着只有当hashMap装满之后才会进行扩容,虽然空间利用率有大的提升,但是这就会导致大量的hash冲突,使得查询效率变低。

当负载因子为0.5或者更低的时候,hash冲突降低,查询效率提高,但是由于负载因子太低,导致原来只需要1M的空间存储信息,现在用了2M的空间。最终结果就是空间利用率太低。

负载因子是0.75的时候,这是时间和空间的权衡,空间利用率比较高,而且避免了相当多的Hash冲突,使得底层的链表或者是红黑树的高度也比较低,提升了空间效率。

6.HashMap扩容后会重新计算Hash值吗?

①JDK1.7

JDK1.7中,HashMap扩容后,所有的key需要重新计算hash值,然后再放入到新数组中相应的位置。

②JDK1.8

在JDK1.8中,HashMap在扩容时,需要先创建一个新数组,然后再将旧数组中的数据转移到新数组上来。

此时,旧数组中的数据就会根据(e.hash & oldCap),数据的hash值与扩容前数组的长度进行与操作,根据结果是否等于0,分为2类。

1.等于0时,该节点放在新数组时的位置等于其在旧数组中的位置。

2.不等于0时,该节点在新数组中的位置等于其在旧数组中的位置+旧数组的长度。

7.HashMap中当链表长度大于等于8时,会将链表转化为红黑树,为什么是8?

如果 hashCode 分布良好,也就是 hash 计算的结果离散好的话,那么红黑树这种形式是很少会被用到的,因为各个值都均匀分布,很少出现链表很长的情况。在理想情况下,链表长度符合泊松分布,各个长度的命中概率依次递减,当长度为 8 的时候,概率仅为 0.00000006。这是一个小于千万分之一的概率,通常我们的 Map 里面是不会存储这么多的数据的,所以通常情况下,并不会发生从链表红黑树的转换。

通俗点讲就是put进去的key进行计算hashCode时 只要选择计算hash值的算法足够好(hash碰撞率极低),从而遵循泊松分布,使得桶中挂载的bin的数量等于8的概率非常小,从而转换为红黑树的概率也小,反之则概率大。

8.HashMap为什么线程不安全?

1.在JDK1.7中,当并发执行扩容操作时会造成死循环和数据丢失的情况。

在JDK1.7中,在多线程情况下同时对数组进行扩容,需要将原来数据转移到新数组中,在转移元素的过程中使用的是头插法,会造成死循环。

2.在JDK1.8中,在并发执行put操作时会发生数据覆盖的情况。

如果线程A和线程B同时进行put操作,刚好这两条不同的数据hash值一样,并且该位置数据为null,所以这线程A、B都会通过判断,将执行插入操作。

假设一种情况,线程A进入后还未进行数据插入时挂起,而线程B正常执行,从而正常插入数据,然后线程A获取CPU时间片,此时线程A不用再进行hash判断了,问题出现:线程A会把线程B插入的数据给覆盖,发生线程不安全。

9.为什么HashMapJDK1.7中扩容时要采用头插法,JDK1.8又改为尾插法?

JDK1.7的HashMap在实现resize()时,新table[ ]的列表队头插入。

这样做的目的是:避免尾部遍历。

避免尾部遍历是为了避免在新列表插入数据时,遍历到队尾的位置。因为,直接插入的效率更高。

对resize()的设计来说,本来就是要创建一个新的table,列表的顺序不是很重要。但如果要确保插入队尾,还得遍历出链表的队尾位置,然后插入,是一种多余的损耗。

直接采用队头插入,会使得链表数据倒序。

JDK1.8采用尾插法是避免在多线程环境下扩容时采用头插法出现死循环的问题。

10.HashMap是如何解决哈希冲突的?

拉链法(链地址法)

为了解决碰撞,数组中的元素是单向链表类型。当链表长度大于等于8时,会将链表转换成红黑树提高性能。

而当链表长度小于等于6时,又会将红黑树转换回单向链表提高性能。

11.HashMap为什么使用红黑树而不是B树或平衡二叉树AVL或二叉查找树?

1.不使用二叉查找树

二叉排序树在极端情况下会出现线性结构。例如:二叉排序树左子树所有节点的值均小于根节点,如果我们添加的元素都比根节点小,会导致左子树线性增长,这样就失去了用树型结构替换链表的初衷,导致查询时间增长。所以这是不用二叉查找树的原因。

2.不使用平衡二叉树

平衡二叉树是严格的平衡树,红黑树是不严格平衡的树,平衡二叉树在插入或删除后维持平衡的开销要大于红黑树

红黑树的虽然查询性能略低于平衡二叉树,但在插入和删除上性能要优于平衡二叉树

选择红黑树是从功能、性能和开销上综合选择的结果。

3.不使用B树/B+树

HashMap本来是数组+链表的形式,链表由于其查找慢的特点,所以需要被查找效率更高的树结构来替换。

如果用B/B+树的话,在数据量不是很多的情况下,数据都会“挤在”一个结点里面,这个时候遍历效率就退化成了链表

12.HashMap和Hashtable的异同?

①HashMap是⾮线程安全的,Hashtable是线程安全的。

Hashtable 内部的⽅法基本都经过 synchronized 修饰。

②因为线程安全的问题,HashMap要⽐Hashtable效率⾼⼀点。

③HashMap允许键和值是null,而Hashtable不允许键或值是null。

HashMap中,null 可以作为键,这样的键只有⼀个,可以有⼀个或多个键所对应的值为 null。

HashTable 中 put 进的键值只要有⼀个 null,直接抛出 NullPointerException。

④ Hashtable默认的初始⼤⼩为11,之后每次扩充,容量变为原来的2n+1。

HashMap默认的初始⼤⼩为16,之后每次扩充,容量变为原来的2倍。

⑤创建时如果给定了容量初始值,那么 Hashtable 会直接使⽤你给定的⼤⼩,⽽ HashMap 会将其扩充为2的幂次⽅⼤⼩。

⑥JDK1.8 以后的 HashMap 在解决哈希冲突时当链表⻓度⼤于等于8时,将链表转化为红⿊树,以减少搜索时间。Hashtable没有这样的机制。

Hashtable的底层,是以数组+链表的形式来存储。

⑦HashMap的父类是AbstractMap,Hashtable的父类是Dictionary

相同点:都实现了Map接口,都存储k-v键值对。

13.HashMap和HashSet的区别?

HashSet 底层就是基于 HashMap 实现的。(HashSet 的源码⾮常⾮常少,因为除了 clone() 、 writeObject() 、 readObject() 是 HashSet ⾃⼰不得不实现之外,其他⽅法都是直接调⽤ HashMap 中的⽅法)

1.HashMap实现了Map接口,HashSet实现了Set接口

2.HashMap存储键值对,HashSet存储对象

3.HashMap调用put()向map中添加元素,HashSet调用add()方法向Set中添加元素。

4.HashMap使用键key计算hashCode的值,HashSet使用对象来计算hashCode的值,在hashCode相等的情况下,使用equals()方法来判断对象的相等性。

5.HashSet中的元素由HashMap的key来保存,而HashMap的value则保存了一个静态的Object对象。

14.HashSet和TreeSet的区别?

相同点:HashSet和TreeSet的元素都是不能重复的,并且它们都是线程不安全的。

不同点:

①HashSet中的元素可以为null,但TreeSet中的元素不能为null

②HashSet不能保证元素的排列顺序,TreeSet支持自然排序、定制排序两种排序方式

③HashSet底层是采用哈希表实现的,TreeSet底层是采用红黑树实现的。

④HashSet的add,remove,contains方法的时间复杂度是 O(1),TreeSet的add,remove,contains方法的时间复杂度是 O(logn)

HashSet底层是基于HashMap实现的,存入HashSet中的元素实际上由HashMap的key来保存,而HashMap的value则存储了一个静态的Object对象。

value中的值都是统一的一个private static final Object PRESENT = new Object();

15.HashMap的遍历方式?

①通过map.keySet()获取key,根据key获取到value

for(String key:map.keySet())
    System.out.println("key : "+key+" value : "+map.get(key));

②通过map.keySet()遍历key,通过map.values()遍历value

//第二种只遍历key或者value
for(String key:map.keySet())    //遍历map的key
    System.out.println("键key :"+key);


for(String value:map.values())  //遍历map的值
    System.out.println("值value :"+value);

③通过Map.Entry(String,String) 获取,然后使用entry.getKey()获取到键,通过entry.getValue()获取到值

for(Map.Entry<String, String> entry : map.entrySet())
    System.out.println("键 key :"+entry.getKey()+" 值value :"+entry.getValue());

④通过Iterator

Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();

while(it.hasNext())
    Map.Entry<String, String> entry = it.next();
    System.out.println("键key :"+entry.getKey()+" value :"+entry.getValue());

希望对你有所帮助!

如果看完的小伙伴有兴趣了解更多的话,欢迎添加vx小助手:SOSOXWV  免费领取资料哦!

以上是关于银行前端面试高频基础问题——varlet和const到底有哪些区别?讲不清楚当场发感谢信!?的主要内容,如果未能解决你的问题,请参考以下文章

Java后端面试高频问题:HashMap的底层原理

2019秋招:460道Java后端面试高频题答案版模块四:Java虚拟机

大学生前端面试自我介绍

python后端面试第一部分:python基础--长期维护

2019年17道高频React面试题及详解

第12章 课程总结