Lazy loaded image
技术分享
JavaScript 核心
00 min
2021-3-13
2024-11-25
type
status
date
slug
summary
tags
category
icon
password

JavaScript 重点知识总结

什么是JavaScript

JavaScript 的核心是 ECMAScript,并集成了 DOMBOM

ECMAScript(JS语法规范)

重要版本:ECMA-262,俗称ES6、ES2015、ES Harmony,支持了类、模块、迭代器、生成器、箭头函数、Promise、反射、代理等众多新数据类型。

DOM(Document Object Model,文档对象模型)

DOM 将 HTML 或 XML 抽象为分层节点结构的文档对象,通过它实现添加、删除和修改面页的各个部分,如下图:
notion image
所有节点都必须继承Node接口,共享了相同的基本属性和方法,Node类型众多,由上图可以看到,除了顶层节点为 DOCUMENT_NODE 类型,最常用的就是 元素节点ELEMENT_NODE 和 文本节点TEXT_NODE。
Document节点类型作为顶层节点,直接表示了DOM节点树,Document接口向HTML或XML文档提供了全局操作。值得注意的是,在浏览器中window对象提供了document全局属性作为HTMLDocument对象(继承了Document接口)的实例,返回当前窗口文档的Document对象,因此通常直接使用document实例更方便的获取Document对象:
Element节点类型表示HTML或XML元素,从Document中获得的所有对象都继承Element接口,其中HTML元素通过HTMLElement对象(继承了Element接口)表示,增加了一些属性。

BOM(Browser Object Model,浏览器对象模型)

BOM 的核心是 window对象(ECMAScript中Global对象的一种实现,在浏览器中用window作为全局上下文对象),表示脚本正在运行时的浏览器窗口。
window对象中最为常见的几个属性和方法如:定时器setTimeout()setInterval()、DOM对象document、URL信息对象location。

内存管理

JavaScript 最常用的垃圾收集策略是标记清理(mark-and-sweep)
如果已经明确某个全局变量(局部变量会在超出作用域后自动回收)在上下文不会再使用,那么显式将它设为null,会在下次垃圾回收时被回收掉。
使用JS闭包很容易在不知不觉中造成内存泄漏:

语言基础

数据类型

基本类型
  • String
  • Number
  • Boolean
  • Null
  • Undefined
引用类型
  • Object(包括Function、Array)
类型判断

函数

JS中的函数实际是对象,均是Function类型的实例

var 和 const、let 的区别

ES6之后,不要再使用var,优先使用const,其次使用let
var
const
let
作用域
函数内部
代码块
代码块
提升

数据类型

typeof 操作符
判断一个变量是否为基本类型,对一个变量使用typeof会返回下列字符串之一:
  • "undefined" - 变量未定义,变量可能未初始化或显式被赋值了undefined,甚至变量从未声明
  • "boolean" - 布尔值
  • "string" - 字符串
  • "number" - 数字
  • "object" - 对象,注意typeof null返回的是”object",因为null被视为空对象
  • "function" - 函数
  • "symbol" - 符号,用于给对象增加新的唯一、不可变的属性,ES6为对象内置了一批常用符号属性
建议声明变量的同时进行初始化,不要对变量显示赋值为undefined,对于对象类型变量可以显式赋值为null
instanceof 操作符
判断一个对象变量属于哪个类型,返回true或false,对于基本类型使用始终返回false
类型转换
string -> number,可以使用Number(),建议优先使用 parseInt() 函数
任意类型 -> string,可以使用String(),也可以使用toString(),但null和undefined没有toString()方法

数组

TODO:常用API

作用域

编译原理

传统的编译基本流程
通常将js归位解释型语言,但实际上js代码在执行前的微秒级别内都由js引擎(如V8)进行了编译

变量查找

查找从内部作用域开始逐层往外查找,直到匹配到第一个标识符时停止,而外层同名标识符被遮蔽

提升

变量声明和函数声明都会被提升,函数声明提升优先于变量

闭包(closure)

观察如下代码,执行它就会产生闭包:
由以上代码可知,闭包通常指的是:
在嵌套函数中,内部函数引用了外部函数作用域中的变量,并在外部函数之外的其他作用域中被执行
本质:当内部函数bar在其他作用域中被执行后,将始终持有对foo作用域上下文对象的引用,因此可以访问foo作用域中的所有变量,同时foo的上下文对象由于被bar引用而无法被垃圾回收器自动回收。
观察以下代码,是否产生闭包?
根据闭包定义,以上代码满足前两个条件,但并不满足第三个条件,内部函数bar并未在外部函数以外的其他作用域中执行,因此并未产生闭包。
观察以下代码,是否产生闭包?
根据闭包定义,以上代码满足三个条件,回调函数同样属于被嵌套的内部函数,引用了更外层的msg变量,回调函数最后会被引擎自动调用,始终持有wait的上下文对象,因此产生了闭包。
在定时器、事件监听器、异步请求等任务中,只要使用了回调函数,就会产生闭包。
闭包的优势:由于内部函数始终持有外部函数的上下文对象,每次调用内部函数,外部函数的变量对内部函数来说如同全局变量一样,因此可以防止变量被反复创建,节省内存资源。
闭包的劣势:由于内部函数始终持有外部函数的上下文对象,因此上下文对象不会被垃圾回收,过多的闭包可能会造成内存泄漏。

