JavaScript 常见面试题
JS 的基本数据类型
undefined
null
Boolean
Number
String
Symbol // ES6新增
2
3
4
5
6
null
与 undefined
的区别
null
表示"没有对象",即该处不应该有值。
- 作为函数的参数,表示该函数的参数不是对象。
- 作为对象原型链的终点。
undefined
表示"缺少值",就是此处应该有一个值,但是还没有定义。
- 变量被声明了,但没有赋值时,就等于
undefined
。 - 调用函数时,应该提供的参数没有提供,该参数等于
undefined
。 - 对象没有赋值的属性,该属性的值为
undefined
。 - 函数没有返回值时,默认返回
undefined
。
判断 JavaScript 数据类型的方式
typeof
对于原始数据类型,我们可以使用 typeof()函数来判断他的数据类型:
typeof 1 //number
typeof '1' //string
typeof true //boolean
typeof undefined //undefined
typeof null //object null被认为是一个object的占位符
2
3
4
5
instaceof
对于引用类型我们使用 instanceof 来进行类型判断。
var obj = {}
obj instanceof Object //true
var arr = []
arr instanceof Array //true
var now = new Date()
now instanceof Date //true
var func = function() {}
func instanceof Function //true
var str = 'string'
str instanceof String //false
2
3
4
5
6
7
8
9
10
11
12
13
14
Object.prototype.toString.call()
var num1 = 1
var num2 = new Number(1)
Object.prototype.toString.call(num1) == '[object Number]' //true
Object.prototype.toString.call(num2) == '[object Number]' //true
var arr = []
Object.prototype.toString.call(arr) == '[object Array]' //true
var func = function() {}
Object.prototype.toString.call(func) == '[object Function]' //true
function A() {}
var a = new A()
Object.prototype.toString.call(a) == '[object Object]' //true
2
3
4
5
6
7
8
9
10
11
12
13
14
constructor
在 W3C 定义中的定义:constructor 属性返回对创建此对象的数组函数的引用
console.log([].constructor == Array)
console.log({}.constructor == Object)
console.log('string'.constructor == String)
console.log((123).constructor == Number)
console.log(true.constructor == Boolean)
2
3
4
5
如何判断数组的类型
数组操作
简单列举了数组操作函数及其用处
map
: 遍历数组,返回回调返回值组成的新数组forEach
: 无法 break,可以用try/catch
中throw new Error
来停止filter
: 过滤some
: 有一项返回 true,则整体为 trueevery
: 有一项返回 false,则整体为 falsejoin
: 通过指定连接符生成字符串push
/pop
: 末尾推入和弹出,改变原数组, 返回推入/弹出项【有误】TODO:fixunshift
/shift
: 头部推入和弹出,改变原数组,返回操作项【有误】TODO:fixsort(fn)
/reverse
: 排序与反转,改变原数组concat
: 连接数组,不影响原数组, 浅拷贝slice(start, end)
: 返回截断后的新数组,不改变原数组splice(start, number, value...)
: 返回删除元素组成的数组,value 为插入项,改变原数组indexOf
/lastIndexOf(value, fromIndex)
: 查找数组项,返回对应的下标reduce
/reduceRight(fn(prev, cur), defaultPrev)
: 两两执行,prev 为上次化简函数的 return 值,cur 为当前值(从第二项开始)
TODO:没写完ES6 数组新增内容
数组中的 forEach 和 map 的区别
这两种都是数组遍历的常用方法,两者有相同也有不同之处。
相同点: 都是循环遍历数组中的每一项
forEach 和 map 方法里每次执行匿名函数都支持 3 个参数,参数分别是 item(当前项),index(索引值),arr(原数组)。匿名函数中的 this 都是指向 window,只能遍历数组,都不会改变原数组。
不同点:
- map _ map 方法返回一个新的数组,数组种的元素为原始数组调用函数处理后的值。 _ map 方法不会对空数组进行检测,map 方法不会改变原始数组。 * 浏览器支持:chrome、Safari1.5+、opera、IE9+
- forEach _ forEach 返回 undefined _ forEach 方法用来调用数组的每个元素,将元素传给回调函数。 * forEach 对于空数组是不会调用回调函数的。
JS 有哪些内置对象
Object 是 JavaScript 中所有对象的父对象
数据封装对象:Object、Array、Boolean、Number 和 String
其他对象:Function、Arguments、Math、Date、RegExp、Error
JS 创建对象的方法
- new 操作符 + Object 创建对象
- 字面量创建对象
- 工厂模式
- 构造函数模式
- 原型模式
- 混合模式
- 动态原型模式
- 寄生构造函数模式
- 稳妥构造函数模式
可以参考js 创建对象的方法
get 请求传参长度的误区
误区:我们经常说 get 请求参数的大小存在限制,而 post 请求的参数大小是无限制的 实际上 HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对 get 请求参数的限制是来源与浏览器或 web 服务器,浏览器或 web 服务器限制了 url 的长度。为了明确这个概念,我们必须再次强调下面几点:
- HTTP 协议 未规定 GET 和 POST 的长度限制
- GET 的最大长度显示是因为 浏览器和 web 服务器限制了 URI 的长度
- 不同的浏览器和 WEB 服务器,限制的最大长度不一样
- 要支持 IE,则最大长度为 2083byte,若只支持 Chrome,则最大长度 8182byte
get 和 post 请求在缓存方面的区别
- get 请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。
- post 不同,post 做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此 get 请求适合于请求缓存。
闭包
闭包就是能够读取其他函数内部变量的函数
闭包的特征
- 函数内再嵌套函数
- 内部函数可以引用外层的参数和变量
- 参数和变量不会被垃圾回收制回收
对闭包的理解
使用闭包主要是为了设计私有的方法和变量。
闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。在 js 中,函数即闭包,只有函数才会产生作用域的概念。
闭包的用处,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,最后是封装对象的私有属性和私有方法。
闭包的好处
能够实现封装和缓存等
闭包的坏处
就是消耗内存、不正当使用会造成内存溢出的问题
闭包的注意点
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在 IE 中可能导致内存泄露。
解决方法是:在退出函数之前,将不使用的局部变量全部删除。
闭包的经典问题
for(var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
// 结果:3 3 3
2
3
4
5
6
有什么办法依次输出 0 1 2
使用 let
for(let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
2
3
4
5
在这里,每个 let 和代码块结合起来形成块级作用域,当 setTimeout() 打印时,会寻找最近的块级作用域中的 i,所以依次打印出 0 1 2
使用立即执行函数解决闭包的问题
for(let i = 0; i < 3; i++) {
(function(i){
setTimeout(function() {
console.log(i);
}, 1000);
})(i)
}
2
3
4
5
6
7
JS 作用域及作用域链
作用域链
一般情况下,变量取值到 创建 这个变量 的函数的作用域中取值。
但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。
原型和原型链
概念
每个对象都会在其内部初始化一个属性,就是 prototype(原型),当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去 prototype 里找这个属性,这个 prototype 又会有自己的 prototype,于是就这样一直找下去。
原型和原型链的关系
instance.constructor.prototype = instance.__proto__
原型和原型链的特点
JavaScript 对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。
当我们需要一个属性的时,Javascript 引擎会先看当前对象中是否有这个属性,如果没有的
就会查找他的 Prototype 对象是否有这个属性,如此递推下去,一直检索到 Object 内建对象
组件化和模块化
组件化
- 特点
降低系统各个功能的耦合性,并且提高了功能内部的聚合性。这对前端工程化及降低代码的维护来说,是有很大的好处的,耦合性的降低,提高了系统的伸展性,降低了开发的复杂度,提升开发效率,降低开发成本。 - 原则
专一、可配置性、标准性、复用性、可维护性
模块化
- 好处 - 避免变量污染,命名冲突 - 提高代码复用率 - 提高了可维护性 - 方便依赖关系管理
- 方法 - IIFE
Object.assign 的理解
第一级属性深拷贝,以后级别属性浅拷贝
Object.assign(target, ...sources)
参数:
target: 目标对象
sources: 源对象
只会拷贝源对象自身的并且可枚举的属性到目标对象。方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
Object.create()的理解
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。
对 This 对象的理解
this 总是指向函数的直接调用者(而非间接调用者)
如果有 new 关键字,this 指向 new 出来的那个对象
在事件中,this 指向触发这个事件的对象,特殊的是,IE 中的 attachEvent 中的 this 总是指向全局对象 Window
解决异步回调地狱
promise、generator、async/await