前言
本篇是面试系列的JavaScript的基础篇。文章主要围绕着基础类型、做用域、闭包、原型、继承、this的指向、深浅拷贝、字符串和数组的方法、节流、防抖、函数式编程等知识展开的。面试
正文
基础类型
JavaScript的数据类型主要分为两类:原始类型、引用类型ajax
原始类型:6种编程
- null 【JS的数据底层都是用二进制进行存储的,前三位为0会被判断成对象,null是全都为0】
- undefined
- Number【双精度64位二进制格式的值——数字、±Infinity、NaN】
- String
- Boolean
- Symbol【ES6新增定义,实例是惟一且不可变的】
引用类型:windows
在内存中,存储的形式:数组
- 原始类型,会被保存到栈内存中
- 引用类型,会被保存到堆内存中
类型之间的赋值方式:promise
- 原始类型的赋值:对值类型的拷贝,会复制一份值
- 引用类型的赋值:对引用地址的拷贝
基础类型的隐式转换也是面试考察的重点,Symbol类型有兴趣的小伙伴也能够手写实现一下【面试系列——手写代码实现(一)】浏览器
执行上下文 | 做用域 | 闭包
JS中存在的执行上下文类型:缓存
- 全局上下文:windows对象
- 函数上下文:每次调用函数时,建立一个新的上下文
- eval上下文
每一个执行上下文,都存在三个属性:做用域、变量、this闭包
做用域:JS只有全局做用域和函数做用域app
- var建立的变量只有函数做用域
- let和const建立的变量既有函数做用域,也有块级做用域
做用域链:JavaScript引擎在寻找一个变量名的时候,会在当前做用域进行查找,若是没有,就会继续往外层做用域进行查找,直到全局做用域为止,这就造成了一个做用域链。
闭包:引用外部函数变量的内部函数【红宝书的定义】
主要使用场景:
- setTimeout、setInterval、setImmediate之类的定时器、事件回调、ajax请求的回调
- 被外部函数做为函数返回,或者返回对象中引用内部函数的状况
能够使用当即执行函数(IIFE)来实现闭包,代码以下:
(function(i){
console.log(i);
})(i);
复制代码
闭包只是存储外部变量的引用,不会拷贝外部变量的值;闭包引用的变量会被存放到堆内存中。
this | apply | call | bind
this的指向,是在执行上下文被建立的时候,被肯定的。
this的绑定规则总共有下面五种:
- 默认绑定【严格 | 非严格】
- 默认模式下,this指向全局对象
- 严格模式下,this指向undefined
- 隐式绑定:函数调用时,会有一个上下文对象,函数的this会绑定在这个上下文对象上面
- 显式绑定:经过apply/call/bind的方式,来实现this的显式绑定
- new绑定:JS构造函数经过new操做符进行调用,此处的this指向新建立的对象实例
- 箭头函数绑定:引用外层上下文的this
- 箭头函数的this,至关于普通变量
- 寻找箭头函数的this,就至关于寻找外层做用域
- 若是改变了外层做用域的this,就能够改变箭头函数的this
this的指向优先级,依次按照 箭头函数 > new操做符 > 显示绑定 > 隐式绑定 > 默认绑定
apply | call | bind的做用:改变函数中this的指向
apply和call的区别:
- 相同点:
- 第一个参数都是指向函数中的this
- 均可以接受一个参数
- 不一样点:
- apply只能接受两个参数,其中后一个参数能够是数组、类数组、对象
- call能够接受多个参数
apply/call和bind的区别:
- apply和call是函数执行时调用的
- bind是函数定义时调用的
this的指向是面试考察的重点,通常都会以题目的形式进行考察;apply、call、bind是须要手写实现的【面试系列——手写代码实现(一)】
原型和继承
原型(prototype):给其余对象提供共享属性的对象
隐式引用(proto):全部对象,都存在一个隐式引用,指向它的原型
构造函数(Constructor):构造函数,它的原型指向实例的原型
- 构造函数和普通函数的区别:
- 使用new操做符生成实例的函数就是构造函数
- 直接调用的就是普通函数
- Symbol是基础类型,不是构造函数,它经过Symbol()建立实例
原型链:如图所示
图中,__proto__造成的链条组合,就是原型链
继承方式:
- 原型链继承:本质就是重写原型对象,代之以新对象的实例
- 缺点:
- 多个实例对引用类型进行篡改
- 子类型原型上的构造函数被重写了
- 给子类型添加属性和方法须要在替换原型以后
- 建立子类型以后,没法向父类构造函数传参
- 构造函数继承:使用父类构造函数来加强子类实例,等同于复制父类的实例给子类(不使用原型)
- 核心:superType.call(this)
- 缺点:
- 只能继承父类实例的属性和方法,不能复制父类原型上的属性和方法
- 没法实现复用,每一个实例都会有父类实例的副本,影响性能
- 组合继承:上述两种方式的组合,用原型链的方式继承原型上的属性和方法,用构造函数的方式,来实现实例属性和方法的继承
- 寄生组合式继承:ES6 Class的继承原理
- 子类实例继承了父类实例的属性和方法
- 原型链的继承
- 父类构造函数上的属性继承
原型,基本只要记住原型链的图,面试都没有太大的问题;继承的概念比较深涩难懂,须要多加实践。new、instanceof、ES6的继承都是须要手写实现的。详见【面试系列——手写代码实现(一)】
浅拷贝和深拷贝
浅拷贝:建立一个新对象,将原对象的属性值赋值给新对象
- 使用场景:Object.assign、展开符(...)、Array.prototype.slice
Object.assign:主要将一个和多个对象的可枚举属性进行合并
深拷贝:拷贝一个对象的所有属性,拷贝完成以后,两个对象相互不影响
- 使用场景:JSON.parse(JSON.stringify(obj))
其中JSON拷贝的缺点:
- 会忽略undefined
- 会忽略Symbol
- 不能序列化函数
- 不能拷贝循环引用对象
- 不能正确调用new Date()
- 不能处理正则
深拷贝和浅拷贝是面试考察的重点,主要是手写实现深拷贝,在面试题中出现次数不少【面试系列——手写代码实现(一)】
异步处理
异步解决方案:
- Raw Callback Style:朴素函数做为回调函数,接受error,data等参数
- Promise Callback Style:经过{ then }对象,去处理onFulfilled和onRejected两个回调函数
- Generator Callback Style:经过 * 号和yield关键词,将多层嵌套的callback扁平化的语法糖
- Async/await Callback Style:经过async和await关键词,将Promise+Generator标准化和语法化的产物
Promise:是一个类,经过new来进行声明。
Promise有三种状态:
- pending:等待状态,初始化状态,可转变成fulfilled或rejected
- fulfilled:成功状态,不可转变状态,且有一个不可变值(value)
- rejected:失败状态,不可转变状态,且有一个不可变的失败缘由(reason)
then方法:参数包含两个函数
- onFulfilled - 成功时执行的函数【参数是上一次返回的成功值】
- onRejected - 失败时执行的函数【参数是上一次返回的失败缘由】
all方法:返回一个Promise,Promise返回一个结果是全部promise的结果数组
race方法:返回一个Promise,Promise返回一个结果值【最快响应的那个Promise的值】
Generator函数就是一个状态机,内部包含多个状态
- yield做为一种暂停标志
- next方法能够访问下一个状态
Async:Generator的语法糖,是一个经过异步执行并隐式返回Promise对象的函数
async对generator的改进:
- 内置执行器
- 返回Promise
- await后面能够是Promise,也能够是原始值
- 更好的语义
异步处理,是一个常考的命题。Promise,常常被用在开发环境中,了解其原理是必不可少的。面试中,也会被要求去实现一个简单的Promise或者Promise方法。async和await也会被问及,它的实现原理也是须要掌握的。具体实现部分能够查看【面试系列——手写代码实现(一)】
模块化
常见的模块化方案:
CommonJS
- 一个文件就是一个模块,经过执行该文件来加载模块
- 每一个模块内部,module变量表明当前模块,这个变量就是一个对象,exports是它对外的接口
- require命令第一次加载该脚本时,就会执行整个脚本,在内存中生成一个对象(屡次加载,只有第一次会被运行,结果被缓存)
- 特色:
- 全部代码都运行在模块做用域,不会污染全局做用域
- 独立性是模块最重要的特色,模块内部最好不会和程序其余部分直接交互
- 模块屡次加载,只有第一次会被执行,并将结果会被缓存下来;若是想要从新执行,就得清除缓存
- 模块加载的顺序,按照其在代码中的位置
- CommonJS,它是同步加载,不适合浏览器
AMD
- 它采用异步的方式加载模块,模块的加载不会影响它后面语句的运行。全部依赖这个模块的代码,都会被放到回调函数里面去
- AMD经过define的方式来定义模块
define(id?, dependencies?, factory);
- 使用时,依然经过require关键词
require([module], callback);
CMD
- 它是seajs所推广的一种模块化方法
- 与AMD的区别是:
- 对于依赖的模块,AMD是提早执行的,CMD是延迟执行的;【requirejs2.0,也改为了延迟执行】
- AMD推崇依赖前置,CMD推崇就近依赖
ES6 Module
- 设计思想:尽可能的静态化,使得在编译的时候,就能清楚模块之间的依赖关系,以及输入和输出的变量。【AMD和CMD,都是在运行时肯定的】
- 经过import和export关键词组成,export做为对外接口,import是输入其余模块的功能
- 还有一个export default命令用于指定模块的默认输出【一个模块只有一个默认输出】
ES6 Module 和 CommonJS 的区别:
- 前者是值的引用,后者是值的拷贝
- 前者是编译时输出接口,后者是运行时加载
require的性能问题:因为它是值的拷贝,比较占用内存
CommonJS和ES6 Module是考察的重点,尤为是它们之间的区别。本篇概述的比较浅显,能够看一些详解的文章,详细了解一下。
防抖和节流
防抖:事件触发n秒后,执行回调,若是在这n秒内再次被触发,就从新计时
- 实现:
- 返回一个函数,函数内部会去清除计时器
- 而后从新执行setTimeout进行计时
节流:事件在一段时间内只会被触发一次,若是屡次触发,只有一次生效。
- 实现:
- 返回一个函数,函数内部定义一个last【上回执行的时间】
- 获取当前时间now,来经过last+delay的比较,看是否执行回调
防抖和节流是平常开发工做也常常会使用的优化方法,面试必考题。面试者须要对其实现原理,聊熟于心,可以熟练编写代码。详见【面试系列——手写代码实现(一)】
函数式编程
特性:
- 函数是一等公民——函数能够跟其余变量同样,做为其余函数的输入输出
- 不可变量
- 纯函数——没有反作用的函数,不修改函数外部的变量
- 引用透明——一样的输入,一定是一样的输出
- 惰性计算
React就是函数式编程的典型表明,ReactView = render(data);
优点:
- 更好的状态管理——它的宗旨就是无状态的
- 更简单的复用
- 更加优雅的组合
高阶函数:只要知足如下两点中的一点便可
函数柯里化:就是部分求值,将使用多个参数的函数转出一系列一个参数的函数的方式,并接受剩余参数的函数
函数式编程,近些年来挺火的。虽然JavaScript名义上不是一种函数式编程语言,可是这种方式,能够提升编码效率;总体的上手成本仍是比较高的,并且深涩难懂。可是将来,函数式编程仍然会持续火爆。
总结
最后但愿分享的内容,对你的面试能有帮助。这些知识点只是浅显的概述,仍然须要你去看一些文章,深刻理解。网上也有不少深刻讲解的系列,好比冴羽老师的。