this

隐式绑定

函数被哪个对象调用,函数中的this就会绑定到那个对象的作用域

显示绑定

使用call、apply、bind方法,函数中的this会绑定到指定对象的作用域
Function.prototype.call(对象,参数1,参数2,...)
Function.prototype.apply(对象,[参数1,参数2,...])

硬绑定

使用bind方法,返回一个新函数,无论在哪调用这个新函数,原函数中的this始终与指定对象绑定
Function.prototype.bind(对象,arguments)

new绑定

new构造函数会创建新对象,新对象会绑定构造函数中的this

面向对象

对象属性

js对象可以定义两类属性:
  • 数据属性 —— 每个属性都内置 [[Configurable]]、[[Enumberable]]、[[Writable]]、[[Value]] 特性
  • 访问器属性 —— 每个属性都内置 [[Configurable]]、[[Enumberable]]、[[Get]]、[[Set]] 特性
数据属性没有[[Get]]、[[Set]]特性标识,但内置了getter、setter隐藏函数,才保证了正常的属性访问和赋值操作。当显式指定了get、set函数,属性就会作为访问器属性,不再使用内置getter、setter。
只能使用 Object.defineProperty() 修改属性的默认特性:
以字面量形式创建对象时,通过以下方式定义两类属性:
属性访问的另一种方式键访问

对象复制

浅拷贝
深拷贝

对象遍历

数组
数组内置有迭代器属性 @@iterator,看看它是怎么工作的:
对象

对象创建

使用构造函数
对构造函数的一种优化
使用
js中的类,本质还是由构造函数和原型实现的,只是es6定义的一种语法糖结构

对象继承

原型链
每个构造函数都有一个prototype对象,对象中有个constructor属性,指回构造函数本身,实例内部有个指针指回构造函数的prototype对象(通过instance.__proto__访问)。
Object、Funciton与原型对象的关系:
notion image
实例访问对象中的属性或方法,经历以下过程:
使用原型链继承的核心:子类原型指向父类实例
notion image
方法的扩展与重写
盗用父类构造函数
直接将父类实例作为子类原型,会导致父类实例的自身属性变为子类的原型属性,在子类实例中共享。
通过在子类构造函数中执行父类构造函数,使父类中的属性下放到子类实例,实现继承的属性私有化:
ES6继承语法

异步编程

回调函数(callback)

WIKI: Callback
a callback is any reference to executable code that is passed as an argument to other code; that other code is expected to call back (execute) the code at a given time.
This execution may be immediate as in a synchronous callback, or it might happen at a later point in time as in an asynchronous callback.
由WIKI给出的callback概念可知,callback作为参数传函数中,并在某个时间被执行,回调有可能同步也可能异步,如:forEach(callback),map(callback)中为同步回调,setTimeout(callback)中为异步回调。
本质:回调函数封装了程序的延续
ES6之前,回调函数可以用于解决异步编程中如下问题:
事件循环(Event Loop)
The Event Loop is a queue of callback functions. When an async function executes, the callback function is pushed into the queue. The JavaScript engine doesn't start processing the event loop until the code after an async function has executed.
当代码执行到异步函数时,引擎会将其中的callback压入到一个称为eventloop的队列中,等后续所有同步代码执行完成后,这个队列会以先进先出的顺序循环拿出callback执行。
每次循环执行callback的过程称为一次tick,每次循环后还会检查一次job queue
任务队列(Job Queue)
挂在每次事件循环的最后才执行的一个包含一系列微任务(Microtask)的队列,在每个宏任务(Task)的callback执行后被完全执行。因此,一次tick过程包括了一个宏任务和当前所有微任务的执行。
Task:
#
浏览器
Node
I/O
setTimeout
setInterval
setImmediate
requestAnimationFrame
Microtask:
#
浏览器
Node
process.nextTick
MutationObserver
Promise.then
执行顺序:当前script全局上下文中的同步代码全部执行(此时执行栈为空) -> 当前任务队列中的全部微任务 -> 事件循环中的下一个宏任务 -> 当前任务队列中的全部微任务 -> 事件循环中的下一个宏任务 -> ...
上一篇
Micro-App整合大数据中心
下一篇
SpringBoot核心