1、发展史
5大主流浏览器
名称 | 内核 |
---|---|
IE | trident |
chrome | webkit blink |
safari | webkit |
firefox | gecko |
opera | presto |
历史事件
2、js相关概念
ECMA
European Computer Manufacturers Association(欧洲计算机制造联合会)
评估、开发、认可 电信、计算机标准
ECMA - 262 (标准清单的第262行) 脚本语言的规范及标准
像 es5、6、7…都是对脚本语言制定的规范
编译型与解释型语言
编译型:源码 -> 编译器 -> 机器语言 -> 可执行文件
解释型:源码 ->解释器 -> 解析一行,执行一行
解释型语言不需要根据不同平台进行移植,只要有解释器就能执行
编译型语言需要特定的编译环境,但运行速度比解释型语言快一些,适用于大型程序,大量数据处理等场景
JAVA类似于混合型
.java -> javac -> .class -> JVM解释执行
脚本语言
-> 脚本引擎 -> 解释器
javaScript、Jscript、vbscript 客户端脚本
php 服务端脚本
单线程多线程
js引擎是单线程
模拟多线程:通过轮转时间片
将多个任务,切分成多个任务碎片,然后随机排列组成队列,按照这个队列将任务碎片送进JS进程,再按顺序执行任务碎片(多个任务之间反复横跳)
获取全局对象方式
1 | //浏览器 web |
this指向
函数中this的指向,谁调用方法,函数默认 this 就指向谁
严格模式下 函数默认 this 为 undefined
1、隐式绑定this,也就是全局this->window
2、预编译函数this->window
3、显示绑定:谁调用this指向谁
4、apply/call 强绑定修改this
5、箭头函数this指向父级this
6、构造函数this指向实例化对象
1 | function test () { |
(1)第一段代码,在foo()函数体内采用严格模式并使用了this.a,this会指向undefined;第二段代码foo()在严格模式下调用,this不会指向undefined
(2)决定 this 绑定对象的并不是调用位置是否处于严格模式,而是函数体是否处于严格模式。如果函数体处于严格模式,this 会被绑定到 undefined,否则this 会被绑定到全局对象
1 | // 片段1 |
call 、apply、bind
1 | //call 多个参数依次传递 |
获取窗口大小
1 | let pageWidth = window.innerWidth, |
地址栏参数处理
1 | // URLSearchParams |
web (World wide web)
1 | web(World Wide Web)即全球广域网,也称为万维网,它是一种基于超文本和HTTP的、全球性的、动态交互的、跨平台的分布式图形信息系统。 |
web开发概念
1 | b/s(浏览器和服务器) 就是 web开发 |
H5
1 | 时代迫切要求一个统一的互联网通用标准。HTML之前,由于个浏览器之间的不统一,光是修改Web浏览器之间兼容性而引起的bug就浪费了大量时间。而H5的目标就是将Web带入一个成熟的应用平台,在html5平台上,视频,音频,图像,动画,一级同电脑交互都被标准化。 |
旧时代的网页三剑客
1 | -fireworks |
面向对象 OOP
面向对象的三大特性:继承、多态、
1 | 继承 :子类继承父类的属性和方法,通过extends关键字 |
面向对象/面向过程编程
1 | 面向过程 |
高阶函数
定义:接收一个函数作为参数,或返回一个函数 的函数 称之为高阶函数
高阶函数就是操作函数的函数,它接收一个或多个函数作为参数并返回一个新函数(犀牛书)
例如:数组的map,reduce方法,就是高阶函数
柯里化
将一个接收多个参数的函数,转化为接收一个参数的函数
1 | 优点:、、 |
闭包
匿名函数与IIFE(自执行函数)不是闭包。
定义:能够读取到函数内部变量的函数
优点:保持对函数内部变量的引用,避免被回收机制回收
缺点:IE中可能导致内存泄漏。所以在退出函数前要将不使用的局部变量删除
立即执行函数
IIFE .除了使用括号,还可以使用 + - = !等运算符
(()=>{}() )
(()=>{})()
!()=>{}()
优点:构建一个新的函数作用域,避免全局污染
纯函数
1 | 简单来说,一个函数的返回值只依赖于它的参数,并且执行过程中没有任何副作用。 |
惰性函数
1 | 惰性函数执行时的分支,只会在第一次调用时执行。之后会被进入的分支中的代码所代替 |
记忆函数
1 | 通过缓存计算结果的方式来减少计算的次数,以达到提升性能的结果 |
深浅拷贝
1 | 对于js的原始数据类型的拷贝都是深拷贝 |
节流防抖
- 防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。**代码实现重在清零
clearTimeout
**。防抖可以比作等电梯,只要有一个人进来,就需要再等一会儿。业务场景有避免登录按钮多次点击的重复提交。- 节流:控制流量,单位时间内事件只能触发一次,与服务器端的限流 (Rate Limit) 类似。**代码实现重在开锁关锁
timer=timeout; timer=null
**。节流可以比作过红绿灯,每等一个红灯时间就可以过一批。
1 | 防抖 |
AOP在JS中的实现
1 | 在程序中时常用到的某些程序步骤、阶段、片段抽离出来,与陈旭本身的程序逻辑抽离 |
二进制算法
1 | 2^7 2^6 2^5 2^4 2^3 2^2 2^1 2^0 |
桶排序
1 | var arr = [3,1,9,13,3,2,4,5]; |
睡眠排序
1 | var arr=[400,200,700,500,600,0,800]; |
TreeShaking
1 | 他是webpack自带的一个功能,在打包代码时,会将没有使用的模块,或者变量之类的代码。自动过滤掉。不打包,以减少打包代码大小。就像一棵树将身上枯萎的树叶摇晃掉 |
AST
1 | 抽象语法树(类似与html的DOM树,ast就是描述js的vdom树) |
Virtural Dom
1 | 虚拟DOM:它并不是真正意义上的DOM,而是一个轻量级的JavaScript 对象,在状态发生变化时,Virtual Dom会进行Diff运算,来更新只需要被替换的Don,而不是全部,而不是全部重绘。运行过程如下图 |
js运行机制
1 | 1、堆 |
字符串编码
最早出现的编码表是美国的ASCII码表【采用一个字节编码】,但支持数字英文和符号。
随着计算机发展,后来出现了一种编码放啊是,是国际化标准组织ISO制定的,这种编码方式支持西欧语言,向上兼容 ACSCII码,但不支持中文。这种编码方式为:ISO-8859-1【latain-1】
随着计算机向亚洲发展,计算机开始支持中文,日文,韩文等国家文字,其中支持中文的编码方式:
BG2312 < GBK < GB18030
支持繁体中文:大五码 big5
后来出现了一种编码方式统一了全球所有文字,容量较大,这种编码方式叫做:Unicode编码,它有很多具体的实现方式:
- UTF-8
- UTF-16
- UTF-32
- …
1 | 1、ASCII 码 |
Object与Function关系
1 | Object、Function和其它对象的关系可以归纳为下面四点: |
文件格式转化 Base64,FIle,Blob
1 | // 文件类型转base64 ----主要应用场景:图片预览 |
HTML 的实体编码
HTML 实体是一段以连字号(&)开头、以分号(;)结尾的字符串。用以显示不可见字符及保留字符 (如 HTML 标签)
在前端,一般为了避免 XSS 攻击,会将
<>
编码为<
与>
,这些就是 HTML 实体编码。
- 不可分的空格:&nbsp;
- <(小于符号):&lt;
- (大于符号):&gt;
- &(与符号):&amp;
- ″(双引号):&quot;
- ‘(单引号):’&apos;
- ……
在 HTML 转义时,仅仅只需要对六个字符进行编码: &
, <
, >
, "
, '
, ```。可使用 he (opens new window)这个库进行编码及转义
1 | // 实体编码 |
什么是 Data URL
Data URL 是前缀为
data:
协议的 URL; 它允许内容创建者向文档中嵌入小文件,比如图片等。 Data URL 由四部分组成:
- 前缀
data:
- 指示数据类型的 MIME 类型。例如
image/jpeg
表示 JPEG 图像文件;如果此部分被省略,则默认值为text/plain;charset=US-SACII
- 如果为非文本数据,则可选 base64 做标记
- 数据
1 | data:[mediatype][;base64], data |
缺陷:
- base64 编码后的图片会比原来的体积大三分之一左右。
- Data URL 形式的图片不会缓存下来,每次访问页面都要被下载一次。可以将 Data URL 写入到 CSS 文件中随着 CSS 被缓存下来。
计算首屏、白屏时间
1 | 白屏时间: |
重绘、重排
重排和重绘是**关键渲染路径**中的两步
- 重排(Reflow):元素的位置发生变动时发生重排,也叫回流。此时在关键渲染路径中的 Layout 阶段,计算每一个元素在设备视口内的确切位置和大小。当一个元素位置发生变化时,其父元素及其后边的元素位置都可能发生变化,代价极高
- 重绘(Repaint): 元素的样式发生变动,但是位置没有改变。此时在关键渲染路径中的 Paint 阶段,将渲染树中的每个节点转换成屏幕上的实际像素,这一步通常称为绘制或栅格化
另外,重排必定会造成重绘。以下是避免过多重拍重绘的方法
- 使用
DocumentFragment
进行 DOM 操作,不过现在原生操作很少也基本上用不到- CSS 样式尽量批量修改
- 避免使用 table 布局
- 为元素提前设置好高宽,不因多次渲染改变位置
cookie
根据同源策略,cookie 是区分端口的,但是浏览器实现来说,“cookie 区分域,而不区分端口,也就是说,同一个 ip 下的多个端口下的 cookie 是共享的!
所以:localhost:3000 与 localhost:5000 的 cookie 信息共享
属性:
- Domain
- Path
- Expire/MaxAge
- HttpOnly
- Secure
- SameSite
CSRF
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
- 使用 JSON API。当进行 CSRF 攻击时,请求体通过
<form>
构建,请求头为application/www-form-urlencoded
。它难以发送 JSON 数据被服务器所理解。 - CSRF Token。生成一个随机的 token,切勿放在 cookie 中,每次请求手动携带该 token 进行校验。
- SameSite Cookie。设置为 Lax 或者 Strict,禁止发送第三方 Cookie。
CSP
CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。
两种方法可以启用 CSP:
通过 HTTP 头信息的
Content-Security-Policy
的字段1
2Content-Security-Policy: script-src 'self'; object-src 'none';
style-src cdn.example.org third-party.org; child-src https:网页的
<meta>
标签1
2
3
4
5
6
7
8
9
10<meta
http-equiv="Content-Security-Policy"
content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">
脚本:只信任当前域名
<object>标签:不信任任何URL,即不加载任何资源
样式表:只信任cdn.example.org和third-party.org
框架(frame):必须使用HTTPS协议加载
其他资源:没有限制
跨域
1 | 出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port) |
MVC
1 | 经典MVC模式中,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。其中,View的定义比较清晰,就是用户界面。 |
MVP
1 | 全称:Model-View-Presenter ;MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。 |
MVVM
1 | 简单的讲,MVVM是MVC的改进版。我们都知道MVC软件架构模式是苹果推荐的开发模式。 |
1 | 所以为了解决这个问题,MVVM就闪亮登场了。他把View和Contrller都放在了View层(相当于把Controller一部分逻辑抽离了出来),Model层依然是服务端返回的数据模型。而ViewModel充当了一个UI适配器的角色,也就是说View中每个UI元素都应该在ViewModel找到与之对应的属性。除此之外,从Controller抽离出来的与UI有关的逻辑都放在了ViewModel中,这样就减轻了Controller的负担 |
数组的坍塌
1 | 数组坍塌是什么呢? |
HLS与RHS 操作
1 | LHS 和 RHS 的含义是“赋值操作的左侧或右侧”并不一定意味着就是“= 赋值操作符的左侧或右侧”。 |
解构赋值重命名操作
1 | const obj = {a: 1, b: 2, c: 3}; |
空值合并运算符
1 | 如果操作符左边是null,undefined,会返回操作符右边的值否则返回左边的值 |
可选链式操作符
1 | const obj = { a: [1, 2], b() {} }; |
IIFE(立即调用函数表达式)
1 | IIFE( 立即调用函数表达式)是一个在定义时就会立即执行的 JavaScript 函数。 |
JSON序列化与解析
1 | // json -> object |
LRUCache
LruCache算法,又称为近期最少使用算法。
缓存最近的少量的记录,超出则移出近期最少使用的数据
1 | class LRUCache { |
js 代码压缩 minify 的原理是什么
我们知道
javascript
代码经压缩 (uglify) 后,可以使体积变得更小,那它代码压缩的原理是什么。如果你来做这么一个功能的话,你会怎么去压缩一段
js
代码的体积
uglify 包里有 ast.js 所以它一定是生成了抽象语法树 接着遍历语法树并作出优化,像是替换语法树中的变量,变成a,b,c那样的看不出意义的变量名。还有把 if/else 合并成三元运算符等。 最后输出代码的时候,全都输出成一行。
通过 AST 分析,根据选项配置一些策略,来生成一颗更小体积的 AST 并生成代码。
目前前端工程化中使用 terser (opens new window)和 swc (opens new window)进行 JS 代码压缩,他们拥有相同的 API。
常见用以压缩 AST 的几种方案如下:
- 去除多余字符: 空格,换行及注释
1
2
3
4
5
6
7
8// 压缩前
// 对两个数求和
function sum (a, b) {
return a + b;
}
// 压缩后
function sum(a,b){return a+b}压缩变量名:变量名,函数名及属性名
1
2
3
4
5
6
7
8
9
10
11
12function sum (first, second) {
return first + second;
}
// 压缩: 缩短变量名
function sum (x, y) {
return x + y;
}
// 再压缩: 去除空余字符
function s(x,y){return x+y}解析程序逻辑:合并声明以及布尔值简化
1
2
3
4
5
6
7
8
9
10
11
12// 压缩前
const a = 3;
const b = 4;
// 压缩后
const a = 3, b = 4;
// 压缩前
!b && !c && !d && !e
// 压缩后
!(b||c||d||e)解析程序逻辑: 编译预计算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// 压缩前
const ONE_YEAR = 365 * 24 * 60 * 60
// 压缩后
const ONE_YAAR = 31536000
// 压缩前
function hello () {
console.log('hello, world')
}
hello()
// 压缩后
console.log('hello, world')…
toJSON方法
1 | Object.prototype实际上并未定义toJSON()方法,但JSON.stringify()方法会从要序列化的对象上寻找toJSON()方法。如果要序列化的对象上存在这个方法,就会调用它,然后序列化该方法的返回值,而不是原始对象。 |
js静态导入、动态导入js模块
1 | 1、静态导入 |
import.meta.url
1 | import.meta.url的主要使用场景是引用与模块位于同一(或相对)目录下的图片、数据文件或其他资源。使用URL()构造函数可以非常方便地相对于import.meta.url这样的绝对URL来解析相对URL。例如,假设你要写一个模块,其中包含需要本地化的字符串,而相关的本地化文件保存在l10n/目录下,这个目录也保存着模块本身。 |
自定义错误子类型
1 |
|
Intl 对象 详情MDN文档
1 | Intl 对象是 ECMAScript 国际化 API 的一个命名空间,它提供了精确的字符串对比、数字格式化,和日期时间格式化。Collator,NumberFormat 和 DateTimeFormat 对象的构造函数是 Intl 对象的属性。 |
URL API
1 | 1、URL类可以解析URL,同时允许修改已有的URL(如添加搜索参数或修改路径),还可以正确处理对不同URL组件的转义和反转义。 |
公认符号 Symbol
1 | 1、Symbol.hasInstance 与 instanceof 组合使用 |
客户端 JavaScript 的线程模型
1 | JavaScript是单线程的语言,而单线程执行让编程更容易:你可以保证自己写的两个事件处理程序永远不会同时运行。在操作文档内容时,你敢肯定不会有别的线程会同时去修改它。而且,在写JavaScript代码时,你永远不需要关心锁、死锁或者资源争用。 |
客户端JavaScript时间线
1 | 1、浏览器创建Document对象并开始解析网页,随着对HTML元素及其文本内容的解析,不断向文档中添加Element对象和Text节点。此时,document.readyState属性的值是“loading”。 |
web安全模型
1 | 由于网页可以在你的私人设备上执行任意JavaScript代码,因此存在明显的安全隐患。浏览器厂商一直在努力平衡两个相互制约的目标: |
sandbox
1 | <iframe sandbox="value"> |
BigInt
1 |
|
sort排序
1 | 可以使用localeCompare() 方法来实现中文按照拼音排序,方法相当简单 |
数组扁平化
1 | ES6 |
页面显示隐藏
1 | 1、pageshow/pagehide 会在页面跳转前后触发 |
路由模式
1 | 扩展 |
浏览器的缓存机制
1 | // |
浏览器指纹
什么是浏览器指纹?
我们常说的指纹,都是指人们手指上的指纹,因具有唯一性,所以可以被用来标识一个人的唯一身份。而浏览器指纹是指仅通过浏览器的各种信息,如CPU核心数、显卡信息、系统字体、屏幕分辨率、浏览器插件等组合成的一个字符串,就能近乎绝对定位一个用户,就算使用浏览器的隐私窗口模式,也无法避免。
实现浏览器指纹的技术有哪些?
1、基本指纹
浏览器基本指纹是任何浏览器都具有的特征标识,比如屏幕分辨率、硬件类型、操作系统、用户代理(User agent)、系统字体、语言、浏览器插件 、浏览器扩展、浏览器设置 、时区差等众多信息,这些指纹信息“类似”人类的身高、年龄等,有很大的冲突概率,只能作为辅助识别。可以在该网址进行查看本地浏览器的基本特征,https://www.whatismybrowser.com/
2、高级指纹
浏览器高级指纹与基本指纹的区别是,基本指纹就像是人的外貌特征,外貌可以用男女、身高、体重区分,然而这些特征不能对某个人进行唯一性标识,仅使用基本指纹也无法对客户端进行唯一性判定,基于HTML5的诸多高级功能就能生成高级指纹了。
- Canvas指纹
Canvas(画布)是HTML5中一种动态绘图的标签,可以使用其生成甚至处理高级图片
原理大致如下:
相同的HTMLCanvasElement元素绘制操作,在不同操作系统、不同浏览器上,产生的图片内容不完全相同。在图片格式上,不同浏览器使用了不同的图形处理引擎、不同的图片导出选项、不同的默认压缩级别等。在像素级别来看,操作系统各自使用了不同的设置和算法来进行抗锯齿和子像素渲染操作。即使相同的绘图操作,产生的图片数据的CRC检验也不相同。Canvas几乎已被所有主流浏览器支持,可以通过大部分的PC、平板、智能手机访问。
- WebGL指纹
通过HTMLCanvasElement元素可以获取到Webgl对象(canvas.getContext(“webgl”)),通过此对象可以获取到用户的硬件信息,比如显卡名称、显卡型号、显卡制造商等,比如:ANGLE (NVIDIA GeForce GTX 1050 Ti Direct3D11 vs_5_0 ps_5_0),Google Inc.。
由于硬件一般是不会随意更换的,有些是电脑买来到电脑报废就没更换过硬件,电脑硬件种类也比较多,虽然非常大的碰撞率,但是依然可以被用来当做用户指纹的一部分,收集用户的信息也多,就越能代表用户的唯一指纹,这点不可忽视
- AudioContext指纹
HTML5提供给JavaScript编程用的Audio API则让开发者有能力在代码中直接操作原始的音频流数据,对其进行任意生成、加工、再造,诸如提高音色,改变音调,音频分割等多种操作,甚至可称为网页版的Adobe Audition。
AudioContext指纹原理大致如下:
方法一:生成音频信息流(三角波),对其进行FFT变换,计算SHA值作为指纹。
方法二:生成音频信息流(正弦波),进行动态压缩处理,计算MD5值。
两种方法都是在音频输出到音频设备之前进行清除,用户根本就毫无察觉就被获取了指纹。
AudioContext指纹基本原理:
主机或浏览器硬件或软件的细微差别,导致音频信号的处理上的差异,相同器上的同款浏览器产生相同的音频输出,不同机器或不同浏览器产生的音频输出会存在差异。
从上可以看出AudioContext和Canvas指纹原理很类似,都是利用硬件或软件的差异,前者生成音频,后者生成图片,然后计算得到不同哈希值来作为标识。音频指纹测试地址:https://audiofingerprint.openwpm.com/
- WebRTC指纹
WebRTC(网页实时通信,Web Real Time Communication),是可以让浏览器有音视频实时通信的能力,它提供了三个主要的API来让JS可以实时获取和交换音视频数据,MediaStream、RTCPeerConnection和RTCDataChannel。当然如果要使用WebRTC获得通信能力,用户的真实ip就得暴露出来(NAT穿透),所以RTCPeerConnection就提供了这样的API,直接使用JS就可以拿到用户的IP地址。用户的内网IP地址也是大多数情况下不会改变,所以也是可以用来当做用户指纹的其中一个因子。
如何防止被生成 用户指纹
…
js 中在 new 的时候发生了什么
- 创建了一个新对象
- 链接到原型
- 绑定 this 指向
- 返回这个对象
1 | function _new() { |
JSBridge
JSBridge 就是 JavaScript 和 Native(IOS/Android)之间的桥梁,提供两者相互调用的能力
生成随机字符串
1 | 1、方式1 |
Number.isNaN 与 isNaN
确定传递的值是否为
NaN
全局的 isNaN 方法 会将不是Number 类型的数 转化为 Number 类型再判断
es5的 Number.isNaN 不会对传入值 进行 类型转换,只对 NaN 返回 true
1 | isNaN(NaN) |
多版本chrome浏览器切换
图片防盗链原理
防盗链,就是防有人盗用你的链接。别人在他的网站上引用了你的资源 (图片,音频),这样就会浪费你的流量,资源被引用的多了起来,你这边的服务器可能就扛不住挂了,你说这是多么悲哀的事情!
实现图片防盗链:
通过获取请求头的 Host(请求的主机) 和 Referer(来源) 两个参数。如果 Host和Referer 值不相同说明是其他地方引用了你的视图资源,可以 返回一张默认图片
1 | let fs = require('fs'); |
什么是浏览器的关键渲染路径
DOM
生成 DOM 会从远程下载 Byte,并根据相应的编码 (如
utf8
) 转化为字符串,通过 AST 解析为 Token,生成 Node 及最后的 DOM。CSSOM
当解析 CSS 文件时,最终会生成 CSSOM
Render Tree
DOM 与 CSSOM 会一起生成 Render Tree,只包含渲染网页所需的节点。
Layout
计算每一个元素在设备视口内的确切位置和大小
Paint
将渲染树中的每个节点转换成屏幕上的实际像素,这一步通常称为绘制或栅格化
什么是安全正则
有些正则可能导致死机
1
2
3
4
5 const safe = require("safe-regex");
const re = /(x+x+)+y/;
// 能跑死 CPU 的一个正则
re.test("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
safe-regex 用于检测一个正则是否安全
1 | const safe = require("safe-regex"); |
在网站开发中,常见的跨域问题。
什么是跨域?
- 不同域名属于跨域,如:www.a.com 和www.b.com,另外www.a.com 和www.a.com.cn也属于不同域名
- 主域名和子域名(二级域名、三级域名等)跨域,如:www.a.com 和 bbs.a.com 跨域
- 不同协议属于跨域,如:http://www.a.com 和 https://www.a.com
- Ip和域名属于跨域,如:123.23.23.12 和 www.a.com
浏览器中的跨域行为有哪些
浏览器中的同源策略主要影响的是通过脚本(如JavaScript)发起的跨域请求和数据访问
如:
XMLHttpRequest 和 Fetch API 请求:
当你使用 JavaScript 中的 XMLHttpRequest 或 Fetch API 发起 HTTP 请求时,如果请求的 URL 与当前页面的源(协议、域名、端口)不同,浏览器会阻止这个请求,这就是同源策略对 AJAX 请求的限制。
DOM 操作:
如果你尝试从一个不同源的 iframe 或 window 对象中访问或修改 DOM,浏览器会阻止这种操作。例如,你不能通过 JavaScript 读取另一个域下的 iframe 的内容。
Web Storage 访问:
同源策略也限制了 JavaScript 对 Web Storage(如 localStorage 和 sessionStorage)的访问。如果两个页面来自不同的源,那么一个页面的脚本不能读取或修改另一个页面的 Web Storage。
Cookie 访问:
出于安全考虑,浏览器不允许来自不同源的页面访问彼此的 Cookie
不受同源策略影响的行为
普通资源加载:
浏览器在加载普通的 HTML 资源(如
<img>
,<script>
,<link>
,<iframe>
等 src)时,不会受到同源策略的限制。但是,这些资源的后续使用可能会受到限制。JSONP:
虽然 JSONP 是一种跨域请求技术,但它并不是通过 XMLHttpRequest 或 Fetch API 实现的,而是通过动态插入
<script>
标签来实现的,因此不受同源策略的限制。WebSocket:
WebSocket 是一种网络通信协议,它在建立连接后可以在单个 TCP 连接上进行全双工通信。由于 WebSocket 连接在建立时就已经确定了通信的双方,因此不受同源策略的限制。
需要注意的是,虽然这些行为本身可能不受同源策略的限制,但它们的后续使用和处理可能会受到其他安全策略或机制的限制。
例如,即使你能够通过<img>
标签加载一个跨域的图片,但如果你试图通过 JavaScript 读取这个图片的数据(如像素值),那么可能会受到其他安全策略的限制。
- 图片资源的跨域使用
- 浏览器可以加载来自不同源的图片资源(例如,使用
<img>
标签),但这并不意味着可以自由地通过脚本(如JavaScript)来访问这些图片的像素数据。这是因为Canvas API和WebGL API等在处理图片时,对于跨域图片有额外的限制。 - 如果要在一个canvas中绘制跨域图片,并尝试读取其像素数据,浏览器会抛出一个安全错误。除非图片服务器在响应头中明确设置了
Access-Control-Allow-Origin
字段,允许当前域访问。
- 浏览器可以加载来自不同源的图片资源(例如,使用
- 跨域iframe的内容访问
- 通过
<iframe>
标签,浏览器可以嵌入来自不同源的网页。但是,由于同源策略的限制,当前页面的脚本不能直接访问iframe中的DOM内容,除非iframe的内容也设置了document.domain
来放宽同源策略,或者通过window.postMessage
方法进行跨文档通信。
- 通过
- 跨域Web字体(如CSS @font-face)
- 浏览器可以加载并使用来自不同源的Web字体。但是,出于安全考虑,字体文件的内容通常被限制为只包含字体数据,而不允许包含可执行的脚本或恶意代码。
方法
npm i 与 npm ci
两个命名都是下载依赖,不同的是 npm ci 不改变 package.lock.json
npm ci (6.0 版本以上)
会删除项目中的 node_modules 文件夹;
会依照项目中的package.json 来安装确切版本的依赖项;
不像 npm install, npm ci 不会修改你的 package-lock.json 但是它确实期望你的项目中有一个 - package-lock.json 文件 - 如果你没有这个文件, npm ci 将不起作用,此时必须使用 npm install
浏览器控制台调试js玩法
查找页面所有粗体元素
1
2
3
4
5const isBold = (e) => {
let w = window.getComputedStyle(e).fontWeight;
return w === "bold" || w === "700";
};
Array.from(document.querySelectorAll("*")).filter(isBold);获取控制台选中的元素引用
控制台中的
$0
是对元素检查器中当前选定元素的自动引用。您可以检查当前所选元素的事件侦听器
1
getEventListeners($0)
调试所选元素的事件
1
2调试所选元素的所有事件: monitorEvents($0)
调试所选元素的特定事件:monitorEvents($0, ["control", "key"])检查页面悬浮无法选中元素
1
2
3
4// 5s 后js暂停
setTimeout(function() { debugger; }, 5000);
// 方式2, 控制台快捷键随时暂停 JS 执行
ctrl + \
禁止浏览器debugger 行为
打开控制台时,一直debugger
1 | setInterval(() => { |
判断是否打开控制台
第三方库:devtools-detector
1 | <script src="/js/devtools-detector.js"></script> |
十进制二进制相互转换
1 | // 二进制转十进制 |
浏览器复制
1 | // 等同于触发ctrl+c |
addeventListener 第三个参数
1 | options 可选 |
浏览器中 Frame 与 Event Loop 的关系是什么
浏览器组成中有两大引擎,JS 引擎和渲染引擎。
Frame(帧)是渲染引擎每隔 16ms(默认 60fps)将渲染树渲染、合成成位图的结果
每次 Event Loop 是 JS 引擎执行的一个周期,执行过程中可能依赖渲染引擎的执行结果,比如访问 DOM 和 CSSOM,也可能影响渲染引擎绘制帧,比如调用 requestAnimationFrame,在每个帧开始绘制时执行一段回调函数(通常包含影响渲染结果的代码)
因此 Frame 和 Event Loop 是相对独立运行的,但是 Event Loop 中执行的代码可能依赖或影响 Frame
读取二进制信息
ArrayBuffer,二进制数组
TypedArray
所有这些视图(Uint8Array
,Uint32Array
等)的通用术语是 TypedArray。它们都享有同一组方法和属性。
请注意,没有名为 TypedArray
的构造器,它只是表示 ArrayBuffer
上的视图之一的通用总称术语:Int8Array
,Uint8Array
及其他,很快就会有完整列表。
当你看到 new TypedArray
之类的内容时,它表示 new Int8Array
、new Uint8Array
及其他中之一。
类型化数组的行为类似于常规数组:具有索引,并且是可迭代的。
一个类型化数组的构造器(无论是 Int8Array
或 Float64Array
,都无关紧要),其行为各不相同,并且取决于参数类型。
参数有 5 种变体:
1 | new TypedArray(buffer, [byteOffset], [length]); |
如果二进制数据实际上是一个字符串怎么办?
内建的 TextDecoder 对象在给定缓冲区(buffer)和编码格式(encoding)的情况下,允许将值读取为实际的 JavaScript 字符串。
TextDecoder
1 | let decoder = new TextDecoder([label], [options]); |
label
—— 编码格式,默认为utf-8
,但同时也支持big5
,windows-1251
等许多其他编码格式。options
—— 可选对象:
fatal
—— 布尔值,如果为true
则为无效(不可解码)字符抛出异常,否则(默认)用字符\uFFFD
替换无效字符。ignoreBOM
—— 布尔值,如果为true
则 BOM(可选的字节顺序 Unicode 标记),很少需要使用。
解码:
1 | let str = decoder.decode([input], [options]); |
input
—— 要被解码的BufferSource
。options
—— 可选对象:
stream
—— 对于解码流,为 true,则将传入的数据块(chunk)作为参数重复调用decoder
。在这种情况下,多字节的字符可能偶尔会在块与块之间被分割。这个选项告诉TextDecoder
记住“未完成”的字符,并在下一个数据块来的时候进行解码。
通过创建子数组视图来解码
1 | let uint8Array = new Uint8Array([0, 72, 101, 108, 108, 111, 0]); |
TextEncoder
TextEncoder 做相反的事情 —— 将字符串转换为字节。
1 | let encoder = new TextEncoder(); |
只支持 utf-8
编码。
它有两种方法:
encode(str)
—— 从字符串返回Uint8Array
。encodeInto(str, destination)
—— 将str
编码到destination
中,该目标必须为Uint8Array
。
1 | let encoder = new TextEncoder(); |
Blob
arrayBuffer
和视图(view)都是 ECMA 标准的一部分,是 JavaScript 的一部分。在浏览器中,还有其他更高级的对象,特别是
Blob
,在 File API 中有相关描述。
Blob
由一个可选的字符串type
(通常是 MIME 类型)和blobParts
组成 —— 一系列其他Blob
对象,字符串和BufferSource
。
构造函数的语法为:
1 | new Blob(blobParts, options); |
blobParts
是Blob
/BufferSource
/String
类型的值的数组。options
可选对象:
type
——Blob
类型,通常是 MIME 类型,例如image/png
,endings
—— 是否转换换行符,使Blob
对应于当前操作系统的换行符(\r\n
或\n
)。默认为"transparent"
(啥也不做),不过也可以是"native"
(转换)。
eg:
1 | // 从字符串创建 Blob |
我们可以用 slice
方法来提取 Blob
片段:
1 | blob.slice([byteStart], [byteEnd], [contentType]); |
yteStart
—— 起始字节,默认为 0。byteEnd
—— 最后一个字节(专有,默认为最后)。contentType
—— 新 blob 的type
,默认与源 blob 相同。
Blob
对象是不可改变的我们无法直接在
Blob
中更改数据,但我们可以通过slice
获得Blob
的多个部分,从这些部分创建新的Blob
对象,将它们组成新的Blob
,等。这种行为类似于 JavaScript 字符串:我们无法更改字符串中的字符,但可以生成一个新的改动过的字符串。
Blob 作用于 URL 及转换
URL.createObjectURL(blob)
- 如果介意内存,我们需要撤销(revoke)它们
- 直接访问
Blob
,无需“编码/解码”
1
2
3
4
5
6
7
8<!-- download 特性(attribute)强制浏览器下载而不是导航 -->
<a download="hello.txt" href='#' id="link">Download</a>
<script>
let blob = new Blob(["Hello, world!"], {type: 'text/plain'});
link.href = URL.createObjectURL(blob);
</script>Blob 转换为 data url
- 无需撤销(revoke)任何操作。
- 对大的
Blob
进行编码时,性能和内存会有损耗。
1
2
3
4
5
6
7
8
9
10
11
12
13// `Blob` 转换为 base64
let link = document.createElement('a');
link.download = 'hello.txt';
let blob = new Blob(['Hello, world!'], {type: 'text/plain'});
let reader = new FileReader();
reader.readAsDataURL(blob); // 将 Blob 转换为 base64 并调用 onload
reader.onload = function() {
link.href = reader.result; // data url
link.click();
};image to blob
对于页面截屏,我们可以使用诸如 https://github.com/niklasvh/html2canvas 之类的库。它所做的只是扫一遍浏览器页面,并将其绘制在
<canvas>
上。然后,我们就可以像上面一样获取一个它的Blob
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 获取任何图像
let img = document.querySelector('img');
// 生成同尺寸的 <canvas>
let canvas = document.createElement('canvas');
canvas.width = img.clientWidth;
canvas.height = img.clientHeight;
let context = canvas.getContext('2d');
// 向其中复制图像(此方法允许剪裁图像)
context.drawImage(img, 0, 0);
// 我们 context.rotate(),并在 canvas 上做很多其他事情
// toBlob 是异步操作,结束后会调用 callback
canvas.toBlob(function(blob) {
// blob 创建完成,下载它
let link = document.createElement('a');
link.download = 'example.png';
link.href = URL.createObjectURL(blob);
link.click();
// 删除内部 blob 引用,这样浏览器可以从内存中将其清除
URL.revokeObjectURL(link.href);
}, 'image/png');Blob 转换为 Stream
当我们读取和写入超过
2 GB
的 blob 时,将其转换为arrayBuffer
的使用对我们来说会更加占用内存。这种情况下,我们可以直接将 blob 转换为 stream 进行处理。Blob
接口里的stream()
方法返回一个ReadableStream
,在被读取时可以返回Blob
中包含的数据。如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 从 blob 获取可读流(readableStream)
const readableStream = blob.stream();
const stream = readableStream.getReader();
while (true) {
// 对于每次迭代:data 是下一个 blob 数据片段
let { done, data } = await stream.read();
if (done) {
// 读取完毕,stream 里已经没有数据了
console.log('all blob processed.');
break;
}
// 对刚从 blob 中读取的数据片段做一些处理
console.log(data);
}
浏览器访问媒体
1 | MediaDevices |
1 |
|
1 | 前端实现录音功能 |
统计页面出现的元素出现次数
1 | const allElements = document.querySelectorAll("*"); |
页面禁止选中复制
或使用 JS 如下,监听
selectstart
事件,禁止选中。当用户选中一片区域时,将触发
selectstart
事件,Selection API 将会选中一片区域。禁止选中区域即可实现页面文本不可复制。
1 | document.body.onselectstart = (e) => { |
设置一个支持过期时间的 localStrage
1 | function initLocalStorage() { |
想了下 只能去改 api 去封装一层 不过存在一个问题就是别人在使用的时候 也需要遵循这个规则 没想到别的办法
history 、hash 路由实现原理
端路由实现的本质是监听 url 变化,实现方式有两种:Hash 模式和 History 模式,无需刷新页面就能重新加载相应的页面。 Hash url 的格式为
www.a.com/#/
,当#后的哈希值发生变化时,通过 hashchange 事件监听,然后页面跳转。 History url 通过history.pushState
和history.replaceState
改变 url
两者区别
- hash 只能改变#后的值,而 history 模式可以随意设置同源 url;
- hash 只能添加字符串类的数据,而 history 可以通过 API 添加多种类型的数据;
- hash 的历史记录只显示之前的
www.a.com
而不会显示 hash 值,而 history 的每条记录都会进入到历史记录; - hash 无需后端配置且兼容性好,而 history 需要配置
index.html
用于匹配不到资源的情况。
history API
- 通过
history.pushState()
跳转路由 - 通过
popstate event
监听路由变化,但无法监听到history.pushState()
时的路由变化
hash
- 通过
location.hash
跳转路由 - 通过
hashchange event
监听路由变化
DOM 中判断事件是否可阻止默认行为
1 | 通过 e.cancelable 判断事件是否可取消 |
DOM 转 图片
第三方库
- html2canvas (opens new window): Screenshots with JavaScript
- dom-to-image (opens new window): Generates an image from a DOM node using HTML5 canvas
思路: DOM -> SVG -> Canvas -> JPEG/PNG
重点:foreignObject 标签,将 html -> canvas
在canvas 中 图片跨域
1 | img.setAttribute("crossOrigin", "anonymous"); |
取消请求
根据发送网络请求的 API 不同,取消方法不同
- xhr
- fetch
- axios
如果使用
fetch
发送请求可以使用AbortController
1
2
3
4
5
6const controller = new AbortController();
// signal 选项控制 fetch 请求
const signal = controller.signal;
fetch('https://somewhere', { signal })
// 取消请求
controller.abort()如果使用
axios
,取消原理同 fetch1
2
3
4
5
6
7
8var CancelToken = axios.CancelToken;
var source = CancelToken.source();
axios.get('/https://somewhere', {
cancelToken: source.token
}
// 取消请求
source.cancel()XHR 使用
xhr.abort()
1
2
3
4
5
6
7
8
9const xhr = new XMLHttpRequest(),
method = "GET",
url = "https://developer.mozilla.org/";
xhr.open(method, url, true);
xhr.send();
// 取消发送请求
xhr.abort();
检测对象中是否循环引用
示例,如下数据为循环结构/循环引用
1
2 const user = { id: 10086, name: "山月" };
user._user = user;
1 | function isCircularReference(value) { |
判断当前浏览器 是否PC端,哪个手机端
1 | // |
使用第三方库:https://github.com/kaimallea/isMobile
1
2
3 import isMobile from "ismobilejs";
const mobile = isMobile();
bind/softBind/apply/call 都是 this 显式绑定的方法
- bind 会返回一个硬绑定的新函数,新函数会使用指定的第一个 thisCtx 去调用原始函数,并将其它参数传给原始函数。 硬绑定会降低函数的灵活性,在绑定之后不能通过显式或硬绑定的方式改变 this,只能通过 new 改变
- softBind 会对指定的函数进行封装,首先检查调用时的 this,如果 this 绑定到全局对象或者 undefined,那就用指定的 thisCtx 去调用函数,否则不会修改 this
- apply 和 call 功能相同,都是以指定的 thisCtx 和参数去执行方法,并返回原方法的返回值,只是 apply 中参数以数组传递
1 | Function.prototype.myBind = function (ctx = globalThis) { |
js 实现 bind
简单版
1 | Function.prototype.fakeBind = function (obj, ...args) { |
bind 优化版本:考虑 bind 后返回的函数作为构造函数被 new
1 | Function.prototype.bind = function (context, ...args) { |
js 实现 softbind
bind 函数多次调用会已第一次绑定的 this 为准,softbind 已最后一次绑定传入的 this 为准;
1 | Function.prototype.softBind = function(obj, ...rest) { |
如何实现 promise.map,限制 promise 并发数
实现一个 promise.map,进行并发数控制,有以下测试用例
1
2
3
4
5
6 pMap([1, 2, 3, 4, 5], (x) => Promise.resolve(x + 1));
pMap([Promise.resolve(1), Promise.resolve(2)], (x) => x + 1);
// 注意输出时间控制
pMap([1, 1, 1, 1, 1, 1, 1, 1], (x) => sleep(1000), { concurrency: 2 });
1 | function pMap(list, mapper, concurrency = Infinity) { |
关于 JSON.stringify
1 | const obj = { |
把类数组转化成数组
1 | Array.from(arrayLike); |
如何在 url 中传递数组
1 | a=3&a=4&a=5 |
实现 compose 函数,进行函数合成
1 | // 同步版本 |
Loadsh.get 方法的实现
1 | function get(obj, keys, defaultValue) { |
js 深拷贝
1 | /** |
实现一个once函数,记忆返回结果只执行一次
once是社区使用最广泛的一个库,代码实现与上大同小异,然而每月下载量可达上亿,比 vue/react/angular 三者一个月的下载量加起来还要高一倍
1 | function onceCache(fn) { |
实现一个无限累加的 sum 函数
示例如下:
1
2
3
4
5 sum(1, 2, 3).valueOf(); //6
sum(2, 3)(2).valueOf(); //7
sum(1)(2)(3)(4).valueOf(); //10
sum(2)(4, 1)(2).valueOf(); //9
sum(1)(2)(3)(4)(5)(6).valueOf(); // 21
1 | function sum(...args) { |
实现一个同步的 sleep 函数
1 | // 版本1 |
sample 函数,从数组随机取一个元素
数组原型上面拓展,先判断调用者(this) 是否为数组,是数组就随机一个下标
1 |
|
数组洗牌函数 shuffle
1
2 // 打乱数组,有可能是 [1, 3, 2, 4],但对原数组没有影响
shuffle([1, 2, 3, 4]);
1 |
|
翻转一个字符串
1 | 1、方法1 |
loadsh_merge 合并对象
将两个对象深度合并
1 | const getRawType = (val) => { |
如何过滤数组的 假值(falsy value)
falsy value 包含:
false
,null
,0
,""
,undefined
,NaN
1 | function compact(array) { |
创建一个数组大小为 100,每个值都为0的数组
1 | // 方法一: |
Number.prototype.toLocaleString()
返回这个数字在特定语言环境下的表示字符串
语法:
1 numObj.toLocaleString([locales [, options]])
locales
缩写语言代码
- “zh”: 中文
- “zh-cmn”: 中文普通话
- “zh-cmn-Hans”: 中文普通话简体
- zh-Hans”: 简体中文
- “zh-Hans-CN”: 中华人民共和国大陆简体中文
- “‘zh-Hans-CN-u-nu-hanidec’” 数字中文
options
- style 要使用的格式样式,默认为 “decimal”
- numberingSystem 编号系统。可能的值包
示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33var number = 123456.789;
// 德国使用逗号作为小数分隔符,分位周期为千位
console.log(number.toLocaleString('de-DE'));
// → 123.456,789
// 在大多数阿拉伯语国家使用阿拉伯语数字
console.log(number.toLocaleString('ar-EG'));
// → ١٢٣٤٥٦٫٧٨٩
// 印度使用千位/拉克(十万)/克若尔(千万)分隔
console.log(number.toLocaleString('en-IN'));
// → 1,23,456.789
// nu 扩展字段要求编号系统,e.g. 中文十进制
console.log(number.toLocaleString('zh-Hans-CN-u-nu-hanidec'));
// → 一二三,四五六.七八九
// 当请求不支持的语言时,例如巴厘语,加入一个备用语言,比如印尼语
console.log(number.toLocaleString(['ban', 'id']));
// → 123.456,789
// 要求货币格式
console.log(number.toLocaleString('de-DE', { style: 'currency', currency: 'EUR' }));
// → 123.456,79 €
// 日元不使用小数位
console.log(number.toLocaleString('ja-JP', { style: 'currency', currency: 'JPY' }))
// → ¥123,457
// 限制三位有效数字
console.log(number.toLocaleString('en-IN', { maximumSignificantDigits: 3 }));
// → 1,23,000
对象的深度比较
默认对象比较相同地址引用的对象为true, 而内容相同,地址引用不同则不相等
1 | function isEqual(x, y) { |
Object.is 与 “===” 的区别
Object.is()
方法判断两个值是否为同一个值,如果满足以下任意条件则两个值相等
Object.is()
与 ==
不同。==
运算符在判断相等前对两边的变量(如果它们不是同一类型)进行强制转换(这种行为将 "" == false
判断为 true
),而 Object.is
不会强制转换两边的值。
Object.is()
与 ===
也不相同。差别是它们对待有符号的零和 NaN 不同,例如,===
运算符(也包括 ==
运算符)将数字 -0
和 +0
视为相等,而将 Number.NaN
与 NaN
视为不相等。
同一页面三个组件请求同一个 API 发送了三次请求,如何优化
缓存结果,避免请求相同数据
1 | const fetchUser = (id) => { |
获取数字整数与小数部分
如:
1.3e-19
1.6
1 | function splitNum(data: unknown): [string, string] { |
拦截页面click 事件
事件捕获阶段阻止事件向子元素传递
1 | window.addEventListener('click', function(event){ |
监测电脑主题是否暗色模式
1 | const isDarkMode = () => window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches; |
Intl.Segmenter
浏览器方法: 字符串拆分为句子、单词或字素。 话说不是有 split 方法么,为啥还要这个捏?
1 | 'Hello! How are you?'.split(/[.!?]/); |
由此它诞生了。
1 | const segmenter = new Intl.Segmenter( |
服务器端,它从 Node.js 16 开始支持。
实现取消请求
以axios 为示例,应用场景:在一个chat-gpt 演示项目中,一个停止回答的功能。暂停回答时取消请求 如果请求中 会返回reject。如果请求完成,则没有效果。暂停回答效果
1 | // 对话接口获取回复消息 |
取消请求
1 | window.lastCancel() |
树型数据组装
1 | /** |
限制非同源iframe嵌入
配置完成后,nginx将在所有响应头中添加X-Frame-Options: SAMEORIGIN
,这将限制网页的嵌入方式为来自同一源的内嵌网页。
如果设置为 DENY
,不光在别人的网站 frame 嵌入时会无法加载,在同域名页面中同样会无法加载。另一方面,如果设置为 SAMEORIGIN
,那么页面就可以在同域名页面的 frame 中嵌套。
1 | server { |
frame-ancestors, 此属性是上述请求头的代替属性,支持此属性的浏览器可能不支持上述属性,当该指令设置为 'none'
时,其作用类似于 X-Frame-Options
: DENY
1 | Content-Security-Policy: frame-ancestors 'none'; |
语法:source可以是
mail.example.com:443
: 匹配所有对于 mail.example.com 在 443 端口的访问意图。https://store.example.com
: 匹配所有使用 https:访问 store.example.com 的意图。
1 | Content-Security-Policy: frame-ancestors <source> <source>; |
浏览器的 performance
浏览器性能查看,如何使用。参考连接
Web 文件系统(OPFS 及工具)
文件系统是往往是构建大型软件的基石之一,很长一段时间 Web 平台因缺失成熟的文件系统成为构建大型软件的阻碍,如今 OPFS (opens new window)可弥补这一缺憾。
- 它是一个文件系统,提供了底层的文件操作 API,不限于特定的存储场景
- 它可对 Web 同源的文件自由进行读写操作,无需用户手动授权
- 它对文件的访问是经过高度性能优化的
OPFS 在 Chrome 86 后可用,主流浏览器均已支持
安全上下文: 此项功能仅在一些支持的浏览器的安全上下文(HTTPS)中可用。
set 新函数
方法提案向实例添加了Set
以下方法:Set``union``intersection``difference``symmetricDifference``isSubsetOf``isSupersetOf``isDisjointFrom
并集算法 union
1
2
3
4
5
6
7const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const allLanguages = frontEndLanguages.union(backEndLanguages);
// => Set {"JavaScript", "HTML", "CSS", "Python", "Java"}交集算法 intersection
1
2
3
4
5
6
7const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const frontAndBackEnd = frontEndLanguages.intersection(backEndLanguages);
// => Set {"JavaScript"}差异算法 difference
正在使用的集合与另一个集合之间的区别在于,所有元素都存在于第一个集合中,而不存在第二个集合中。
1
2
3
4
5
6
7
8
9
10
11const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const onlyFrontEnd = frontEndLanguages.difference(backEndLanguages);
// => Set {"HTML", "CSS"}
const onlyBackEnd = backEndLanguages.difference(frontEndLanguages);
// => Set {"Python", "Java"}对称差 symmetricDifference
两个集合之间的对称差是一个集合包含两个集合之一中的所有元素,但不同时包含两者。
1
2
3
4
5
6
7
8
9
10
11const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const backEndLanguages = new Set(["Python", "Java", "JavaScript"]);
const onlyFrontEnd = frontEndLanguages.symmetricDifference(backEndLanguages);
// => Set {"HTML", "CSS", "Python", "Java"}
const onlyBackEnd = backEndLanguages.symmetricDifference(frontEndLanguages);
// => Set {"Python", "Java", "HTML", "CSS"}子集 isSubsetOf
如果第一个集合中的所有元素都出现在第二个集合中,则该集合是另一个集合的子集。
1
2
3
4
5
6
7
8
9
10
11const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const declarativeLanguages = new Set(["HTML", "CSS"]);
declarativeLanguages.isSubsetOf(frontEndLanguages);
// => true
frontEndLanguages.isSubsetOf(declarativeLanguages);
// => false集合也是其自身的子集。
1
2
3frontEndLanguages.isSubsetOf(frontEndLanguages);
// => true超集 isSupersetOf
如果一个集合中的所有元素都出现在第一个集合中,则第二个集合是另一个集合的超集。这与子集的关系相反。
1
2
3
4
5
6
7
8
9
10
11const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const declarativeLanguages = new Set(["HTML", "CSS"]);
declarativeLanguages.isSupersetOf(frontEndLanguages);
// => false
frontEndLanguages.isSupersetOf(declarativeLanguages);
// => true一个集合也是其自身的超集。
1
2
3frontEndLanguages.isSupersetOf(frontEndLanguages);
// => true不相交 isDisjointFrom
最后,如果一个集合与另一个集合没有共同的元素,那么它们就是不相交的。
1
2
3
4
5
6
7
8
9
10
11
12
13const frontEndLanguages = new Set(["JavaScript", "HTML", "CSS"]);
const interpretedLanguages = new Set(["JavaScript", "Ruby", "Python"]);
const compiledLanguages = new Set(["Java", "C++", "TypeScript"]);
interpretedLanguages.isDisjointFrom(compiledLanguages);
// => true
frontEndLanguages.isDisjointFrom(interpretedLanguages);
// => false
Safari 17(2023 年 9 月发布)和 Chrome 122(2024 年 2 月)已发布这些方法的实现。
属性
document.referrer
document.referrer
返回的是一个 URI,当前页面就是从这个 URI 所代表的页面跳转或打开的。
例如:领奖页面A.html, 从此页面点击跳转 到领奖详情 B.html. 此时B.html 中 输出 document.referrer 值为 A.html. 但是如果 不从 A.html 点击跳转进入,直接访问 B.html 则 此时B.html 中 输出 document.referrer 值为 空
下面的场景无法获得 referrer 信息
- 直接在浏览器中输入地址
- 使用location.reload()刷新(location.href或者location.replace()刷新有信息)
- 在微信对话框中,点击进入微信自身浏览器
- 扫码进入微信或QQ的浏览器
- 直接新窗口打开一个页面
- 从https的网站直接进入一个http协议的网站(Chrome下亲测)
- a标签设置rel=”noreferrer”(兼容IE7+)
- meta标签来控制不让浏览器发送referer
- 点击 flash 内部链接
- Chrome4.0以下,IE 5.5+以下返回空的字符串
- 使用 修改 Location 进行页面导航的方法,会导致在IE下丢失 referrer,这可能是IE的一个BUG
- 跨域
在
iframe
中,Document.referrer
会初始化为父窗口Window.location
的href
。
meta http-equiv
、在 html 的 header 标签中加上
1 | <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> |
是告诉浏览器,把本站的所有 http 连接升级为 https 连接。
js拓展
Pomelo
1 | 网易游戏服务器开发框架(https://www.oschina.net/p/pomelo) |
主流对象储存平台
1 | 1、amazon s3 |
七牛云对象储存
1 | https://qiniu.com |
js游戏引擎
1、PlayCanvas,或者Babylon.js
这两者都是定位为 Web端 的游戏引擎的,有作为游戏引擎较为完备的工具链,并且都有自己的编辑器。
2、treejs
threejs是一个定位为渲染的库,这就意味着它不会被业务绑定,可以做什么完全取决于你自己的业务场景,这就好比一把刀,有人拿来切菜 有人拿来砍人 有人拿来雕刻 刀本身只是一把刀而已。
这里列出一些主要的业务场景
游戏互动 当然游戏互动会融合各种其他技术,比如物理引擎 界面gui 服务器通讯等等 还有其他更专业的框架可以使用 比如Babylon.js playcanvas laya等更偏游戏的引擎
建筑家装 bim和家装也是webgl的一个比较大的应用场景,基于threejs的bim 家装引擎二次开发也是市面较常见的技术方案
大屏可视化 数据可视化和大屏,配合gis echart啥的搞个炫酷的大屏系统,绝对能唬住领导
白鹭引擎
Egret Engine 是一款使用 TypeScript 编写的 HTML5 游戏引擎,包含渲染、声音、用户交互、资源管理等诸多功能,解决了 HTML5 性能、碎片化问题,应用于 2D 游戏、3D 游戏开发,及移动端交互式应用构建,拥有完善的跨平台运行能力。 现在 Egret Engine 及其它工具已全部整合到 Egret Launcher,请大家直接下载安装开启您的游戏项目创作。
2D/3D双核心渲染,开发工具链闭环整合,全流程开发,由此开始
项目部署
1 | gitlab有gitlab-ci,使用docker部署即可; |
2D,3D
1 | 1、cesium |
微前端的概念
1 | 微前端就是将不同的功能按照不同的维度拆分成多个子应用。通过主应用来加载这些子应用。 微前端的核心在于 `如何拆` 和 `拆完后如何合` |
目前较成熟的微前方案有 qiankun、micro-app、EMP 方案
qiankun 方案
qiankun 方案是基于 single-spa 的微前端方案。
特点
- html entry 的方式引入子应用,相比 js entry 极大的降低了应用改造的成本;
- 完备的沙箱方案,js 沙箱做了 SnapshotSandbox、LegacySandbox、ProxySandbox 三套渐进增强方案,css 沙箱做了 strictStyleIsolation、experimentalStyleIsolation 两套适用不同场景的方案;
- 做了静态资源预加载能力;
不足
- 适配成本比较高,工程化、生命周期、静态资源路径、路由等都要做一系列的适配工作;
- css 沙箱采用严格隔离会有各种问题,js 沙箱在某些场景下执行性能下降严重;
- 无法同时激活多个子应用,也不支持子应用保活;
- 无法支持 vite 等 esmodule 脚本运行;
micro-app 方案
micro-app 是基于 webcomponent + qiankun sandbox 的微前端方案。
特点
- 使用 webcomponet 加载子应用相比 single-spa 这种注册监听方案更加优雅;
- 复用经过大量项目验证过 qiankun 的沙箱机制也使得框架更加可靠;
- 组件式的 api 更加符合使用习惯,支持子应用保活;
- 降低子应用改造的成本,提供静态资源预加载能力;
不足
- 接入成本较 qiankun 有所降低,但是路由依然存在依赖;
- 多应用激活后无法保持各子应用的路由状态,刷新后全部丢失;
- css 沙箱依然无法绝对的隔离,js 沙箱做全局变量查找缓存,性能有所优化;
- 支持 vite 运行,但必须使用 plugin 改造子应用,且 js 代码没办法做沙箱隔离;
- 对于不支持 webcompnent 的浏览器没有做降级处理;
EMP 方案
EMP 方案是基于 webpack 5 module federation 的微前端方案。
特点
- webpack 联邦编译可以保证所有子应用依赖解耦;
- 应用间去中心化的调用、共享模块;
- 模块远程 ts 支持;
不足
- 对 webpack 强依赖,老旧项目不友好;
- 没有有效的 css 沙箱和 js 沙箱,需要靠用户自觉;
- 子应用保活、多应用激活无法实现;
- 主、子应用的路由可能发生冲突;
结论
qiankun 方案对 single-spa 微前端方案做了较大的提升同时也遗留下来了不少问题长时间没有解决;
micro-app 方案对 qiankun 方案做了较多提升但基于 qiankun 的沙箱也相应会继承其存在的问题;
EMP 方案基于 webpack 5 联邦编译则约束了其使用范围;
目前的微前端方案在用户的核心诉求上都没有很好的满足,有很大的优化提升空间。
无界方案
无界微前端方案基于 webcomponent 容器 + iframe 沙箱,能够完善的解决适配成本、样式隔离、运行性能、页面白屏、子应用通信、子应用保活、多应用激活、vite 框架支持、应用共享等用户的核心诉求。
文档地址,demo 地址:https://wujie-micro.github.io/demo-main-vue/home,git 地址:https://github.com/Tencent/wujie
微服务
1 | 一、单体软件 |
CI 和 CD
1 | CI 和 CD 之间(以及不同 CD 之间)有什么区别? |
模块化方案
1、cjs (commonjs)
commonjs 是 Node 中的模块规范,通过 require 及 exports 进行导入导出。同时,webpack 也对 cjs 模块得以解析,因此 cjs 模块可以运行在 node 环境及 webpack 环境下的,但不能在浏览器中直接使用。
静态加载:
// sum.js
exports.sum = (x, y) => x + y;
// index.js
const { sum } = require("./sum.js");
动态加载:
require(`./${a}`);
2、esm (es module)
esm 是 tc39 对于 ESMAScript 的模块话规范,正因是语言层规范,因此在 Node 及 浏览器中均会支持。
它使用 import/export 进行模块导入导出.
静态加载
// sum.js
export const sum = (x, y) => x + y;
// index.js
import { sum } from "./sum";
动态加载
const ms = await import("https://cdn.skypack.dev/ms@latest");
ms.default(1000);
esm 是未来的趋势,目前一些 CDN 厂商,前端构建工具均致力于 cjs 模块向 esm 的转化,比如 skypack、 snowpack、vite 等。
3、AMD
AMD说明:
AMD:Asynchronous Module Definition(异步模块定义)
专门用于浏览器端,模块的加载是异步的
基本语法:
定义暴露模块:
1. 没有依赖的模块:
define(function(){
return 模块
})
2. 定义有依赖的模块:
define([‘module1’,’module2’],function(m1,m2){
return 模块
})
引入使用模块:
require([‘module1’,’module2’],function(m1,m2){
使用m1/m2
})
实现(浏览器端):
使用库: Require.js
未使用 AMD
1 | // dataService.js:(用于提供数据) |
1 | // alerter.js: (用于使用数据) |
1 | // index.html: (在浏览器端展示,引用顺序不能乱) |
使用AMD
1 | // dataService.js: |
1 | // alerter.js: |
1 | // app.js: |
1 | // index.html: |
1 | 4、umd |
ORC 文字图片识别文字
1 | 光学字符识别或光学字符阅读器 (OCR) 是将文本图像转换为机器编码文本的过程。例如,您可以为书页拍照,然后通过 OCR 软件运行以提取文本。 |
跨平台开发
兼容多个平台的前端技术栈
-
可以看作是前端领域使用的第一代跨平台技术
Cordova 将您的 HTML/JavaScript 应用程序包装到一个本机容器中,该容器可以访问多个平台的设备功能。这些功能通过统一的 JavaScript API 公开,让您可以轻松编写一组代码,以针对当今市场上的几乎所有手机或平板电脑并发布到他们的应用商店。
React Native
由Facebook于2015年4月开源的跨平台移动应用框架,它可能是目前使用最广的跨平台开发技术.
2018年6月,Airbnb技术团队在尝试推广RN技术2年后宣布放弃使用React Native而回归自研的原生开发框架,并接连发表了5篇博文[插图]来阐述他们两年间对于RN技术优缺点的总结和体会
React Native对独立开发者而言不够友好,它更适合于对用户体验有一定诉求且人员结构多样化的团队来使用。建议在选用一项技术之前做好评估工作,它的优势是大家都希望借助的,但它的风险和代价却不是每个团队都能承担的。
小程序
动端的桌面上,这样一来,对于普通用户而言,它与App的使用体验似乎也没有什么区别了,更方便的是,它只需要重启一次就可以完成版本更新,所以可以将它也算作跨平台方案之一。如果你不介意自己的应用程序依附于第三方平台而存在,甚至本来就需要依赖平台方的流量扶持,那么小程序可能是目前最好的选择
与传统的Hybrid技术一样,小程序本质上也是基于原生应用中的WebView控件来实现跨平台运行的
小程序平台众多,会出现A平台小程序不能在B程序中运行。著名的uni-app框架和Taro框架都是基于这种多平台的开发需求而产生的解决方案
Google推出的Flutter、阿里推出的Weex、移动端快应用等
- 本文作者: 王不留行
- 本文链接: https://wyf195075595.github.io/2022/06/17/programming/javascript/js番外拓展/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!