3 基础概念
3.1 闭包
执行外部函数返回内部函数后,虽然外部函数已经弹出调用栈了,但是内部函数对外部函数变量的引用依然保存在内存中,那么内部函数和这些变量的集合就叫做闭包。
闭包的好处: 1. 私有变量在内存中持久化
闭包的坏处: 1. 使用不当会造成内存泄漏
3.2 原型
在 js 中对象是由构造函数创建的,构造函数中会有一个 prototype 属性,指向一个对象,这个对象包含了由该构造函数创建的实例所共享的属性和方法, 由该构造函数创建的实例可以通过 proto 属性指向这个对象,这个对象就是我们所说的原型。
当想要访问对象的某个属性时,如果在当前对象查找不到,就会往该对象的原型查找,对象的原型也会有属于他的原型对象,如此循环,直到 null 停止,这就是我们所说的原型链
隐式原型: proto 显示原型: prototype
相关方法: 1. hasOwnProperty() 判断属性是否是实例自身的属性 2. Object.getPrototypeOf() 获取实例的原型
3.3 作用域
作用域分成全局作用域
、函数作用域
、块级作用域
,它标识着一个变量是否合法 (编译过程中就已经确认了)
当查询一个变量时,如果当前作用域查询不到,会往上一级作用域查找,如此循环,直到全局作用域,这就是我们所说的作用域链
3.4 执行上下文
从类型上看
- 全局执行上下文
- 函数执行上下文
- eval 执行上下文
从生命周期上看
- 创建阶段
- this 绑定
- 创建词法环境 (let、const会被提升到词法环境)
- 创建变量环境 (var 声明的变量会被提升到变量环境)
- 执行阶段
- 对变量进行赋值,执行代码
- 回收阶段
- 当执行上下文弹出调用站后,会对上下文进行回收
执行上下文栈: 当 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 循环的变量不会被清除
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中的变量会被提升,导致变量覆盖
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