Skip to content

3 基础概念

3.1 闭包

执行外部函数返回内部函数后,虽然外部函数已经弹出调用栈了,但是内部函数对外部函数变量的引用依然保存在内存中,那么内部函数和这些变量的集合就叫做闭包。

闭包的好处: 1. 私有变量在内存中持久化

闭包的坏处: 1. 使用不当会造成内存泄漏

3.2 原型

在 js 中对象是由构造函数创建的,构造函数中会有一个 prototype 属性,指向一个对象,这个对象包含了由该构造函数创建的实例所共享的属性和方法, 由该构造函数创建的实例可以通过 proto 属性指向这个对象,这个对象就是我们所说的原型。

当想要访问对象的某个属性时,如果在当前对象查找不到,就会往该对象的原型查找,对象的原型也会有属于他的原型对象,如此循环,直到 null 停止,这就是我们所说的原型链

隐式原型: proto 显示原型: prototype

相关方法: 1. hasOwnProperty() 判断属性是否是实例自身的属性 2. Object.getPrototypeOf() 获取实例的原型

3.3 作用域

作用域分成全局作用域函数作用域块级作用域,它标识着一个变量是否合法 (编译过程中就已经确认了)

当查询一个变量时,如果当前作用域查询不到,会往上一级作用域查找,如此循环,直到全局作用域,这就是我们所说的作用域链

3.4 执行上下文

从类型上看

  1. 全局执行上下文
  2. 函数执行上下文
  3. eval 执行上下文

从生命周期上看

  1. 创建阶段
    • this 绑定
    • 创建词法环境 (let、const会被提升到词法环境)
    • 创建变量环境 (var 声明的变量会被提升到变量环境)
  2. 执行阶段
    • 对变量进行赋值,执行代码
  3. 回收阶段
    • 当执行上下文弹出调用站后,会对上下文进行回收

执行上下文栈: 当 JS 执行代码时,首先遇到全局代码,会创建一个全局执行上下文并压入执行栈中,当遇到函数调用时,就会为该函数创建一个新的函数执行上下文压入栈中, 引擎会执行位于执行上下文栈顶的函数,当函数执行完后,执行上下文会从栈中弹出,继续执行下一个上下文,当所有代码都执行完毕后,从栈中弹出全局执行上下文

执行上下文

3.5 类数组对象

一个拥有 length 属性和若干索引属性的对象就是类数组对象。

将类数组对象转化成数组的方法:

1 Array.from(arrayLike)

2 Array.prototype.slice.call(arrayLike)

3 Array.prototype.concat.apply([], arrayLike)

4 Array.prototype.splice.call(arrayLike, 0)

注意: 类数组对象不一定有 iterator 接口,所以不能用拓展运算符

类数组对象

1 arguments 可以使用拓展运算符

2 可迭代对象(Iteratable Object,例如 [] )与可枚举对象 (可以通过 for..in 遍历,例如 {} ) 不可用拓展运算符转化

3.6 严格模式

使用 use strict 即可声明严格模式

目的:

  • 消除 JavaScript 一些语法不合理、不严谨之处,减少怪异行为
  • 消除代码运行的一些不安全之处,保证代码运行的安全

区别:

  • 禁止使用 with 语句
  • 禁止 this 关键字指向全局对象
  • 对象的属性名不能重复
  • arguments.callee 被禁止

3.7 尾调用

在函数的最后一步调用另一个函数,称为尾调用(父函数执行上下文弹出调用栈)

只在严格模式开启

3.8 常见的问题

3.8.1 Map、Object、WeakMap 的区别

  • 从 key 的角度上看

    • Map 的 key 是可以任意的
    • Object 的 key 只能是字符串
    • WeakMap 的 key 只能是对象
  • 从获取长度的角度上看

    • Map 通过 size 属性获取键值对的个数
    • Object 需要通过 Object.keys() 获取键值对的个数
    • WeakMap 没有 size 属性,也没有办法获取键值对的个数
  • 从迭代的角度上看

    • Map 具有 iterator 属性,可迭代
    • Object 可枚举
    • WeakMap 不可迭代
  • 从存储的性能角度上看

    • WeakMap 是一个弱引用关系,不影响引用对象的垃圾回收
    • 在频繁的增删键值对时,Map 的性能表现更好

3.8.2 Object.is 和 === 、 == 的区别

1 == 会进行隐式转化

2 === 不会进行隐私转化

3 Object.js 和 === 类似,但 NaN 和 NaN 判断相等,+0 和 -0 判断不等

3.8.3 什么是 DOM 和 BOM

DOM: 文档对象模型,提供了访问和操作网页内容的方法和接口

BOM: 浏览器对象模型,提供了与浏览器窗口进行交互的方法和接口

3.8.4 for...in 和 for...of 的区别

for...in 是对属性的遍历,常用于遍历可枚举的对象,例如对象,但它会遍历原型上的属性,所以一般我们会和 Object.hasOwnProperty() 一起使用

for...of 是对值的遍历,常用于遍历可迭代的对象,例如数组。

3.8.5 ajax、axios、fetch 的区别

  • Ajax 是一个创建交互式网页应用开发技术,一种在无需重新加载整个网页的情况下,能够更新部分网页的技术
  • Axios 是一个基于 promise 封装的 HTTP 请求库,可以用在浏览器和 node.js 中
  • Fetch 基于 Promise 设计,是一个可以发送网络请求的 API 他的缺点
    • 1 fetch 只对网络请求报错,对 400、500 都当做成功的请求,需要封装去处理
    • 2 fetch 默认不会带 cookie,需要添加配置项: fetch(url, { credentials: 'include' })
    • 3 fetch 不支持 abort,不支持超时控制,使用 setTimeout/Promise.reject 实现,但无法阻止网络请求过程,造成了流量的浪费
    • 4 fetch 没有办法原生监测请求的进度,而 XHR 可以

3.8.6 JavaScript 为什么要进行变量提升,它导致什么问题?

1 变量提升的表现

在函数某些地方声明了变量,都会被提升到函数的头部,使得我们在声明前访问变量都不会报错

2 变量提升的原因

JavaScript 是一个解释型语言,他会执行 js 代码前会有一个编译过程,创建执行上下文,创建变量环境,例如 var 声明的变量会被提升到变量环境中

当访问一个变量的时候,会在当前上下文的变量环境查询

3 导致的问题

1 for 循环的变量不会被清除

js
var temp = 'hello world';

for(var i = 0; i < temp.length; i++) {
  console.log(temp[i]);
}

console.log(i); // 11
var temp = 'hello world';

for(var i = 0; i < temp.length; i++) {
  console.log(temp[i]);
}

console.log(i); // 11

2 一些条件语句为false中的变量会被提升,导致变量覆盖

js
var temp = new Date();

function fn() {
    console.log(temp);
    if (false) {
        var temp = 'hello world';
    }
}

fn(); // undefined
var temp = new Date();

function fn() {
    console.log(temp);
    if (false) {
        var temp = 'hello world';
    }
}

fn(); // undefined