前端面试基础知识题
1. 使用js实现二分查找
二分查找,也称为折半查找,是指在有序的数组里找出指定的值,返回该值在数组中的索引。
查找步骤如下:
1、从有序数组的最中间元素开始查找,如果该元素正好是指定查找的值,则查找过程结束。否则进行下一步; 2、如果指定要查找的元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半区域查找,然后重复第一步的操作; 3、重复以上过程,直到找到目标元素的索引,查找成功;或者直到子数组为空,查找失败。
优点是比较次数少,查找速度快,平均性能好; 其缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而查找频繁的有序列表。
代码语言:javascript复制//arr:数组;key:查找的元素;start:开始索引;end:结束索引
function search2(arr,key,start,end){
//首先判断当前起始索引是否大于结束索引,如果大于说明没有找到元素返回-1
if(start > end) {
return -1;
}
//如果手动调用不写start和end参数会当做第一次运行默认值
//三元表达式:如果不写end参数则为undefined说明第一次调用所以结束索引为arr.length-1
//如果是递归调用则使用传进来的参数end值
var end= end===undefined ? arr.length-1 : end;
//如果 || 前面的为真则赋值start,如果为假则赋值后面的0
//所以end变量没有写var end = end || arr.length-1;这样如果递归调用时候传参end为0时会被转化为false,导致赋值给arr.length-1造成无限循环溢出;
var start=start || 0;
//取中间的索引
var mid=parseInt((start end)/2);
if(key==arr[mid]){
//如果找到则直接返回
return mid;
}else if(key<arr[mid]){
//如果key小于则递归调用自身,将结束索引设置为中间索引-1
return search2(arr,key,start,mid-1);
}else{
//如果key大于则递归调用自身,将起始索引设置为中间索引 1
return search2(arr,key,mid 1,end);
}
}
var arr = [0,13,21,35,46,52,68,77,89,94];
search2(arr, 77); //7
search2(arr, 99); //-1
2. ES6有哪些新特性?
新特性主要归为四大类:
- 解决原有语法上的一些不足
比如let 和 const 的块级作用域
- 对原有语法进行增强
比如解构、展开、参数默认值、模板字符串
- 全新的对象、全新的方法、全新的功能
比如promise、proxy、object的assign、is
- 全新的数据类型和数据结构
比如symbol、set、map
3. Js中数组是如何在内存中存储的?
数组不是以一组连续的区域存储在内存中,而是一种哈希映射的形式。它可以通过多种数据结构来实现,其中一种是链表。
4. 移动端的点击事件的有延迟,时间是多久,为什么会有? 怎么解决这个延时?
移动端点击有 300ms 的延迟是因为移动端会有双击缩放的这个操作,因此浏览器在 click 之后要等待 300ms,看用户有没有下一次点击,来判断这次操作是不是双击。有三种办法来解决这个问题:
通过 meta 标签禁用网页的缩放。
通过 meta 标签将网页的 viewport 设置为 ideal viewport。
调用一些 js 库,比如 FastClick。
click 延时问题还可能引起点击穿透的问题,就是如果我们在一个元素上注册了 touchStart 的监听事件,这个事件会将这个元素隐藏掉,我们发现当这个元素隐藏后,触发了这个元素下的一个元素的点击事件,这就是点击穿透。
5. For…in和for…of有什么区别?
for…of 是ES6新增的遍历方式,允许遍历一个含有iterator接口的数据结构(数组、对象等)并且返回各项的值,和ES3中的for…in的区别如下:
for…of 遍历获取的是对象的键值,for…in 获取的是对象的键名;
for… in 会遍历对象的整个原型链,性能非常差不推荐使用,而 for … of 只遍历当前对象不会遍历原型链;
对于数组的遍历,for…in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of 只返回数组的下标对应的属性值;
总结: for…in 循环主要是为了遍历对象而生,不适用于遍历数组;for…of 循环可以用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象。
6. 如果new一个箭头函数会怎么样?
箭头函数是ES6中的提出来的,它没有prototype,也没有自己的this指向,更不可以使用arguments参数,所以不能 New 一个箭头函数。
new操作符的实现步骤如下: 1、创建一个空的简单JavaScript对象(即{}); 2、为步骤1新创建的对象添加属性__proto__,将该属性链接至构造函数的原型对象 ; 3、将步骤1新创建的对象作为this的上下文 ; 4、如果该函数没有返回对象,则返回this。 所以,上面的第二、三步,箭头函数都是没有办法执行的。
7. 数据类型检测的方式有哪些?
(1)typeof:其中数组、对象、null都会被判断为object,其他判断都正确。
(2)instanceof:instanceof可以正确判断对象的类型,其内部运行机制是判断在其原型链中能否找到该类型的原型。可以看到,instanceof只能正确判断引用数据类型,而不能判断基本数据类型。instanceof 运算符可以用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
(3)constructor:constructor有两个作用,一是判断数据的类型,二是对象实例通过 constrcutor 对象访问它的构造函数。需要注意,如果创建一个对象来改变它的原型,constructor就不能用来判断数据类型了。
(4)Object.prototype.toString.call():Object.prototype.toString.call() 使用 Object 对象的原型方法 toString 来判断数据类型:同样是检测对象obj调用toString方法,obj.toString()的结果和Object.prototype.toString.call(obj)的结果不一样,这是为什么?
这是因为toString是Object的原型方法,而Array、function等类型作为Object的实例,都重写了toString方法。不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串…),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object原型上的toString方法。
8. Object.is() 与比较操作符 “=”、“” 的区别?
使用双等号(==)进行相等判断时,如果两边的类型不一致,则会进行强制类型转化后再进行比较。
使用三等号(===)进行相等判断时,如果两边的类型不一致时,不会做强制类型准换,直接返回 false。
使用 Object.is 来进行相等判断时,一般情况下和三等号的判断相同,它处理了一些特殊的情况,比如 -0 和 0 不再相等,两个 NaN 是相等的。
9. isNaN 和 Number.isNaN 函数有什么区别?
NaN:全局属性 NaN 的值表示不是一个数字(Not-A-Number)。 在 JavaScript 中,NaN 最特殊的地方就是,我们不能使用相等运算符(== (en-US) 和 === (en-US))来判断一个值是否是 NaN,因为 NaN == NaN 和 NaN === NaN 都会返回 false。因此,必须要有一个判断值是否是 NaN 的方法。
方法简介:函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。 函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。
总结:和全局函数 isNaN() 相比,Number.isNaN() 不会自行将参数转换成数字,只有在参数是值为 NaN 的数字时,才会返回 true。 Number.isNaN() 方法确定传递的值是否为NaN,并且检查其类型是否为Number。它是原来的全局isNaN() 的更稳妥的版本。
10. 谈谈你对浏览器中进程和线程的理解
浏览器是多进程的
它主要包括以下进程:
- Browser 进程:浏览器的主进程,唯一,负责创建和销毁其它进程、网络资源的下载与管理、浏览器界面的展示、前进后退等。
- GPU 进程:用于 3D 绘制等,最多一个。
- 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建。
- 浏览器渲染进程(浏览器内核):内部是多线程的,每打开一个新网页就会创建一个进程,主要用于页面渲染,脚本执行,事件处理等。
渲染进程(浏览器内核)
浏览器的渲染进程是多线程的,页面的渲染,JavaScript 的执行,事件的循环,都在这个进程内进行:
- GUI 渲染线程:负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。
- JavaScript 引擎线程:也称为 JavaScript 内核,负责处理 Javascript 脚本程序、解析 Javascript 脚本、运行代码等。(例如 V8 引擎)
- 事件触发线程:用来控制浏览器事件循环,注意这不归 JavaScript 引擎线程管,当事件被触发时,该线程会把事件添加到待处理队列的队尾,等待 JavaScript 引擎的处理。
- 定时触发器线程:传说中的 setInterval 与 setTimeout 所在线程,注意,W3C 在 HTML 标准中规定,规定要求 setTimeout 中低于 4ms 的时间间隔算为 4ms 。
- 异步 http 请求线程:在 XMLHttpRequest 连接后通过浏览器新开一个线程请求,将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由 JavaScript 引擎执行。
注意,GUI 渲染线程与 JavaScript 引擎线程是互斥的,当 JavaScript 引擎执行时 GUI 线程会被挂起(相当于被冻结了),GUI 更新会被保存在一个队列中等到 JavaScript 引擎空闲时立即被执行。所以如果 JavaScript 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
单线程的 JavaScript
所谓单线程,是指在 JavaScript 引擎中负责解释和执行 JavaScript 代码的线程唯一,同一时间上只能执行一件任务。