面试系列——JavaScript基础篇

前言

本篇是面试系列的JavaScript的基础篇。文章主要围绕着基础类型、做用域、闭包、原型、继承、this的指向、深浅拷贝、字符串和数组的方法、节流、防抖、函数式编程等知识展开的。面试

正文

基础类型

JavaScript的数据类型主要分为两类:原始类型、引用类型ajax

原始类型:6种编程

  • null 【JS的数据底层都是用二进制进行存储的,前三位为0会被判断成对象,null是全都为0】
  • undefined
  • Number【双精度64位二进制格式的值——数字、±Infinity、NaN】
  • String
  • Boolean
  • Symbol【ES6新增定义,实例是惟一且不可变的】

引用类型:windows

  • Object
  • Array
  • Function

在内存中,存储的形式:数组

  • 原始类型,会被保存到栈内存中
  • 引用类型,会被保存到堆内存中

类型之间的赋值方式: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名义上不是一种函数式编程语言,可是这种方式,能够提升编码效率;总体的上手成本仍是比较高的,并且深涩难懂。可是将来,函数式编程仍然会持续火爆。

总结

最后但愿分享的内容,对你的面试能有帮助。这些知识点只是浅显的概述,仍然须要你去看一些文章,深刻理解。网上也有不少深刻讲解的系列,好比冴羽老师的。

相关文章
相关标签/搜索