<script>
的加载和执行时机头等函数(first-class function)
一种编程语言被称为具有头等函数时,语言中的函数将会像任何其他变量一样被对待(如:函数可以作为参数传递给其他函数、可以作为返回值被返回、可以作为值赋值给变量)。
原型编程(prototype-based programming)
它的类(class)没有明确的定义,只是通过向其它的类中添加属性和方法来得到它,甚至偶尔使用空对象来创建类。
JS的面向对象是基于原型的面向对象(the prototype-based OO)。
引擎、编译器、作用域
JavaScript范围
ECMAScript是JavaScript的标准,狭义的JavaScript指ECMAScript。浏览器、Node.js、Deno、Bun、等都是JavaScript的运行时环境。
JavaScript = ECMAScript + 宿主环境提供的API。
浏览器(web应用)的JavaScript包括:
Web API:
Node.js的JavaScript包括:
操作系统的API:
用户界面
包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面之外,其他显示的各个部分都属于用户界面。
浏览器引擎
在用户界面和呈现引擎之间传送指令。
渲染引擎
也称为浏览器内核(web browser engine)、排版引擎(layout engine)或样板引擎,是一种软件组件,负责获取标记式内容(如:HTML、XML以及图像文件等)、整理信息(如CSS、XSL),并将排版后的内容输出至显示屏或打印机。
所有网页浏览器、电子邮件客户端以及其他需要根据表示性的标记语言来显示内容的应用程序,都需要排版引擎。
各浏览器使用的浏览器内核
1. IE:Trident。 2. Chrome:前WebKit,现Blink。 3. Firefox:Gecko。 4. Safari:WebKit。 5. Opera:前Presto,现Blink。 6. Edge:EdgeHTML。
网络
用于网络调用,如:HTTP请求。其接口与平台无关,并为所有平台提供底层实现。
用户界面后端
用于绘制基本的窗口小部件,如:组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
JS引擎(JS解释器、JS Engine、JS虚拟机)
- JS引擎提供了执行JavaScript的运行时环境。JS引擎都实现了Standard ECMA-262(ECMAScript:JavaScript使用的标准)。
- React Native、Node.js、Deno、Bun等都是通过扩展JS Engine, 在具备强大的本地资源和原生接口调用能力的同时,结合JavaScript丰富的库和社区和及其稳定的跨平台能力进行开发。
一个专门处理JS脚本的虚拟机,一般会附带在网页浏览器中。
各浏览器使用的JS引擎
1. JScript:ie8-,ASP。 2. Chakra:ie9+,Edge。 3. V8:Chrome,Opera,Node.js,MongoDB。 4. SpiderMonkey:Firefox。 5. Nitro:Safari。
引擎工作流程:
数据存储
这是持久层。浏览器需要在硬盘上保存各种数据,如:cookie。HTML5定义了「网络数据库」,这是一个完整(但是轻便)的浏览器内数据库。
参考:JavaScript - 预编译。
<script></script>
为范围,即每遇到一个代码块都会进行:预编译 -> 执行。预编译:在内存中开辟一块空间,用来存放变量和函数。
为使用var
声明的变量、使用function
声明的函数在内存中开辟一块空间,用来存放两者声明(不会赋值,所有变量的值都是undefined
、函数内容会被预编译);
const/let
不允许同名声明。
在预编译时,function
的优先级比var
高:
e.g.
```javascript // 预编译阶段a1/b2为函数,运行时a1/b2赋值成为变量 console.log(a1); // => ƒ a1() {} var a1 = 1; function a1() {} console.log(a1); // => 1 console.log(b2); // => ƒ b2() {} function b2() {} var b2 = 1; console.log(b2); // => 1 // 预编译阶段c3/d4为函数,运行时没有赋值 console.log(c3); // => ƒ c3() {} var c3; function c3() {} console.log(c3); // => ƒ c3() {} console.log(d4); // => ƒ c4() {} function d4() {} var d4; console.log(d4); // => ƒ c4() {} // 预编译阶段e5为变量,运行时被赋值给匿名函数 console.log(e5); // => undefined var e5 = function () {}; console.log(e5); // => ƒ () {}(匿名函数) ```
基本数据类型:
Undefined
、Null
、Boolean
、Number
、String
、Symbol
、BigInt
存放在栈内存(Stack),按值访问
基本类型是保存在栈内存中的简单数据段,它们的值都有固定的大小,保存在栈空间,通过按值访问,并由系统自动分配和自动释放。这样带来的好处就是,内存可以及时得到回收,相对于堆来说,更加容易管理内存空间。
不能添加新属性;基本包装类型的属性只能读取、不能修改。
引用数据类型:
Object
(由Object
原型链继承的其他所有引用数据类型,包括基本包装类型)
存放在堆内存(Heap),按指针访问
引用类型是保存在堆内存中的对象,值大小不固定,栈内存中存放的该对象的访问地址指向堆内存中的对象,JS不允许直接访问堆内存中的位置,因此操作对象时,实际操作对象的引用。
所有引用数据类型的实例,都可以添加新属性;已有属性的读取/修改,根据属性描述(数据属性/访问器属性)决定。
针对基本数据类型
Boolean
、Number
、String
、Symbol
、BigInt
(除了 、Undefined
)Null
作用
使JS的对象涵盖所有的值(除了 、Undefined
),允许基本数据类型转换为基本包装类型后方便地调用Object对象提供的方法(如:Null
valueOf
、toString
)。
基本数据类型与基本包装类型的自动转换
'abc'.length // 3
// abc是一个字符串,本身不是对象,不能调用length属性;
// JS引擎自动将其转为基本包装类型,在这个对象上调用length属性;
// 调用结束后,这个临时对象就会被销毁。
自动转换生成的基本包装类型是只读的,无法修改
var s = '' // s:基本数据类型
s.x = 1 // 自动转换后进行赋值操作,然后销毁这个基本包装类型的实例,
s.x // undefined。这时的自动转换生成的基本包装类型的实例,和上面那个已经销毁的实例不是同一个
在非严格模式,若是基本数据类型(除了undefined/null
之外)使用this替代设置this
,则this
成为那个值的基本包装类型。
e.g.
```javascript function A () { console.log(this) } A.call() // => 全局变量 A.call(undefined) // => 全局变量 A.call(null) // => 全局变量 A.call(1) // => Number的基本包装类型 A.apply(1) // => Number的基本包装类型 A.bind(1)() // => Number的基本包装类型 function B () { 'use strict' console.log(this) } B.call() // => undefined B.call(undefined) // => undefined B.call(null) // => null B.call(1) // => 1 B.apply(1) // => 1 B.bind(1)() // => 1 ```
引用数据类型
Object
、Array
、Function
、RegExp
的字面量和构造函数(或普通函数)创建的结果一致,都是引用数据类型。- 其他引用数据类型仅能够通过构造函数(或普通函数)方式创建,没有
字面量创建方式。创建方式区别:
字面量:
加载JS脚本后就编译(compilation),更好的性能。
构造函数(或普通函数):
运行JS脚本时编译(runtime compilation),仅在需要传参数情况下才使用此方式。
对象
e.g.
// ①对象字面量
var obj1 = {a: 'b'}
// ②构造函数实例化
// 基本包装类型:若参数是null或undefined,将会创建并返回一个空对象;否则,将返回一个与给定值对应类型的对象(基本包装类型)
new Object() // {}
new Object(null) // {}
new Object(undefined) // {}
new Object(1) // Number的基本包装类型,等价于:new Number(1)
new Object(true) // Boolean的基本包装类型,等价于:new Boolean(true)
new Object('str') // String的基本包装类型,等价于:new String('str')
new Object(Symbol()) // Symbol的基本包装类型
new Object(1n) // BigInt的基本包装类型
// 引用类型等于脱去new Object创建的对象
new Object({a: 'b'}) // {a: 'b'}
new Object(new Date()) // new Date()
// ③普通函数(与new的实例化方式返回结果一致)
Object()
数组
e.g.
// ①数组字面量
var arr1 = [2, 3]; // [2, 3]
// ②构造函数实例化
var arr2 = new Array(); // []
var arr3 = new Array(2); // [undefined, undefined](空位)
var arr4 = new Array(2, 3); // [2, 3]
// ③普通函数(与new的方式结果一致)
var arr5 = Array(); // []
var arr6 = Array(2); // [undefined, undefined](空位)
var arr7 = Array(2, 3); // [2, 3]
基本数据类型
有字面量方式和构造函数(或普通函数)方式:
Boolean
、Number
、String
有字面方法和普通函数方式:
BigInt
有普通函数方式:
Symbol
仅有字面量方式:
Undefined
、Null
字符串
e.g.
// ①字符串字面量
var str1 = 'string'; // 'string'
// ②普通函数(与字面量的方式结果一致)
var str2 = String('string'); // 'string'
// ③构造函数实例化
var str3 = new String('string'); // 基本包装类型,等价于:new Object('string')
console.log(typeof str1, str1 instanceof String); // => string false
console.log(typeof str2, str2 instanceof String); // => string false
console.log(typeof str3, str3 instanceof String); // => object true
console.log(str1 === str2, str2 === str3); // => true false
2进制表示:
0b二进制数
、0B二进制数
8进制表示:
0o八进制数
、0O八进制数
0八进制数
(不推荐)
16进制表示:
0x十六进制数
、0X十六进制数
JS内部会自动将8进制、16进制、2进制转为10进制。
不同进制数互相转换、用
Number(字符串)
将带进制数前缀的字符串转为10进制数。
决定了表达式中运算执行的先后顺序。优先级高的运算符会作为优先级低的运算符的操作数。
无论优先级如何都不会执行短路求值后不需要执行的部分。
短路求值(Short-circuit evaluation、minimal evaluation、McCarthy evaluation、最小化求值),是一种逻辑运算符的求值策略:只有当第一个运算数的值无法确定逻辑运算的结果时,才对第二个运算数进行求值。例如,当AND的第一个运算数的值为false时,其结果必定为false;当OR的第一个运算数为true时,最后结果必定为true,在这种情况下,就不需要知道第二个运算数的具体值。在一些语言中(如Lisp),默认的逻辑运算符就是短路运算符,而在另一些语言中(如Java,Ada),短路和非短路的运算符都存在。
e.g. 在表达式a && (b++)
中,若a
为假时(falsy),则即使(b++)
在圆括号中,也不会被求值、不会被执行到。
解析器会尽量将新行并入当前行,当且仅当符合ASI规则时才会将新行视为独立的语句:
;
空语句var
语句do-while
语句(不是while
)continue
语句break
语句return
语句throw
语句前置分号策略:只要判断行首字符为:
[
(
+
-
/
`
之一,就在其前面增加分号。
一种新的语法从提案到变成正式标准,需要经历五个阶段:
一个提案只要能进入Stage 2,就差不多会包括在以后的正式标准(Standard)里面。
综合了多项技术的浏览器端网页开发技术,能在不刷新整个页面的前提下维护数据。
XMLHttpRequest
或Fetch
与网页服务器进行异步数据交换。XMLHttpRequest
fetch
navigator.sendBeacon
产生原因
这个方法主要用于满足统计和诊断代码的需要,这些代码通常尝试在卸载(unload)文档之前向Web服务器发送数据。过早的发送数据可能导致错过收集数据的机会。然而,对于开发者来说保证在文档卸载期间发送数据一直是一个困难。因为用户代理通常会忽略在unload事件处理器中产生的异步XMLHttpRequest。 - 过去,为了解决这个问题, 统计和诊断代码通常要在: 1. 发起一个同步XMLHttpRequest来发送数据。 2. 创建一个``元素并设置src,大部分用户代理会延迟卸载(unload)文档以加载图像。 3. 创建一个几秒的no-op循环。 上述的所有方法都会迫使用户代理延迟卸载文档,并使得下一个导航出现的更晚。下一个页面对于这种较差的载入表现无能为力。 这就是sendBeacon()方法存在的意义。使用sendBeacon()方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能,这意味着:数据发送是可靠的、数据异步传输、不影响下一导航的载入。
unload
状态下也可以异步发送,不阻塞页面刷新/跳转等操作在互联网中发现、搜集、下载网页信息
搜索引擎蜘蛛
判断:
只能用userAgent判断。各大厂的蜘蛛有严格的行为规范和固定的userAgent。
大部分蜘蛛:不会去执行任何JS内容、完全忽略Flash。
Googlebot可以获取IntersectionObserver API滚动加载的内容(无法
scroll、其他交互)。
抓取互联网上的网页:顺着当前网页上的超链接到达另一个网页,不断进行。
对信息进行提取和组织建立索引库
分词
。倒排索引(inverted index)
:从关键词
匹配到文档、文档中位置、次数
等信息的索引库。检索器根据用户输入的查询内容,在索引库检出文档,对将要输出的结果进行排序,最后将查询结果返回用户
SEO(search engine optimization,搜索引擎优化):利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名。
站内SEO
添加信息:
<title>标题</title>
<meta name="keywords" content="关键字">
、<meta name="description" content="描述">
关键字密度和关联度。
<a>
添加href
属性,用JS事件绑定去拦截浏览器默认行为。<img>
添加alt
;<a>
添加title
。<h1>
。SEO相关内容:进行服务端渲染、不要在客户端异步加载并渲染
用户推荐相关的,不要做SEO,因此不要把千人千面的推荐内容提交给蜘蛛(若要对推荐内容做SSR,可以单独用userAgent判断蜘蛛而特殊对待)。
lazyload的SEO(lazyload大部分时候和SEO相违背)
<noscript><img src="真实地址"></noscript>
来针对蜘蛛。大部分蜘蛛:不会去执行任何JS内容、完全忽略Flash。
Googlebot可以获取IntersectionObserver API滚动加载的内容(无法
scroll、其他交互)。
站外SEO
添加各具体搜索引擎提供的SEO配置,使用各搜索引擎后台监控SEO效果
主动推送网站各页面链接,主动上传sitemap文件,加入各种搜索引擎的认证。
站点根目录放置robots.txt
robots.txt
可以设置:允许/拦截某种机器人、禁止爬取目录、sitemap
路径、Crawl-delay指令、等。
外部链接的导入、导出,影响网页级别(PageRank)
<a>
中添加rel="nofollow"
。<link rel="canonical" href="网址">
整合重复的网址URL相关:
垃圾回收器会按照固定的时间间隔(或代码执行中预定的时间)周期性地执行,找出不再继续使用的变量,然后释放其占用的内存。
垃圾回收器必须跟踪并判断变量是否有用,对于不再有用的变量打上标记,以备将来回收。
标记清除(mark-and-sweep)(现代浏览器使用方式)
垃圾回收器在运行时给存储在内存中的所有变量加上标记;然后,去掉环境中的变量以及被环境中变量引用的变量的标记;最后,对那些带标记的值进行释放。
引用计数(reference counting)
跟踪记录每个值被引用的次数,被引用一次加1,引用取消就减1,当引用次数为0时,则说明没有办法再访问这个值了,当垃圾回收器下次运行时,释放引用次数为0的值所占空间。
可能产生一个严重的问题:循环引用,引用次数永远不会是0。
利于垃圾回收的编码方式:
- 用
变量 = null
等方法,让变量成为零引用,从而进行清除元素、垃圾回收(导致内存泄露的情况除外)。- 块级作用域中的变量,当块级作用域执行结束时,就可以进行垃圾回收。因此为变量显式声明块级作用域并把变量绑定在其中,有利于垃圾回收、代码性能。
数组的空位:数组的某一个位置没有任何值
undefined
。undefined
,依然有值。delete
删除一个数组成员,会形成空位,并且不会影响length
属性。length
属性赋予大于其长度的值,新创建的项都是空位。length + 1
的项,会在中间产生空位。in
)。
new Array(数量)
(或Array(数量)
)返回的是有空位的稀疏数组。Array.apply(null, new Array(数量));
返回的是没有空位的密集数组。- 若数组最后一个元素后面有逗号,并不会产生空位,而是忽略这个逗号:
[1, ].length === 1
。
ES5和ES6对处理空位的区别:
ES5大多数情况下会忽略空位:
forEach
、filter
、every
、some
、reduce
、reduceRight
等遍历方法的回调函数会跳过空位;map
的回调函数会跳过空位,但返回值保留空位。join
、toString
将空位
、undefined
、null
处理成空字符串''
。ES6明确将空位转为undefined
。
意味着用ES6语法遍历带空位的数组,也会对是空位的项进行回调函数执行,空位的项的值为undefined
。
伪协议(自定义协议):操作系统提供支持的、为关联应用程序而使用的、在标准协议(
http
、https
、ftp
等)之外的,一种协议(mailto
、tel
、file
、data
、自定义URL Scheme
等)。
;
分割执行语句)是String
类型,则返回给当前页面替换原页面内容(允许任何HTML标签)<a>
、<iframe>
、<img>
的src
属性,window.location.href
,window.open
从结构上看,所有的数据(data)最终都可以分解成三种类型:
标量(scalar)
单独的字符串(
String
)或数字(Number
)或其他值(Boolean
、Null
等)。序列(sequence)
又称为:数组(array)、列表(list)。
若干个相关的数据按照一定顺序排序在一起。
映射(mapping)
又称为:散列(hash)、字典(dictionary)
键-值。
JSON(JavaScript Object Notation)是一种数据交换格式
定义明确简单
易于人阅读和编写、也易于机器解析和生成。
采用完全独立于语言的文本格式
能够跨编程语言传输数据。
因为传递的是文本格式,所以无法传递byte类型数据(二进制文件,如:图片)。
JSON的数据类型
来自:www.json.org。
作者-道格拉斯·克罗克福特(Douglas Crockford)-设计的JSON实际上是JavaScript的一个子集,并且声称JSON的规格永远不必升级,因为该规定的都规定了。
Object
注意:键名必须使用双引号
"
;不能增加额外的逗号,
。
Array
注意:不能增加额外的逗号
,
。
String
注意:字符串必须使用双引号
"
。
Number
注意:必须是十进制;浮点数不能省略小数点前的
0
(错误:;正确:.1
0.1
)。
true
、false
、 null
不支持JS的其他基本数据类型:
、Undefined
、Symbol
。BigInt
以上数据类型在键值
的任意组合、嵌套
JSON的发展
<script>
的加载和执行时机没有defer
或async
:
立即加载并执行(同步),阻塞解析HTML。
defer
:
异步加载,在DOM解析完成后、DOMContentLoaded
触发前执行,顺序执行。
(浏览器兼容性)多个
defer
脚本不一定按照顺序执行,也不一定会在DOMContentLoaded
事件触发前执行,因此最好只包含一个延迟脚本。
async
:
异步加载,加载完马上执行,不影响DOMContentLoaded
触发时机。(乱序执行,仅适用于不考虑依赖、不操作DOM的脚本)
动态创建的
<script>
默认是async
(可以手动设置dom.async = false
)。
模块化属性:
type="module"
:与defer
相同。type="module" async
:与async
相同。按从上到下顺序解析页面内容,针对<script>
(包括动态创建和修改src
):
defer
或async
的<script>
。defer
或async
的<script>
;defer
或async
的<script>
或修改<script>
的src
,(无论是原本就存在的、还是动态加载的)异步加载、不确定顺序执行。判断JS、CSS文件是否加载完毕
1. JS 1. 监听文件的`load`事件,触发则加载完成。 2. 监听JS文件的`readystatechange`事件(大部分浏览器只有`document`能够触发),当文件的`readyState`值为`loaded/complete`则JS加载完成。 2. CSS 1. 监听文件的`load`事件,触发则加载完成。 2. 轮询CSS文件的`cssRules`属性是否存在,当存在则CSS加载完成。 3. 写一个特殊样式,轮询判断这个样式是否出现,来判断CSS加载完成。
cookie free
cookie是同源(且同路径),不同域名可以避免某些静态资源携带不必要的cookie而占用带宽。
浏览器对同一域名有HTTP并发数限制
动静分离,静态资源方便做CDN
将网站静态资源(HTML、JS、CSS、图片、字体、多媒体资源等)与后台应用(API)分开部署。
缺点:
优点