前言
本文将总结JS的相关知识以及面试常考知识点,不会细致的讲解,遇到重要的知识点我会推荐给大家几篇自认为不错的文章,方便大家更好的理解。
问题1:JavaScript的数据类型有哪些?
基本数据类型:Number、String、boolean、null、undefined
引用数据类型:Object、Array、Function
衍生问题1:怎么判断一个数据是什么类型?
可以判断数据类型的方式有三种:typeof、instanceof、===,其中typeof不能区分Array与object,null与object,instanceof可以判断对象的具体类型,=== 仅可以判断null与undefined,因为他们的值是唯一的。
衍生问题2:为什么typeof判断null的结果是object?
第一,因为null表示一个空对象指针,它代表的其实就是一个空对象,所以使用typeof操作符检测时返回”object”也是可以理解的。第二,因为JavaScript中的值是由一个表示类型的标签和实际的数值表示的,对象的标签值为0,由于null代表空指针,绝大多数的平台表示为0x00,因为,null的类型标签为0,typeof null因此返回object。ES6时,曾有人提出将typeof null修改为null,但是被拒绝了。
衍生问题3:null与undefined的区别是什么?
当我们声明了一个值,没有初始化时,他的值为undefined。undefined 其实是一个全局变量,window.undefined的值为undefined。
当初始化时赋值为null,表示将要赋值为一个对象。结束后,赋值为null,表示该对象成为垃圾对象,被垃圾回收器回收。
具体区别可以参考这篇文章:深入理解undefined与null
问题2:什么是作用域与作用域链
作用域:作用域指的是变量的适用范围,即在程序的执行上下文中变量的可访问性,作用域相对于执行上下文是静态的,在编写代码时就确定了。作用就是隔离变量,不同作用域的同名变量不会有冲突。
作用域链:作用域链决定了哪些数据能被函数访问。当一个函数创建后,它的作用域链会被创建此函数的作用域中可访问的数据对象填充。
给大家推荐几篇文章:
问题3:什么是执行上下文?
执行上下文是评估和执行JS代码的环境的抽象概念,JS代码执行的时候都是在执行上下文中运行。JS中执行上下文分为三种:全局执行上下文、函数执行上下文、eval函数执行上下文。执行上下文在创建时会有三个阶段:绑定this、创建词法环境、创建变量环境。
推荐大家看这篇文章更详细的了解执行上下文:理解 JavaScript 中的执行上下文和执行栈
问题4:为什么会有变量提升?
JS引擎在代码执行前会有一个解析过程,创建了执行上下文,初始化了一些代码执行时需要用到的对象。当我们访问一个变量时,我们会到当前执行上下文中的作用域链去查找,而作用域链的首段指向的是当前执行上下文的变量对象,这个变量对象就是执行上下文的一个属性,它包含了函数的形参、所有的函数和变量声明,这个对象是在代码解析的时候创建的,这就是会出现变量提升的根本原因。
问题5:说一说你知道的this
本质上任何函数都是通过对象来调用的,默认就是window,所有函数内部都有 一个变量this,this的值就是调用当前函数的对象。改变this指向的方法有:直接函数调用,默认指向window、对象方法调用,this指向这个对象、函数通过new调用,this指向新创建的对象、call/apply/bind
衍生问题:call、apply、bind的区别
- call方法的第一个参数是要绑定给thi的值,后面传入的是一个参数列表。当第一个参数为null、undefined时。默认指向window
- apply接收两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组。同call一样,当第一个参数为null、undefined时,默认指向window
- bind和call很相似,第一个参数是this的指向,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数以及bind接收的参数列表的使用,bind的返回值是一个函数。
问题6:什么是原型与原型链?
- 任何实例对象都有一个__proto__属性,即隐式原型;任何函数都有一个prototype属性,默认指向一个空的Object实例对象,即显示原型,这个实例对象也有__proto__属性,这样就构成了原型链。
- 实例对象的隐式原型等于其构造函数的显示原型。
- 访问一个对象的属性时,先在自身的属性中查找,找到返回,如果没有,再沿着__proto__属性向上查找,找到返回,找不到返回undefined。
- 任何函数都是Function的实例。
- Object的原型对象是原型链的尽头
为了更好的理解原型与原型链,可以参考下图:
推荐大家阅读这篇文章:深入理解JavaScript原型
问题7:什么是闭包?
- 当一个嵌套的内部函数引用了外部函数的变量时,就产生了闭包,闭包存在于内部函数中。
- 闭包产生的条件:外部函数被调用,内部函数引用了外部函数的数据,执行内部函数的定义就会产生闭包;
- 闭包的作用:使函数内部的变量在函数执行完之后,仍然存活在内存中(延长了局部变量的声明周期)、让函数外部可以访问到函数内部的数据、模块化
- 闭包在内部函数定义执行时就产生了,在内部函数成为垃圾对象时死亡。
- 闭包的缺点:函数执行完之后,函数内的局部变量没有释放,占用内存的时间会变长,容易造成内存泄露。
大家可以做一下这道题,看看是否掌握了闭包
function fun(n, o) {
console.log(o)
return {
fun: function(m) {
return fun(m, n)
}
}
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1); c.fun(2); c.fun(3);
推荐大家阅读几篇文章,可以更好的理解闭包:
我从来不理解JavaScript闭包,直到有人这样向我解释它...
问题8:什么是防抖和节流?
防抖:当持续触发事件时,一定时间内没有在触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又触发了事件,就重新开始延时。
节流:当持续触发事件时,保证一定时间段内只调用一次时间处理函数。
// 防抖
function debounce(fn, wait) {
var timeout = null;
return function() {
if(timeout !== null)
clearTimeout(timeout);
timeout = setTimeout(fn, wait);
}
}
// 节流
var throttle = function(func, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if (!timer) {
timer = setTimeout(function() {
func.apply(context, args);
timer = null;
}, delay);
}
}
}
衍生问题1:防抖和节流的区别
函数节流不管事件触发的多频繁,都会保证在规定的时间内一定会执行一次事件处理函数,而函数防抖只是在最后一次事件后才触发一次。比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。
问题9:普通函数和箭头函数的区别
- 箭头函数没有this、argument的绑定
- 不能使用new操作符,没有construct属性
- this的指向不能修改
- 不支持arguments对象
- 没有原型
问题10:数组的方法有哪些?
不修改原数组:
- concat:连续两个或多个数组,返回新的拼接后的新数组
- join:把数组转成字符串,按规定字符连接
- toString:将数组转为字符串
- slice:返回数组中指定区间的元素,包左不包右
- indexOf:返回选项在数组中第一次出现的位置
- lastIndexOf:返回选项在数组中最后一次出现的索引
- reduce:遍历迭代,返回计算后的值
- some:判断数组中是否有一项满足条件,满足返回true
- every:判断数组中的每一项是否满足
修改原数组
- push:向数组的末尾添加一个元素,返回数组新的长度
- shift:删除数组中的第一个元素,并返回删除的值
- unshift:向数组的开头添加一个元素,返回数组新的长度
- pop:删除并返回数组的最后一个元素
- splice:删除或添加数组
- reverse:反转数组
- sort:数组排序
问题11:JS的事件循环模型
JS是单线程的,JS引擎先执行初始化代码,将事件回调函数交给对应的模块管理,当事件发生时,模块管理会将回调代码及其数据放到回调队列,只有当初始化代码全部完成之后,才会遍历读取回调队列中的回调函数执行。任务队列分为宏任务队列和微任务队列,进入整体代码之后,开始第一次循环,接着执行所有的微任务,然后再次从宏任务开始,找到其中第一个宏任务执行完毕,在执行所有的微任务。
阅读这篇文章可以深刻的理解事件循环模型:这一次,彻底弄懂 JavaScript 执行机制
问题12:in和hasOwnProperty的区别
in操作符:检查指定对象原型链上是否有对应的属性值
hasOwnProperty:检测指定对象自身上是否有对应的属性值
二者的区别在于in会查找原型链,而hasOwnProperty不会。
问题13:详细说一说Promise对象
- 什么是promise:promise是一个对象,从它可以获取异步操作的信息,promise提供统一的API,各种异步操作都可以用相同的方式进行处理。
- promise有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败),当promise对象的状态改变之后,就不会再改变,任何使用都可以得到这个结果。
- promise构造函数接收一个函数作为参数,这个函数两个参数分别为:resolve、reject。resolve函数的作用是将promise对象的状态从pending变为fulfilled,在异步操作成功之后调用,并将异步结果作为参数传递出去。reject函数与resolve函数作用相似,只是将promise状态从pending变为rejected。
- 可以调用promise实例的then方法,来指定resolve和rejected的回调函数
- all函数:谁跑的慢,以谁为准执行回调。all接收一个数组参数,里面的值最终都算返回promise对象
- race函数:谁跑的快,以谁为执行回调。
我们可以利用race函数,给某个异步操作设置超时时间,并且在超时后执行相应的操作,代码如下:
//请求某个图片资源
function requestImg(){
var p = new Promise((resolve, reject) => {
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = '图片的路径';
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise((resolve, reject) => {
setTimeout(() => {
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise.race([requestImg(), timeout()]).then((data) =>{
console.log(data);
}).catch((err) => {
console.log(err);
});
requestImg
函数会异步请求一张图片,我把地址写为"图片的路径",所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。我们把这两个返回Promise对象的函数放进race,于是他俩就会赛跑,如果5秒之内图片请求成功了,那么遍进入then方法,执行正常的流程。如果5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息。
推荐大家阅读阮一峰老师的文章:Promise 对象 ---- 阮一峰
问题14:函数式编程的特点
- 函数是一等公民:函数与其他数据类型一样,可以作为参数传递
- 惰性执行:只在调用的时候执行,不产生无用的中间变量
- 无状态和数据不可变
- 没有副作用
- 纯函数
问题15:JavaScript代码中的use strict 是什么意思?
use strict是一种ES5添加的严格运行模式,这种模式使得JavaScript在更严格的模式下运行。
设立严格模式的目的,主要有以下几个:
- 消除JavaScript语法的一些不合理,不严谨之处,减少一些怪异行为。
- 消除代码运行的一些不安全之处,保证代码的运行安全
- 提高编译器效率,增加运行速度
- 为未来新版本的JavaScript做铺垫
最后
以上只是部分JavaScript相关问题,后序还会继续总结,大家加油!