status
Published
slug
javascript-indoor-note
type
Post
category
Technology
date
Aug 20, 2022 → Nov 7, 2022
tags
笔记
前端
JavaScript
summary
JavaScript入门教程笔记,记录汇总知识点
JavaScript概览
- 轻量级脚本语言
- 嵌入式语言,可以嵌入宿主环境(host)
- 浏览器、Node项目
- 宿主环境会提供额外API供JavaScript调用
- 需要调用宿主环境提供的底层API,可以解释运行也可以编译运行(JIT)
- 编程风格是函数式编程和面向对象编程的一种混合体
- 支持函数式等多种编程范式
JavaScript语法特殊点
- 动态类型语言,变量可以随时更改类型(弱类型)。
- 变量提升(hoisting):所有的变量的声明语句,都会被提升到代码的头部
- JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。
- 严格相等运算符(
===
)
switch
语句内部采用的是“严格相等运算符”
- JavaScript 语言允许,语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置
- undefined & null
null
是一个表示“空”的对象,转为数值时为0
;undefined
是一个表示"此处无定义"的原始值,转为数值时为NaN
。- 在
if
语句中,null
和undefined
都会被自动转为false
- 布尔值转换规则:
- false:
undefined
,null
,false
,0
,NaN
,""
或''
- true:其余都为
true
,eg:[]
,{}
- 整数 | 小数:JavaScript底层没有整数,所有数都是64位浮点数
- 进行整数运算时:自动将64位浮点数转为32位整数
- 正零(
+0
)和负零(-0
):二者在当做分母的时候返回值不相等,其他时候都是等价的
NaN
(Not a Number):NaN
不等于任何值,包括它本身NaN
在布尔运算时被当做false
NaN
与任何数的运算结果都是NaN
。
Infinity
无穷:Infinity
大于一切数值(除了NaN
),-Infinity
小于一切数值(除了NaN
)Infinity
与NaN
比较,总是返回false
Infinity
与null
计算时,null
会转成0,等同于与0
的计算Infinity
与undefined
计算,返回的都是NaN
isNaN
方法:isNaN
只对数值有效,如果传入其他值,会被先转成数值- 传入字符串、对象和数组的时候,它们会被先转成
NaN
,所以最后返回true
- 对于空数组和只有一个数值成员的数组,
isNaN
返回false
- 判断
NaN
更可靠的方法是,利用NaN
为唯一不等于自身的值的这个特点,进行判断。
- JavaScript 使用 Unicode 字符集,只支持两字节的字符
- JavaScript对于四字节的字符会识别成是两个字符
- JavaScript 返回的字符串长度可能是不正确的
- 对象的引用:
- 不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。
- 如果取消某一个变量对于原对象的引用,不会影响到另一个变量
- 表达式和语句的区分:
- 行首是大括号时,一律解释为代码块。
- 大括号前加上圆括号,解释为对象。
- 参数传递:
- 函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递(passes by value),在函数体内修改参数值,不会影响到函数外部。
- 函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference),在函数内部修改参数,将会影响到原始值
- arguments对象:用来在函数体内部读取所有参数
- 闭包:定义在一个函数内部的函数
- JavaScript 语言特有的"链式作用域"结构:父对象的所有变量,对子对象都是可见的,反之则不成立
- 用处:
- 读取外层函数内部的变量
- 让这些变量始终保持在内存中
- 封装对象的私有属性和私有方法
- eval命令:
- 接受一个字符串作为参数,并将这个字符串当作语句执行。
- 使用别名执行
eval
,eval
内部一律是全局作用域。
- 数组是特殊的对象:
- 数组的元素可以是任意类型
- 数组的key是从小到大排列的整数:0,1,2…
length
属性:该属性是一个动态的值,等于键名中的最大整数加上1
。字符串的键不影响length
属性
- 对象的相加:
- 对象相加的时候先转为原始类型的值再相加
- 即先调用
valueOf
方法,再调用toString
方法 - Date对象的实例,会优先执行
toString
方法。
- 严格相等运算符
- ===:严格相等运算符,比较它们是否为“同一个值”。如果两个值不是同一类型,严格相等运算符(
===
)直接返回false
- 比较复合类型值时,比较它们是否指向同一个地址
- ==:相等运算符,遇到不同类型的值时,会将它们转换成同一个类型,再用严格相等运算符进行比较。
void
运算符的作用是执行一个表达式,然后不返回任何值,或者说返回undefined
。
- 逗号运算符用于对两个表达式求值,并返回后一个表达式的值。
- try/catch/finally的执行顺序
return
语句在finally
之前执行,但是在finally
代码执行完毕后才返回catch
代码块之中,一遇到throw
语句或return
语句,就会去执行finally
代码块
- “分号的自动添加”(Automatic Semicolon Insertion,简称 ASI)
- Date的参数取值,月份序号从0开始,日期从1开始
20220909:
- 控制对象的状态:
- Object.preventExtensions:对象无法添加新的属性
- Object.seal:对象无法添加新的属性也无法删除旧属性
- Object.freeze:对象无法添加新属性、无法删除旧属性,也无法改变属性的值,即变对象为常量
漏洞:可以通过改变原型对象(Prototype)来增加属性
- JSON格式(JavaScript Object Notation)
- JavaScript 语言之中,一切皆对象,运行环境也是对象,所以函数都是在某个对象之中运行,
this
就是函数运行时所在的对象(环境) this
的指向是动态的- JavaScript 提供了
call
、apply
、bind
这三个方法,来切换/固定this
的指向。 call
方法,可以指定函数内部this
的指this
指向传入的对象- 如果参数为空、
null
和undefined
,则默认传入全局对象。 apply
接收一个数组作为函数执行时的参数bind()
方法用于将函数体内的this
绑定到某个对象,然后返回一个新函数。bind()
每次调用都会返回新的函数
- Prototype(原型对象):JavaScript 继承机制的设计思想就是,原型对象的所有属性和方法,都能被实例对象共享
- 每个函数都有默认的
prototype
属性,指向一个对象 - 对于普通函数来说,该属性基本无用
- 对于构造函数来说,生成实例的时候,该属性会自动成为实例对象的原型。
- 只要修改原型对象,变动就立刻会体现在所有实例对象上。
- 如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法。
原型对象的作用:定义所有实例对象共享的属性和方法。
- 原型链:对象到原型,再到原型的原型……
- 原型链的尽头是
null
- 读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的
Object.prototype
还是找不到,则返回undefined
- 如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)。
prototype
对象有一个constructor
属性,默认指向prototype
对象所在的构造函数。
instanceof
运算符只能用于对象,不适用原始类型的值。
- 模块:模块是实现特定功能的一组属性和方法的封装。
- 简单的做法是把模块写成一个对象,所有的模块成员都放到这个对象里面。
- 另一种做法是使用“立即执行函数”(Immediately-Invoked Function Expression,IIFE),将相关的属性和方法封装在一个函数作用域里面,可以达到不暴露私有成员的目的。
- “放大模式”(augmentation):如果一个模块很大,必须分成几个部分,或者一个模块需要继承另一个模块,这时就有必要采用“放大模式”(augmentation)
- 独立性是模块的重要特点,模块内部最好不与程序的其他部分直接交互。
- 严格模式:
'use strict';
- 设计目的:
- 明确禁止一些不合理、不严谨的语法,减少 JavaScript 语言的一些怪异行为。
- 增加更多报错的场合,消除代码运行的一些不安全之处,保证代码运行的安全。
- 提高编译器效率,增加运行速度。
- 为未来新版本的 JavaScript 语法做好铺垫。
- 总结:显式报错、增强的安全措施、静态绑定、向下一个版本的JavaScript过渡
- 一些异步操作相关的基本概念
- 单线程模型:JavaScript只在一个线程上运行,好处是实现简单,执行环境单纯,坏处任务耗时长,后续任务需要排队,拖延程序执行。
- 同步任务:主线程上执行,只有前一个任务执行完毕,才能执行后一个任务。
- 异步任务:被引擎放在一边,不进入主线程、而进入任务队列的任务。只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果),该任务(采用回调函数的形式)才会进入主线程执行。
- 任务队列:里面是各种需要当前程序处理的异步任务
- 机制:主线程会去执行所有的同步任务。等到同步任务全部执行完,就会去看任务队列里面的异步任务如果满足条件,那么异步任务就重新进入主线程开始执行,这时它就变成同步任务了。
- 异步任务的写法通常是回调函数。
- 事件循环:用于等待和发送消息和事件
- 异步操作的模式:
- 回调函数
- 事件监听
- 发布/订阅
- 串行执行/并行执行
- 定时器:
- setTimeout():指定某个函数或某段代码,在多少毫秒之后执行
- setTimeout(f, 0):
setTimeout(f, 0)
会在下一轮事件循环一开始就执行 - 可以调整事件的发生顺序
- 用户自定义的回调函数,通常在浏览器的默认动作之前触发
- 由于
setTimeout(f, 0)
实际上意味着,将任务放到浏览器最早可得的空闲时段执行,所以那些计算量大、耗时长的任务,常常会被放到几个小部分,分别放到setTimeout(f, 0)
里面执行。 - setInterval():指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。
- clearTimeout()
- clearInterval()
setTimeout
和setInterval
的运行机制,是将指定的代码移出本轮事件循环,等到下一轮事件循环,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就继续等待。- Promise对象:Promise 的设计思想是,所有异步任务都返回一个 Promise 实例。Promise 实例有一个
then
方法,用来指定下一步的回调函数。 then
方法可以接受两个回调函数,第一个是异步操作成功时(变为fulfilled
状态)的回调函数,第二个是异步操作失败(变为rejected
)时的回调函数(该参数可以省略)。- Promise 的回调函数不是正常的异步任务,而是微任务(microtask)。它们的区别在于,正常任务追加到下一轮事件循环,微任务追加到本轮事件循环。这意味着,微任务的执行时间一定早于正常任务。
- DOM:JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)
- 作用:将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容)
- 浏览器会根据 DOM 模型,将结构化文档(比如 HTML 和 XML)解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口。
- DOM节点:浏览器提供一个原生的节点对象
Node
,七种节点都继承了Node
,因此具有一些共同的属性和方法。 - 节点树:
- 浏览器原生提供
document
节点,代表整个文档 - 文档的第一层有两个节点,第一个是文档类型节点(
<!doctype html>
),第二个是 HTML 网页的顶层容器标签<html>
,其他 HTML 标签节点都是它的下级节点 - 三种层级关系:
- 父节点关系(parentNode):直接的那个上级节点
- 子节点关系(childNodes):直接的下级节点
- 同级节点关系(sibling):拥有同一个父节点的节点
Document | 整个文档树的顶层节点 |
DocumentType | doctype标签(比如<!DOCTYPE html>) |
Element | 网页的各种HTML标签(比如<body>、<a>等) |
Attr | 网页元素的属性(比如class="right" |
Text | 标签之间或标签包含的文本 |
Comment | 注释 |
DocumentFragment | 文档的片段 |
- NodeList接口,HTMLCollection接口,ParentNode 接口,ChildNode 接口
- document节点对象代表整个文档,每张网页都有自己的
document
对象。window.document
属性就指向这个对象。
Element
节点对象对应网页的 HTML 元素。每一个 HTML 元素,在 DOM 树上都会转化成一个Element
节点对象(以下简称元素节点)。
- Text节点:文本节点(
Text
)代表元素节点(Element
)和属性节点(Attribute
)的文本内容
- DocumentFragment 节点:代表一个文档的片段,本身就是一个完整的 DOM 树形结构。它没有父节点,
parentNode
返回null
,但是可以插入任意数量的子节点。它不属于当前文档,操作DocumentFragment
节点,要比直接操作 DOM 树快得多。 - 一般用于构建一个 DOM 结构,然后插入当前文档
DocumentFragment
节点本身不能被插入当前文档。当它作为appendChild()
、insertBefore()
、replaceChild()
等方法的参数时,是它的所有子节点插入当前文档,而不是它自身- 一旦
DocumentFragment
节点被添加进当前文档,它自身就变成了空节点(textContent
属性为空字符串),可以被再次使用 - 如果想要保存
DocumentFragment
节点的内容,可以使用cloneNode
方法。
- CSSStyleDeclaration 接口用来操作元素的样式。三个地方部署了这个接口;CSSStyleDeclaration 接口可以直接读写 CSS 的样式属性,不过,连词号需要变成骆驼拼写法。
- 元素节点的
style
属性(Element.style
) CSSStyle
实例的style
属性window.getComputedStyle()
的返回值
- StyleSheet 接口:
StyleSheet
接口代表网页的一张样式表,包括<link>
元素加载的样式表和<style>
元素内嵌的样式表。
- Mutation Observer API 用来监视 DOM 变动。DOM 的任何变动,比如节点的增减、属性的变动、文本内容的变动,这个 API 都可以得到通知。
- 它等待所有脚本任务完成后,才会运行(即异步触发方式)。
- 它把 DOM 变动记录封装成一个数组进行处理,而不是一条条个别处理 DOM 变动。
- 它既可以观察 DOM 的所有类型变动,也可以指定只观察某一类变动。
- 事件的本质是程序各个组成部分之间的一种通信方式,也是异步编程的一种实现。
- EventTarget接口:DOM 节点的事件操作(监听和触发),都定义在
EventTarget
接口。
- 监听模型:浏览器的事件模型,就是通过监听函数(listener)对事件做出反应。浏览器监听到了这个事件,就会执行对应的监听函数。这是事件驱动编程模式(event-driven)的主要编程方式。
- HTML中on-属性方式指定的监听代:只会在冒泡阶段触发,不利于代码分工,因此不推荐使用
- 元素节点的事件属性:同一个事件只能定义一个监听函数
EventTarget.addEventListener
是推荐的指定监听函数的方法。它有如下优点:- 同一个事件可以添加多个监听函数。
- 能够指定在哪个阶段(捕获阶段还是冒泡阶段)触发监听函数。
- 除了 DOM 节点,其他对象(比如
window
、XMLHttpRequest
等)也有这个接口,它等于是整个 JavaScript 统一的监听函数接口。 - 监听函数内部的
this
指向触发事件的那个元素节点。
- 事件的传播:一个事件发生后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段:
- 第一阶段(捕获阶段):从
window
对象传导到目标节点(上层传到底层),称为“捕获阶段”(capture phase)。 - 第二阶段(目标阶段):在目标节点上触发,称为“目标阶段”(target phase)。
- 第三阶段(冒泡阶段):从目标节点传导回
window
对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。
- 事件的代理:由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理(delegation)
- 如果希望事件到某个节点为止,不再传播,可以使用事件对象的
stopPropagation
方法。stopPropagation
方法只能阻止这个事件的传播,不能取消这个事件 - 如果想要彻底取消该事件,不再触发后面所有
click
的监听函数,可以使用stopImmediatePropagation
方法
- 浏览器模型: