偶然在掘金看到一个关于面试经验,知识总结的,概况挺全面的。本着学一物写心得的体会,就把所知写下来,文中有部分是原文的直接引用。不足之处,还望提出。
面经地址,想看原文请移步 InterviewMap
js
内置类型
基本类型: string undefined boolean number null symbol(ES6提出,基本很少用,目前)
对象:object 总的来说,js中数据类型有七种,即基本类型和对象。
Typeof
对于基本类型,typeof 都可以正常显示出来,除了null显示的是object。为什么null 会显示object,这是一个历史悠久的问题,就不叙述了。
对于对象,除了函数之外,都会显示object,函数显示function。
如果想获得数据类型的准确类型,可以使用Object.prototype.toString.call(xx)。
![](https://img.haomeiwen.com/i13610935/19b1b0af82116062.png)
类型转换
条件判断时,除了 undefined null false NaN '' 0 -0 外,其他值都会返回 true。包括所有对象。
四则运算符
加法运算,只有在运算时,有一方为字符串,会自动转化为字符串进行拼接。其他的都会转化为数字,如-,*,/,%。
==操作符
==操作符,值相等运算符。与 === 类型值相等运算符不一致。使用 == 可能会带来一些坑,所以,在写的时候,尽量不用==,使用 === 。== 会带来意想不到的问题,如下图,
![](https://img.haomeiwen.com/i13610935/517a2c2b6fe4ed62.png)
原型
![](https://img.haomeiwen.com/i13610935/65720b46b6194ab3.png)
什么是原型?什么是原型链?
原型(prototype),是指每个函数的原型,基本所有函数都会有prototype属性。而prototype属性,本质上是个对象,而且这个对象只有一个 constructor 属性,属性值为函数本身。即 fn.prototype.constructor === fn // true。如果我们改变了函数的原型,那么这个constructor属性就不会存在了,但是可以通过原型链来找到constructor.
原型链?先不急着解释。我们先来了解一下 __proto__ 属性,每个对象都会有这个属性,这个属性指向了生成该对象的构造函数的原型。如下:
function Fn(){}
var fn = new Fn();
fn.__proto__ === Fn.prototype // true
你可以这么理解,原型链,其实就是把实例化对象和构造函数的原型连接在一起,这就是原型链。
注意看,上面使用到了一个 new 操作符,在 new 操作时会发生什么?不急着说,我们补充个小知识。 function 其实是 new Function() 的操作,一个语法糖。现在我们关注一下new操作时会发生什么。
1.首先生成一个新对象。
2.给这个新对象一个属性,__proto__,并将这个属性链接到构造函数的原型上。
3.绑定 this
4.返回这个对象
手动实现一个 new 函数
如果你想更进一步的了解原型,可以仔细阅读 深度解析原型中的各个难点 · Issue #2 · KieSun/Blog · GitHub
new 操作符
new 的过程如下
生成一个 实例化对象 > 对象的原型链接到 值的原型 > 绑定 this指向新生成的对象 > 返回新对象
经典面试题
function Foo() {
getName = function () { alert (1); };
return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}
//请写出以下输出结果:
Foo.getName(); // 2
getName(); // 4
Foo().getName(); //1
getName(); //1
new Foo.getName(); //2
new Foo().getName(); // 3
new new Foo().getName(); //3
原文如下 一道常被人轻视的前端JS面试题 - 小小沧海 - 博客园
运算符优先级 请移步 运算符优先级 - JavaScript | MDN
![](https://img.haomeiwen.com/i13610935/8574d6c331fff22e.png)
this
this指向四个法则
1.通过new 操作符出来的,指向 实例化对象。
2.全局调用,指向window。
3.指向调用者 ,例如 foo.get() this 指向的就是 foo。
4.继承上下文,尤其是箭头函数中。
![](https://img.haomeiwen.com/i13610935/f88f24a61e5307c3.png)
闭包
函数A内返回一个函数B,在函数B中使用函数A的变量,函数B就被称为闭包。
![](https://img.haomeiwen.com/i13610935/65328fb2d0f5bdba.png)
![](https://img.haomeiwen.com/i13610935/8b08a17da219f320.png)
![](https://img.haomeiwen.com/i13610935/aa2f557eeb7098b8.png)
深浅拷贝
涉及到深浅拷贝,那就是关于对象了。由于在js中,基本类型和引用类型不一样,而对象是属于引用类型,基本类型是值,而对象是引用地址。
![](https://img.haomeiwen.com/i13610935/678fccb405ff1be8.png)
浅拷贝
通过 Object.assign来解决这个问题。
![](https://img.haomeiwen.com/i13610935/f3ec000462478ecb.png)
深拷贝
当遇到value值为对象时,浅拷贝就不能满足了。就需要用到深拷贝了。
![](https://img.haomeiwen.com/i13610935/e6457161acc39bf8.png)
简单是通过 JSON.parse(JSON.stringify(a))来实现,这样已经可以满足大多数情况。
![](https://img.haomeiwen.com/i13610935/3f29cbc8c29fb9d3.png)
但是,如果value为undefined或者函数时,就不能使用。
![](https://img.haomeiwen.com/i13610935/857820ae14faf6ac.png)
远古方法是用jquery的extend方法,jQuery.extend([deep], target, object1, [objectN]);
不过我们可以用下 lodash的方法--cloneDeep cloneDeep
![](https://img.haomeiwen.com/i13610935/fe9f103162790550.png)
模块化
es6模块化,目前Chrome浏览器已经支持,但是需要在 script 标签中添加type=module,在目前的项目中,大部分不会这么做,都是使用babel进行转化。
![](https://img.haomeiwen.com/i13610935/52150e031de8aed1.png)
CommonJS
nodejs独有,在浏览器中需要转化。已经做过介绍,这里就不介绍了。
VUE模块化
每一个vue文件就是一个单独的模块,方法和属性只属于这个模块。也是应用了es6的模块化,对外输出。
防抖
你是否在日常开发中遇到一个问题,在滚动事件中需要做个复杂计算或者实现一个按钮的防二次点击操作。
这些需求都可以通过函数防抖动来实现。尤其是第一个需求,如果在频繁的事件回调中做复杂计算,很有可能导致页面卡顿,不如将多次计算合并为一次计算,只在一个精确点做操作。
PS:防抖和节流的作用都是防止函数多次调用。区别在于,假设一个用户一直触发这个函数,且每次触发函数的间隔小于wait,防抖的情况下只会调用一次,而节流的 情况会每隔一定时间(参数wait)调用函数。
我们先来看一个袖珍版的防抖理解一下防抖的实现:
![](https://img.haomeiwen.com/i13610935/c5893e68be230a57.png)
主要的思路就是 事件第一次触发的时候,创建延时器。第二次触发的时候,判断是否已经存在延时器,存在就清除,重新创建一个新的延时器。以此类推,直到最后一次事件触发,才去执行事件中的操作。
这是一个简单版的防抖,但是有缺陷,这个防抖只能在最后调用。一般的防抖会有immediate选项,表示是否立即调用。这两者的区别,举个栗子来说:
例如在搜索引擎搜索问题的时候,我们当然是希望用户输入完最后一个字才调用查询接口,这个时候适用延迟执行的防抖函数,它总是在一连串(间隔小于wait的)函数触发之后调用。
例如用户给interviewMap点star的时候,我们希望用户点第一下的时候就去调用接口,并且成功之后改变star按钮的样子,用户就可以立马得到反馈是否star成功了,这个情况适用立即执行的防抖函数,它总是在第一次调用,并且下一次调用必须与前一次调用的时间间隔大于wait才会触发。
下面我们来实现一个带有立即执行选项的防抖函数
![](https://img.haomeiwen.com/i13610935/2aef11e884e88133.png)
![](https://img.haomeiwen.com/i13610935/078cd0ad9b70d1a5.png)
整体函数实现的不难,总结一下。
对于按钮防点击来说的实现:如果函数是立即执行的,就立即调用,如果函数是延迟执行的,就缓存上下文和参数,放到延迟函数中去执行。一旦我开始一个定时器,只要我定时器还在,你每次点击我都重新计时。一旦你点累了,定时器时间到,定时器重置为 null,就可以再次点击了。
对于延时执行函数来说的实现:清除定时器ID,如果是延迟调用就调用函数
节流
防抖动和节流本质是不一样的。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行
![](https://img.haomeiwen.com/i13610935/5b346b8df9f04886.png)
call,apply,bind区别
共同点,都是改变this指向问题。用法 call(demoObj,xxx,x,xxx)或apply(demoObj,[xxx]),bind(demoObj)。
从上面可以看出,call与apply作用一致,但是传参不一致,call传的是参数列表,而apply是参数数组,且call和apply是立刻调用。
bind是返回一个对应函数,便于稍后调用。这里有一份更加详细的 【优雅代码】深入浅出 妙用Javascript中apply、call、bind - ChokCoco - 博客园
![](https://img.haomeiwen.com/i13610935/0241c04a60df0b6e.png)
Promise
Promise es6新增的语法,解决回调地狱。可以把 Promise 看成一个状态机。初始是 pending 状态,通过函数resolve 和 reject ,将状态转变为 resolved 或者 rejected 状态,状态一旦改变就不能再次变化。
then 函数会返回一个 Promise 实例,并且该返回值是一个新的实例而不是之前的实例。因为 Promise 规范规定除了 pending 状态,其他状态是不可以改变的,如果返回的是一个相同实例的话,多个 then 调用就失去意义了。
![](https://img.haomeiwen.com/i13610935/29c2bbb9eaabf0b9.png)
promise.all 方法,多个promise执行,等所有的promise状态都会 resolved时,才会执行下去。
![](https://img.haomeiwen.com/i13610935/14b68271d2e52b6a.png)
async和await
同样也是解决回调地狱的问题,es7的内容。相比promise的好处就是传参方便。show code
![](https://img.haomeiwen.com/i13610935/705e2e7569a241a0.png)
await 必须在 async 函数中。async函数其实会返回一个promise对象,promise状态为 resolved。
![](https://img.haomeiwen.com/i13610935/4916996512085513.png)
。在上面 demoFn中,如果写有 return xxxx的话,demoFn().then(v=>{console.log(v)})是可以获得内容的。
![](https://img.haomeiwen.com/i13610935/3445c3addbb1e8cf.png)
了解更加仔细的话,请移步 理解 JavaScript 的 async/await - 边城客栈 - SegmentFault 思否
js数据精度问题 为什么 0.1 + 0.2 != 0.3
总体来说,在 js 中计算小数点的,可以先转化在计算。简单解决方法
正则表达式
![](https://img.haomeiwen.com/i13610935/1b7258f6f2828402.png)
![](https://img.haomeiwen.com/i13610935/27f6d0a9f7b132e9.png)
![](https://img.haomeiwen.com/i13610935/a552d58beb6dc253.png)
let var const
var 声明全局,会挂靠在window上,后面可重复 声明,不会有块级作用域。变量提升
let 声明变量,不会挂靠在window上,后面不可重复声明,会形成块级作用域。变量不会提升
const 声明常量,声明时必须要赋值,不会挂靠在window上,后面不可重复声明,会形成块级作用域。变量不会提升
更加详细内容请移步 var、let、const 区别? - 简书
网友评论