调用堆栈
1 | 调用栈是解释器(比如浏览器中的 JavaScript 解释器)追踪函数执行流的一种机制。当执行环境中调用了多个函数时,通过这种机制,我们能够追踪到哪个函数正在执行,执行的函数体中又调用了哪个函数。 |
原始类型
1 | # 最新的 ECMAScript 标准定义了 9 种数据类型: |
值类型和引用类型
1 | # 值类型: |
隐式, 显式, 名义和鸭子类型
JavaScript 是一种弱类型
或者说动态
语言。这意味着你不用提前声明变量的类型,在程序运行过程中,类型会被自动确定。这也意味着你可以使用同一个变量保存不同类型的数据:1
2
3
4
5
6var foo = 42; # foo is a Number now
foo = "bar"; # foo is a String now
foo = true; # foo is a Boolean now
# 隐式转换为布尔:“truthy”和“falsy”。当 JavaScript 需要一个布尔值时(例如:if 语句),任何值都可以被使用。 最终这些值将被转换为 true 或 false。
'', +0, -0, NaN, null, undefined, 0, 转换为boolean类型的时候都是false
== 与 ===, typeof 与 instanceof
1 | # == 会进行隐式转换 |
this, call, apply 和 bind
与其他语言相比,函数的 this 关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。
在绝大多数情况下,函数的调用方式决定了this的值 (运行时绑定)
,所以this的值在每次函数调用的时候都可能不同。es5 中引入了bind来设置this的值。
需要指出的是在es2015中引入的箭头函数中,不提供自身的this绑定,其this值保持为闭合词法的上下文值1
2# call ,apply
二者作用类似,都是指定具体this和参数调用函数,区别是call调用时参数以列表的形式传递,而apply则是以数组的形式传递参数
函数作用域, 块级作用域和词法作用域
作用域:当前执行的上下文。值和表达式在其中“可见“或者可以被访问到的上下文,js作用域以链的形式存在。1
2
3
4
5
6js代码执行时,首先回创建一个全局的执行上下文
# 函数作用域
通常情况下,在函数内部定义的变量不能在函数之外的任何地方访问,同时内部函数包含外部函数作用域。
# 块级作用域
在js中函数块是🈯️被{}包裹住的相关联的状态集合,例如一个for循环体,一个条件if判断都可以纳入一个块级作用域范围
闭包
你可以在函数的内部再定义一个函数,嵌套函数对其容器函数是私有的,他自身也形成了一个闭包,当闭包内包含外部作用域的参数和变量时,会保存其值,当一个闭包中进行变量查找时,更近的作用域有更高的优先权,这就形成了作用域链
map, reduce, filter 等高阶函数
1 | # map |
变量提升
其实质是变量的声明提升1
2
3
4
5console.log(b) # undefined
var b; # 声明提升
console.log(c) # VM315:1 Uncaught ReferenceError: c is not defined
let c; # let 和 const 声明的无法提升变量声明
Promise
Promise 对象用于表示一个异步操作的最终完成 (或失败)及其结果值。1
2
3
4一个 Promise 必然处于以下几种状态之一:
# 待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
# 已兑现(fulfilled): 意味着操作成功完成。
# 已拒绝(rejected): 意味着操作失败。
立即执行函数, 模块化, 命名空间
递归
算法
数据结构
消息队列和事件循环
JavaScript有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。这个模型与其它语言中的模型截然不同,比如 C 和 Java。
一个 JavaScript 运行时包含了一个待处理消息的消息队列。每一个消息都关联着一个用以处理这个消息的回调函数。
在 事件循环 期间的某个时刻,运行时会从最先进入队列的消息开始处理队列中的消息。被处理的消息会被移出队列,并作为输入参数来调用与之关联的函数。正如前面所提到的,调用一个函数总是会为其创造一个新的栈帧。
函数的处理会一直进行到执行栈再次为空为止;然后事件循环将会处理队列中的下一个消息(如果还有的话)。1
2
3
4# 之所以称之为 事件循环,是因为它经常按照类似如下的方式来被实现:
while (queue.waitForMessage()) {
queue.processNextMessage();
}
在浏览器里,每当一个事件发生并且有一个事件监听器绑定在该事件上时,一个消息就会被添加进消息队列。如果没有事件监听器,这个事件将会丢失。所以当一个带有点击事件处理器的元素被点击时,就会像其他事件一样产生一个类似的消息。
setTimeout, setInterval 和requestAnimationFrame
1 | # setTimeout |
多态和代码复用
1 | # 多态 |
按位操作符, 类数组对象和类型化数组
1 | # 按位与 |
DOM 树和渲染过程
DOM(Document Object Model——文档对象模型)是用来呈现以及与任意 HTML 或 XML文档交互的API。DOM 是载入到浏览器中的文档模型,以节点树的形式来表现文档,每个节点代表文档的构成部分
等待资源加载时间和大部分情况下的浏览器单线程执行是影响Web性能的两大主要原因。
通过了解浏览器单线程的本质与最小化主线程的责任可以优化Web性能,来确保渲染的流畅和交互响应的及时。
1 | # DNS查找 |
new 与构造函数, instanceof 与实例
new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。1
2
3
4
5
6
7
8
9
10
11
12# new 关键字会进行如下的操作:
1、创建一个空的简单JavaScript对象(即{});
2、链接该对象(设置该对象的constructor)到另一个对象 ;
3、将步骤1新创建的对象作为this的上下文 ;
4、如果该函数没有返回对象,则返回this。
# 当代码 new Foo(...) 执行时,会发生以下事情:
1、一个继承自 Foo.prototype 的新对象被创建。
2、使用指定的参数调用构造函数 Foo,并将 this 绑定到新创建的对象。new Foo 等同于 new Foo(),也就是没有指定参数列表,Foo 不带任何参数调用的情况。
3、由构造函数返回的对象就是 new 表达式的结果。如果构造函数没有显式返回一个对象,则使用步骤1创建的对象。(一般情况下,构造函数不返回值,但是用户可以选择主动返回对象,来覆盖正常的对象创建步骤)
原型继承与原型链
有些人认为JavaScript并不是真正的面向对象语言,在经典的面向对象语言中,您可能倾向于定义类对象,然后您可以简单地定义哪些类继承哪些类,JavaScript使用了另一套实现方式,继承的对象函数并不是通过复制而来,而是通过原型链继承(在 ES2015/ES6 中引入了 class 关键字,但那只是语法糖,JavaScript 仍然是基于原型的)
当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象( object )都有一个私有属性(称之为 `proto__)指向它的构造函数的原型对象(prototype )。该原型对象也有一个自己的原型对象(
proto` ) ,层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。__
几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。
JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。
Object.prototype 属性表示 Object 的原型对象。
1 | # 原型链继承 |
注:每一个函数对象(Function)都有一个prototype属性,并且只有函数对象有prototype属性,因为prototype本身就是定义在Function对象下的属性。当我们输入类似var person1=new Person(…)来构造对象时,JavaScript实际上参考的是Person.prototype指向的对象来生成person1。另一方面,Person()函数是Person.prototype的构造函数,也就是说Person===Person.prototype.constructor(不信的话可以试试)。在定义新的构造函数Teacher时,我们通过function.call来调用父类的构造函数,但是这样无法自动指定Teacher.prototype的值,这样Teacher.prototype就只能包含在构造函数里构造的属性,而没有方法。因此我们利用Object.create()方法将Person.prototype作为Teacher.prototype的原型对象,并改变其构造器指向,使之与Teacher关联。
* 在原型链上查找属性比较耗时,对性能有副作用,这在性能要求苛刻的情况下很重要。另外,试图访问不存在的属性时会遍历整个原型链。
Object.create 和 Object.assign
1 | # Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__(原型对象)。新对象的原型就是调用 create 方法时传入的第一个参数: |
工厂函数和类
设计模式
Memoization
纯函数, 函数副作用和状态变化
耗性能操作和时间复杂度
JavaScript 引擎
二进制, 十进制, 十六进制, 科学记数法
偏函数, 柯里化, Compose 和 Pipe
代码整洁之道
promise执行顺序
1 | setTimeout(()=>{ |