【FE学习-JS】JS基础
JavaScript⁍
JavaScript可以分为三部分[5]
注:这里暂时没找其他的理论依据,但是道理大概是这么回事
- ECMAScript:核心,描述了Js语法及各种标准,目前通用的有ES5,更新的是ES6
- DOM(文档对象类型):HTML和XML的API。用DOM API可以轻松地删除、添加、替换节点
- BOM(浏览器对象模型):对浏览器窗口进行访问和操作(不同浏览器有自己的实现,没有统一标准)
应用程序接口(Application Programming Interfaces(API)) [6]
API 通常分为浏览器API和第三方API
浏览器API包括DOM API、地理位置API、画布和web GL API,以及 HTMLMediaElement
和 WebRTC
等 影音类 API
JavaScript 在页面上做了什么⁍
在 HTML 和 CSS 集合组装成一个网页后,浏览器的 JavaScript 引擎将执行 JavaScript 代码。
JavaScript 最普遍的用处是通过 DOM API(见上文)动态修改 HTML 和 CSS 来更新用户界面 (user interface)。
每个浏览器标签页就是其自身用来运行代码的独立容器(运行环境)
解释型和编译型⁍
在解释型语言中,代码自上而下运行,且实时返回运行结果。
JavaScript 是轻量级解释型语言。浏览器接受到JavaScript代码,并以代码自身的文本格式运行它。技术上,几乎所有 JavaScript 转换器都运用了一种叫做 即时编译 (JIT,Just-In-Time compiling)的技术;当 JavaScript 源代码被执行时,它会被编译成 二进制的格式 ,使代码运行速度更快。尽管如此,JavaScript 仍然是一门解释型语言,因为编译过程发生在代码运行中,而非之前。
添加Js的方法⁍
内部JavaScript⁍
1 |
|
外部JavaScript⁍
1 |
|
脚本调用策略⁍
HTML 元素是按其在页面中出现的次序调用的,如果用 JavaScript 来管理页面上的元素(更精确的说法是使用 文档对象模型 DOM),若 JavaScript 加载于欲操作的 HTML 元素之前,则代码将出错。
如果Js脚本位于文档头处,则会在HTML文档体之前解析,存在隐患。需要用一些结构笔面错误发生。
- (旧方法)把脚本元素放在文档体的底端(
</body>
标签之前,与之相邻),这样脚本就可以在 HTML 解析完毕后加载了。 - 监听浏览器的 “
DOMContentLoaded
” 事件,即 HTML 文档体加载、解释完毕事件。
1 |
|
- 外部引用添加
async
“异步”属性
1 |
|
async
和 defer
⁍
- 如果脚本无需等待页面解析,且页面的脚本之间彼此独立,不依赖于本页面的其它任何脚本时,那么应使用
async
。 - 如果脚本需要等待页面解析,且依赖于其它脚本,调用这些脚本时应使用
defer
,将关联的脚本按所需顺序置于 HTML 中。
1 |
|
声明/变量/常量⁍
09-3种声明变量方式 P9 - 01:19 [3:1]
- var,可变变量,有函数作用域(就是类比函数的话,可以在第1行调用,但是第10行定义)
- const不可修改(const定义的其实不是变量,是 常量 )
- let可修改 用在循环
var可以省略,但如果省略后就是全局变量(即使定义在函数内也是全局变量(准确说是全局对象的属性?))
1 |
|
如果访问没有声明的变量会抛出 引用错误(ReferenceError) [7]
变量⁍
一个 JavaScript 标识符必须以字母、下划线(_)或者美元符号($)开头;后续的字符也可以是数字(0-9)
变量未被赋值-》返回undefined
变量提升⁍
JavaScript 引擎的工作方式是,先解析代码,获取所有被声明的变量,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句,都会被提升到代码(函数)的头部,这就叫做变量提升(hoisting)。[8]
对于var来说,可以先使用变量稍后再声明变量而不会引发异常。
在 ECMAScript 6 中,let(const)同样会被提升变量到代码块的顶部但是不会被赋予初始值。在变量声明之前引用这个变量,将抛出引用错误(ReferenceError)。
但是只是没有引发异常,变量不能获取之后被赋予的值,此时调用会显示undefined
,而如果与数字进行运算,则会输出NaN
(因为是非数值undefined
进行运算)
1 |
|
对于函数来说,只有函数声明function foo()
会被提升到顶部,而函数表达式var baz = function()
不会被提升。
常量const⁍
常量不可以通过重新赋值改变其值,也不可以在代码运行时重新声明。
但是, 对象属性被赋值为常量是不受保护的,可以修改。(同样,数组也属于对象)[7:1]
1 |
|
数据类型⁍
JavaScript 中的类型应该包括这些:[9]
-
Number
(数字) -
String
(字符串) -
Boolean
(布尔) -
Symbol
(符号)(ES6 新增,一种实例是唯一且不可改变的数据类型。) -
Object
(对象)
-
null
(空) -
undefined
(未定义)
确定js的类型⁍
typeof
运算符instanceof
运算符Object.prototype.toString
方法[9:1]- 具体使用
Object.prototype.toString.call(i)
- 具体使用
1 |
|
typeof⁍
instanceof⁍
instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
1 |
|
必须是object
instanceof - JavaScript | MDN (mozilla.org)
prototype⁍
字面量(Literals,字面形式)⁍
字面量是由语法表达式定义的常量;或,通过由一定字词组成的语词表达式定义的常量[10]
null, undefined 和布尔值⁍
null⁍
JavaScript 中的 null
表示一个空值(non-value),必须使用 null 关键字才能访问,,转为数值时为0
。
1 |
|
undefined⁍
undefined
是一个“undefined(未定义)”类型的对象,表示一个未初始化的值,也就是还没有被分配的值。转为数值时为NaN
。
1 |
|
布尔值⁍
转换规则是除了下面六个值被转为false
,其他值都视为true
。
undefined
null
false
0
NaN
""
或''
(空字符串)
注意,空数组([]
)和空对象({}
)对应的布尔值,都是true
。
数值⁍
JavaScript 采用“遵循 IEEE 754 标准的双精度 64 位格式”(“double-precision 64-bit format IEEE 754 values”)表示数字。——在JavaScript(除了BigInt)当中, 并不存在整数/整型(Integer) 。[11]
1 |
|
JavaScript 浮点数的64个二进制位,从最左边开始,是这样组成的。[12]
- 第1位:符号位,
0
表示正数,1
表示负数 - 第2位到第12位(共11位):指数部分 (0到2047)
- 第13位到第64位(共52位):小数部分(即有效数字)
精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即-253到253,都可以精确表示。
简单的法则就是,JavaScript 对15位的十进制数都可以精确处理。
正向溢出与负向溢出⁍
JavaScript 能够表示的数值范围为21024到2-1023(开区间),超出这个范围的数无法表示。
1 |
|
JavaScript 提供Number
对象的MAX_VALUE
和MIN_VALUE
属性,返回可以表示的具体的最大值和最小值。
科学计数法/浮点数字面量⁍
1 |
|
浮点数字面值可以有以下的组成部分:
- 一个十进制整数,可以带正负号(即前缀“+”或“ - ”),
- 小数点(“.”),
- 小数部分(由一串十进制数表示),
- 指数部分。
1 |
|
1 |
|
整数进制字面量⁍
- 十进制:没有前导0的数值。
- 八进制:有前缀
0o
或0O
的数值,或者有前导0、且只用到0-7的八个阿拉伯数字的数值。 - 十六进制:有前缀
0x
或0X
的数值。 - 二进制:有前缀
0b
或0B
的数值。
特殊数值⁍
NaN
: Not a Number0/0
也是NaN
NaN
不等于任何值,包括它本身。NaN === NaN // false
NaN
与任何数(包括它自己)的运算,得到的都是NaN
。- 可以使用内置函数
isNaN()
来判断一个变量是否为NaN
Infinity
(正无穷)和-Infinity
(负无穷)Infinity
与NaN
比较,总是返回false
。- 四则运算,符合无穷的数学计算规则。
- 0乘以
Infinity
,返回NaN
;0除以Infinity
,返回0
;Infinity
除以0,返回Infinity
。- 与
null
计算时,null
会转成0
- 与
Infinity
减去或除以Infinity
,得到NaN
。- 内置函数
isFinite()
来判断一个变量是否是一个有穷数 (类型为Infinity
,-Infinity
或NaN
则返回false) Infinity
与undefined
计算,返回的都是NaN
。
- 正零
+0
和负零-0
- 区别就是64位浮点数表示法的符号位不同,几乎等价
- 唯一区别:
(1 / +0) === (1 / -0) // false
- 一个是
Infinity
一个是-Infinity
- 一个是
数值转换⁍
parseInt⁍
内置函数 parseInt()
将字符串转换为整型。该函数的第二个可选参数表示字符串所表示数字的基(进制)(2到36之间)如果第二个参数是0
、undefined
和null
,则直接忽略,返回十进制结果。
- 会自动删除头部空格
- 会自动将参数转换为字符串
- 遇到不能转换的字符则停止转换,返回前面已经转换的部分(如果有小数点,只能转换整数部分)(前提是已经是字符串格式)
- 如果第一个字符就不能转换,则返回
NaN
1 |
|
- 对于那些会自动转为科学计数法的数字,
parseInt
会将科学计数法的表示方法视为字符串,因此导致一些奇怪的结果
1 |
|
- 如果
parseInt
的第一个参数不是字符串,会被先转为字符串。这会导致一些令人意外的结果,,对于八进制的前缀0,尤其需要注意:
1 |
|
parseFloat⁍
会自动过滤字符串前导的空格
如果参数不是字符串,或者字符串的第一个字符不能转化为浮点数,则返回NaN
。
parseFloat
会将空字符串转为NaN
。
这些特点使得parseFloat
的转换结果不同于Number
函数。
1 |
|
一元加法运算符⁍
1 |
|
字符串⁍
- JavaScript 使用 Unicode 字符集。JavaScript 引擎内部,所有字符都用 Unicode 表示。
- 反斜杠后面跟八进制或十六进制可以表示Unicode码点
- JavaScript 原生提供两个 Base64 相关的方法。(将任意值转成 0~9、A~Z、a-z、
+
和/
这64个字符组成的可打印字符。)btoa()
:任意值转为 Base64 编码atob()
:Base64 编码转为原来的值
字符串也有一些方法:
1 |
|
模板字面量⁍
ES6中提供了一种模板字面量(template literals),模板字符串提供了一些语法糖来帮你构造字符串。
除此之外,你可以在通过模板字符串前添加一个tag来自定义模板字符串的解析过程,这可以用来防止注入攻击,或者用来建立基于字符串的高级数据抽象。[10:1]
此外模板字面量支持多行字符串的输入
如何放置注入攻击?
1 |
|
对象⁍
对象的创建⁍
有两种简单方法可以创建一个空对象:
1 |
|
和:
1 |
|
这两种方法在语义上是相同的。第二种更方便的方法叫作“对象字面量(object literal)”法。
读取对象属性⁍
读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。
注意,数值键名不能使用点运算符,只能使用方括号运算符。
1 |
|
- 支持链式访问
其他⁍
- 查看一个对象本身的所有属性,可以使用
Object.keys
方法。 delete
命令用于删除对象的属性,删除成功后返回true
。in
运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值)for...in
循环用来遍历一个对象的全部键。- 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性(
toString
属性)。 - 它不仅遍历对象自身的属性,还遍历继承的属性。
- 一般情况下,都是只想遍历对象自身的属性,所以使用
for...in
的时候,应该结合使用hasOwnProperty
方法,在循环内部判断一下,某个属性是否为对象自身的属性。
- 一般情况下,都是只想遍历对象自身的属性,所以使用
- 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性(
with
语句:操作同一个对象的多个属性时,提供一些书写的方便。
关于对象和原型的详情参见: Object.prototype. 解释对象原型和对象原型链可以参见:继承与原型链。
数组⁍
-
数组就是键值为下标的对象
-
清空数组的一个有效方法,就是将
length
属性设为0。
如果想在数组后追加元素,只需要:
1 |
|
也可以直接通过下标添加:
1 |
|
数组的空位⁍
当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位(hole)。
1 |
|
-
上面代码表明,数组的空位不影响
length
属性。虽然这个位置没有值,引擎依然认为这个位置是有效的。 -
数组的空位是可以读取的,返回
undefined
。- 但是给数组某一位赋值
undefined
不代表那一位为空,详见下面的示例
- 但是给数组某一位赋值
-
使用
delete a[i]
命令删除一个数组成员,会形成空位,并且不会影响length
属性。
遍历有空位的数组时,使用数组的forEach
方法、for...in
结构、以及Object.keys
方法进行遍历,空位都会被跳过。但是使用for...of
结构就不会。
1 |
|
方法名称 | 描述 |
---|---|
a.toString() |
返回一个包含数组中所有元素的字符串,每个元素通过逗号分隔。 |
a.toLocaleString() |
根据宿主环境的区域设置,返回一个包含数组中所有元素的字符串,每个元素通过逗号分隔。 |
a.concat(item1[, item2[, ...[, itemN]]]) |
返回一个数组,这个数组包含原先 a 和 item1、item2、……、itemN 中的所有元素。(和push不同的是,它可以添加多个数组形式的数,也可以直接添加多个数) |
a.join(sep) |
返回一个包含数组中所有元素的字符串,每个元素通过指定的 sep 分隔。 |
a.pop() |
删除并返回数组中的最后一个元素。 |
a.push(item1, ..., itemN) |
将 item1、item2、……、itemN 追加至数组 a 。 |
a.reverse() |
数组逆序(会更改原数组 a )。 |
a.shift() |
删除并返回数组中第一个元素。(相当于 出队 ) |
a.slice(start, end) |
返回子数组,以 a[start] 开头,以 a[end] 前一个元素结尾。 |
a.sort([cmpfn]) |
依据可选的比较函数 cmpfn 进行排序,如果未指定比较函数,则按字符顺序比较(即使被比较元素是数字)。 |
a.splice(start, delcount[, item1[, ...[, itemN]]]) |
从 start 开始,删除 delcount 个元素,然后插入所有的 item 。 |
a.unshift(item1[, item2[, ...[, itemN]]]) |
将 item 插入数组头部,返回数组新长度(考虑 undefined )。 |
控制、循环与迭代⁍
循环与迭代 - JavaScript | MDN (mozilla.org)
lable
语句⁍
JavaScript 语言允许,语句的前面有标签(label),相当于定位符,用于跳转到程序的任意位置,标签的格式如下。[8:1]
1 |
|
标签可以是任意的标识符,但不能是保留字,语句部分可以是任意语句。
标签通常与break
语句和continue
语句配合使用,跳出特定的循环。
1 |
|
for...in
语句⁍
for...in
语句循环一个指定的变量来循环一个对象所有可枚举的属性。
Js里不推荐使用for..in
来迭代数组,如果你想改变数组对象,比如添加属性或者方法,for…in 语句迭代的是自定义的属性,而不是数组的元素。[13]
这里注意和python的区别,迭代数组的话
for...of
语句,和forEach()
,是理想的选择。
for...of
语句⁍
在可迭代对象(包括Array
、Map
、Set
、arguments
等等)上创建了一个循环,对值的每一个独特属性调用一次迭代。
for...in
循环遍历的结果是数组元素的下标,而 for...of
遍历的结果是元素的值:
1 |
|
forEach()
⁍
面试官:有了 for 循环 为什么还要 forEach ? - 文章详情 (itpub.net)
Array.prototype.forEach() - JavaScript | MDN (mozilla.org)
1 |
|
异常处理⁍
详见[13:1]
1 |
|
如果finally
块返回一个值,该值会是整个try-catch-finally
流程的返回值,不管在try
和catch
块中语句返回了什么
《JavaScript权威指南》 ↩︎
《前端程序员面试笔试通关宝典》 ↩︎
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Grammar_and_types ↩︎ ↩︎
重新介绍 JavaScript(JS 教程) - JavaScript | MDN (mozilla.org) ↩︎ ↩︎