JavaScript 是浏览器的内置脚本语言。也就是说,浏览器内置了 JavaScript 引擎,并且提供各种接口,让 JavaScript 脚本可以控制浏览器的各种功能。一旦网页内嵌了 JavaScript 脚本,浏览器加载网页,就会去执行脚本,从而达到操作浏览器的目的,实现网页的各种动态效果。
本章开始介绍浏览器提供的各种 JavaScript 接口。首先,介绍 JavaScript 代码嵌入网页的方法。
网页中嵌入 JavaScript 代码,主要有三种方法。
'
;
上面代码先打开一个新窗口,然后在该窗口弹出一个对话框,再将网址导向/
,服务器在浏览器写入一个 Cookie。这个 Cookie 的所属域名为,生效路径为根路径/
。如果 Cookie 的生效路径设为/forums
,那么这个 Cookie
只有在访问/forums
及其子路径时才有效。以后,浏览器访问某个路径之前,就会找出对该域名和路径有效,并且还没有到期的 Cookie,一起发送给服务器。
用户可以设置浏览器不接受 Cookie,也可以设置不向服务器发送 Cookie。设置的 Cookie,可以被/blog
的时候,浏览器将向服务器发送两个同名的 Cookie。
上面代码的两个 Cookie 是同名的,匹配越精确的 Cookie 排在越前面。
domain
属性,那么将不会附带这个 Cookie。如果指定了domain
属性,那么子域名也会附带这个 Cookie。如果服务器指定的域名不属于当前域名,浏览器会拒绝这个 Cookie。
Path
属性指定浏览器发出 HTTP 请求时,哪些路径要附带这个 Cookie。只要浏览器发现,Path
属性是 HTTP 请求路径的开头一部分,就会在头信息里面带上这个 Cookie。比如,PATH
属性是/
,那么请求/docs
路径也会包含该
Cookie。当然,前提是域名必须一致。
用户一旦被诱骗发送这个表单,银行网站就会收到带有正确 Cookie 的请求。为了防止这种攻击,表单一般都带有一个随机 token,告诉服务器这是真实请求。
浏览器加载上面代码时,就会向 Facebook 发出带有 Cookie 的请求,从而 Facebook 就会知道你是谁,访问了什么网站。
Cookie 的SameSite
属性用来限制第三方 Cookie,从而减少安全风险。它可以设置三个值。
Strict
最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。
这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态。
Lax
规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。
导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。详见下表。
设置了Strict
或Lax
以后,基本就杜绝了 CSRF 攻击。当然,前提是用户浏览器支持 SameSite 属性。
Chrome 计划将Lax
变为默认设置。这时,网站可以选择显式关闭SameSite
属性,将其设为None
。不过,前提是必须同时设置Secure
属性(Cookie 只能通过 HTTPS 协议发送),否则无效。
'
;
Cookie 的属性一旦设置完成,就没有办法读取这些属性的值。
删除一个现存 Cookie 的唯一方法,是设置它的expires
属性为一个过去的日期。
上面代码中,名为fontSize
的 Cookie 的值为空,过期时间设为1970年1月1月零点,就等同于删除了这个 Cookie。
浏览器与服务器之间,采用 HTTP 协议通信。用户在浏览器地址栏键入一个网址,或者通过网页表单向服务器提交内容,这时浏览器就会向服务器发出 HTTP 请求。
withCredentials
属性打开的话,跨域请求不仅会发送 Cookie,还会设置远程主机指定的 Cookie。反之也成立,如果withCredentials
属性没有打开,那么跨域的 AJAX 请求即使明确要求浏览器设置 Cookie,浏览器也会忽略。
上面代码中,GET
请求的参数,作为查询字符串附加在 URL 后面。
下面是发送 POST 请求的例子。
注意,所有 XMLHttpRequest 的监听事件,都必须在send()
方法调用之前设定。
send
方法的参数就是发送的数据。多种格式的数据,都可以作为它的参数。
如果send()
发送 DOM 对象,在发送之前,数据会先被串行化。如果发送二进制数据,最好是发送ArrayBufferView
或Blob
对象,这使得通过 Ajax 上传文件成为可能。
下面是发送表单数据的例子。FormData
对象可以用于构造表单数据。
下面的例子是使用FormData
对象加工表单数据,然后再发送。
上面代码使用setTimeout
,拖延了350毫秒,才让页面跳转,因此使得异步 AJAX 有时间发出。
这些做法的共同问题是,卸载的时间被硬生生拖长了,后面页面的加载被推迟了,用户体验不好。
为了解决这个问题,浏览器引入了/analytics'
;
浏览器安全的基石是“同源政策”()。很多开发者都知道这一点,但了解得不全面。
80
(默认端口可以省略),它的同源情况如下。注意,A 和 B 两个网页都需要设置。
这样的话,二级域名和三级域名不用做任何设置,都可以读取这个 Cookie。
iframe
元素可以在当前网页之中,嵌入其他网页。每个iframe
元素形成自己的窗口,即有自己的window
对象。iframe
窗口之中的脚本,可以获得父窗口和子窗口。但是,只有在同源的情况下,父窗口和子窗口才能通信;如果跨域,就无法拿到对方的 DOM。
比如,父窗口运行下面的命令,如果iframe
窗口不是同源,就会报错。
父窗口和子窗口都可以通过message
事件,监听对方的消息。
// 父窗口和子窗口都可以用下面的代码,
message
事件的参数是事件对象event
,提供以下三个属性。
上面代码中,子窗口将父窗口发来的消息,写入自己的 LocalStorage。
父窗口发送消息的代码如下。
注意,请求的脚本网址有一个
callback
参数(?callback=bar
),用来告诉服务器,客户端的回调函数名称(bar
)。第二步,服务器收到请求后,拼接一个字符串,将 JSON 数据放在函数名里面,作为字符串返回(
bar({...})
)。第三步,客户端会将服务器返回的字符串,作为代码解析,因为浏览器认为,这是
<script>
标签请求的脚本内容。这时,客户端只要定义了bar()
函数,就能在该函数体内,拿到服务器返回的 JSON 数据。下面看一个实例。首先,网页动态插入
<script>
元素,由它向跨域网址发出请求。addScriptTag('发出请求。注意,该请求的查询字符串有一个callback
参数,用来指定回调函数的名字,这对于 JSONP 是必需的。服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。
上面代码中,HTTP 请求的方法是
PUT
,并且发送一个自定义头信息X-Custom-Header
。浏览器发现,这是一个非简单请求,就自动发出一个“预检”请求,要求服务器确认可以这样请求。下面是这个“预检”请求的 HTTP 头信息。
OPTIONS /cors HTTP/
可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。
如果服务器否定了“预检”请求,会返回一个正常的 HTTP 回应,但是没有任何 CORS 相关的头信息字段,或者明确表示请求不符合条件。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被
XMLHttpRequest
对象的onerror
回调函数捕获。控制台会打印出如下的报错信息。服务器回应的其他 CORS 相关字段如下。
该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次“预检”请求。
如果浏览器请求包括
Access-Control-Request-Headers
字段,则Access-Control-Allow-Headers
字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在“预检”中请求的字段。该字段与简单请求时的含义相同。
该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
和共享 5MB 的存储空间。另外,与 Cookie 一样,它们也受同域限制。某个网页存入的数据,只有同域下的网页才能读取,如果跨域操作会报错。
Storage 接口只有一个属性。
//
),然后点击了倒退按钮,页面的 URL
这些属性里面,只有origin
属性是只读的,其他属性都可写。
这个特性常常用于让网页自动滚动到新的锚点。
直接改写
location
,相当于写入href
属性。(3)/q=春节这个 URL 之中,汉字“春节”不是 URL 82,将每个字节前面加上百分号,就构成了 URL 编码。
上面代码中,返回的 URL 实例的路径都是在第二个参数的基础上,切换到第一个参数得到的。最后一个例子里面,第一个参数是
..
,表示上层路径。上面代码中,
fetch
命令向服务器发送命令时,可以直接使用URLSearchParams
实例。那么需要字符串的场合,会自动调用
toString
方法。上面代码中,写入数据需要新建一个事务。新建时必须指定表格名称和操作模式(“只读”或“读写”)。新建事务以后,通过
'
});上面代码中,
put()
方法自动更新了主键为1
的记录。索引的意义在于,可以让你搜索任意字段,也就是说从任意字段拿到数据记录。如果不建立索引,默认只能搜索主键(即从主键取值)。
假定新建表格的时候,对
name
字段建立了索引。现在,就可以从
name
找到对应的数据记录了。浏览器原生提供
indexedDB
对象,作为开发者的操作接口。上面代码表示,打开一个名为
test
、版本为1
的数据库。如果该数据库不存在,则会新建该数据库。
open()
方法的第一个参数是数据库名称,格式为字符串,不可省略;第二个参数是数据库版本,是一个大于0
的正整数(0
将报错),如果该参数大于当前版本,会触发数据库升级。第二个参数可省略,如果数据库已存在,将打开当前版本的数据库;如果数据库不存在,将创建该版本的数据库,默认版本为1
。打开数据库是异步操作,通过各种事件通知客户端。下面是有可能触发的4种事件。
- error:打开失败。
- upgradeneeded:第一次打开该数据库,或者数据库版本发生变化。
- blocked:上一次的数据库连接还未关闭。
第一次打开数据库时,会先触发
upgradeneeded
事件,然后触发success
事件。根据不同的需要,对上面4种事件监听函数。
上面代码有两个地方需要注意。首先,
open()
方法返回的是一个对象(IDBOpenDBRequest),监听函数就定义在这个对象上面。其次,success
事件发生后,从openRequest.result
属性可以拿到已经打开的IndexedDB
数据库对象。
indexedDB.deleteDatabase()
方法用于删除一个数据库,参数为数据库的名字。它会立刻返回一个IDBOpenDBRequest
对象,然后对数据库执行异步删除。删除操作的结果会通过事件通知,IDBOpenDBRequest
对象可以监听以下事件。调用
deleteDatabase()
方法以后,当前数据库的其他已经打开的连接都会接收到versionchange
事件。注意,删除不存在的数据库并不会报错。
indexedDB.cmp()
方法比较两个值是否为 indexedDB 的相同的主键。它返回一个整数,表示比较的结果:0
表示相同,1
表示第一个主键大于第二个主键,-1
表示第一个主键小于第二个主键。注意,这个方法不能用来比较任意的 JavaScript 值。如果参数是布尔值或对象,它会报错。
这个对象的所有操作都是异步操作,要通过
readyState
属性判断是否完成,如果为pending
就表示操作正在进行,如果为done
就表示操作完成,可能成功也可能失败。操作完成以后,触发
success
事件或error
事件,这时可以通过result
属性和error
属性拿到操作结果。如果在pending
阶段,就去读取这两个属性,是会报错的。
IDBRequest.result
:返回请求的结果。如果请求失败、结果不可用,读取该属性会报错。打开数据成功以后,可以从
IDBOpenDBRequest
对象的result
属性上面,拿到一个IDBDatabase
对象,它表示连接的数据库。后面对数据库的操作,都通过这个对象完成。
IDBDatabase.version
:整数,数据库版本。数据库第一次创建时,该属性为空字符串。下面是
objectStoreNames
属性的例子。该属性返回一个 DOMStringList 对象,包含了当前数据库所有对象仓库的名称(即表名),可以使用 DOMStringList 对象的contains
方法,检查数据库是否包含某个对象仓库。上面代码先判断某个对象仓库是否存在,如果不存在就创建该对象仓库。
IDBDatabase.close()
:关闭数据库连接,实际会等所有事务完成后再关闭。上面代码创建了一个名为
items
的对象仓库,如果该对象仓库已经存在,就会抛出一个错误。为了避免出错,需要用到下文的objectStoreNames
属性,检查已有哪些对象仓库。
createObjectStore()
方法还可以接受第二个对象参数,用来设置对象仓库的属性。上面代码中,
keyPath
属性表示主键(由于主键的值不能重复,所以上例存入之前,必须保证数据的null
;autoIncrement
属性表示,是否使用自动递增的整数作为主键(第一个数据记录为1,第二个数据记录为2,以此类推),默认为false
。一般来说,keyPath
和autoIncrement
属性只要使用一个就够了,如果两个同时使用,表示主键为递增的整数,且对象不得缺少keyPath
指定的属性。下面是
transaction()
方法的例子,该方法用于创建一个数据库事务,返回一个 IDBTransaction 对象。向数据库添加数据之前,必须先创建数据库事务。
transaction()
方法接受两个参数:第一个参数是一个数组,里面是所涉及的对象仓库,通常是只有一个;第二个参数是一个表示操作类型的字符串。目前,操作类型只有两种:readonly
(只读)和readwrite
(读写)。添加数据使用readwrite
,读取数据使用readonly
。第二个参数是可选的,省略时默认为readonly
模式。
IDBObjectStore.add()
用于向对象仓库添加数据,返回一个 IDBRequest 对象。该方法只用于添加数据,如果主键相同会报错,因此更新数据必须使用put()
方法。该方法接受两个参数,第一个参数是键值,第二个参数是主键,该参数可选,如果省略默认为
null
。创建事务以后,就可以获取对象仓库,然后使用
add()
方法往里面添加数据了。
IDBObjectStore.put()
方法用于更新某个主键对应的数据记录,如果对应的键值不存在,则插入一条新的记录。该方法返回一个 IDBRequest 对象。该方法接受两个参数,第一个参数为新数据,第二个参数为主键,该参数可选,且只在自动递增时才有必要提供,因为那时主键不包含在数据值里面。
该方法的参数为主键的值。
不带参数时,该方法返回当前对象仓库的所有记录数量。如果主键或 IDBKeyRange 对象作为参数,则返回对应的记录数量。
该方法的参数可以是主键值或 IDBKeyRange 对象。
// 指定获取记录的数量// 获取所有记录的主键 // 获取所有符合条件的主键 // 指定获取主键的数量
有了索引以后,就可以针对索引所在的属性读取数据。
上面代码打开对象仓库以后,先用
index()
方法指定获取name
属性的索引,然后用get()
方法读取某个name
属性(foo
)对应的数据。如果name
属性不是对应唯一值,这时get()
方法有可能取回多个数据对象。另外,get()
是异步方法,读取成功以后,只能在success
事件的监听函数中处理数据。该方法可以接受三个参数。
第三个参数可以配置以下属性。
true
,将不允许重复的值
true
,对于有多个值的主键数组,每个值将在索引里面新建一个条目,否则主键数组对应一个条目。
假定对象仓库中的数据记录都是如下的person
类型。
可以指定这个对象的某个属性来建立索引。
上面代码告诉索引对象,name
属性不是唯一值,email
属性是唯一值。
指针对象可以用来遍历数据。该对象也是异步的,有自己的success
和error
事件,可以对它们指定监听函数。
监听函数接受一个事件对象作为参数,该对象的target.result
属性指向当前数据记录。该记录的key
和value
分别返回主键和键值(即实际存入的数据)。continue()
方法将光标移到下一个数据对象,如果当前数据对象已经是最后一个数据了,则光标指向null
。
对象。如果指定该参数,将只处理包含指定主键的记录;如果省略,将处理所有的记录。该方法还可以接受第二个参数,表示遍历方向,默认值为next
,其他可能的值为prev
、nextunique
和prevunique
。后两个值表示如果遇到重复值,会自动跳过。
IDBTransaction 对象用来异步操作数据库事务,所有的读写操作都要通过这个对象进行。
事务的执行顺序是按照创建的顺序,而不是发出请求的顺序。
上面代码中,key
对应的键值最终是2
,而不是1
。因为事务trans1
先于trans2
创建,所以首先执行。
注意,事务有可能失败,只有监听到事务的complete
事件,才能保证事务操作成功。
IDBTransaction.error
:返回当前事务的错误。如果事务没有结束,或者事务成功结束,或者被手动终止,该方法返回null
。
IDBIndex 对象代表数据库的索引,通过这个对象可以获取数据库里面的记录。数据记录的主键默认就是带有索引,IDBIndex 对象主要用于通过除主键以外的其他键,建立索引获取对象。
IDBIndex 是持久性的键值对存储。只要插入、更新或删除数据记录,引用的对象库中的记录,索引就会自动更新。
IDBIndex.multiEntry
:布尔值,针对keyPath
为数组的情况,如果设为true
,创建数组时,每个数组成员都会有一个条目,否则每个数组都只有一个条目。
IDBIndex.unique
:布尔值,表示创建索引时是否允许相同的主键。
IDBIndex 对象有以下方法,它们都是异步的,立即返回的都是一个 IDBRequest 对象。
IDBIndex.count()
:用来获取记录的数量。它可以接受主键或 IDBKeyRange 对象作为参数,这时只返回符合主键的记录数量,否则返回所有记录的数量。
IDBIndex.getAll()
:用来获取所有的数据记录。它可以接受两个参数,都是可选的,第一个参数用来指定主键,第二个参数用来指定返回记录的数量。如果省略这两个参数,则返回所有记录。由于获取成功时,浏览器必须生成所有对象,所以对性能有影响。如果数据集比较大,建议使用 IDBCursor 对象。
IDBCursor.direction
:字符串,表示指针遍历的方向。共有四个可能的值:next(从头开始向后遍历)、nextunique(从头开始向后遍历,重复的值只遍历一次)、prev(从尾部开始向前遍历)、prevunique(从尾部开始向前遍历,重复的值只遍历一次)。该属性通过IDBObjectStore.openCursor()
方法的第二个参数指定,一旦指定就不能改变了。
IDBCursor.continue()
:指针向前移动一个位置。它可以接受一个主键作为参数,这时会跳转到这个主键。
IDBCursor.delete()
:用来删除当前位置的记录,返回一个 IDBRequest 对象。该方法不会改变指针的位置。
IDBCursor.update()
:用来更新当前位置的记录,返回一个 IDBRequest 对象。它的参数是要写入数据库的新的值。
IDBKeyRange 对象代表数据仓库(object store)里面的一组主键。根据这组主键,可以获取数据仓库或索引里面的一组记录。
IDBKeyRange 可以只包含一个值,也可以指定上限和下限。它有四个静态方法,用来指定主键的范围。
与之对应,IDBKeyRange 对象有四个只读属性。
IDBKeyRange.lowerOpen
:布尔值,表示下限是否为开区间(即下限是否排除在范围之外)
IDBKeyRange.upperOpen
:布尔值,表示上限是否为开区间(即上限是否排除在范围之外)
IDBKeyRange 有一个实例方法includes(key)
,返回一个布尔值,表示某个主键是否包含在当前这个主键组之内。
JavaScript 语言采用的是单线程模型,也就是说,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着。随着电脑计算能力的增强,尤其是多核 CPU 的出现,单线程带来很大的不便,无法充分发挥计算机的计算能力。
Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务可以交由 Worker 线程执行,主线程(通常负责 UI 交互)能够保持流畅,不会被阻塞或拖慢。
Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document
、window
、parent
这些对象。但是,Worker 线程可以使用navigator
对象和location
对象。
的全局对象存在console
接口,只定义了Navigator
接口和Location
接口。不过,浏览器实际上支持 Worker 线程使用console.log
,保险的做法还是不使用这个方法。
Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://
),它所加载的脚本,必须来自网络。
主线程采用new
命令,调用Worker()
构造函数,新建一个 Worker 线程。
Worker()
构造函数的参数是一个脚本文件,该文件就是 Worker 线程所要执行的任务。由于 Worker 不能读取本地文件,所以这个脚本必须来自网络。如果下载没有成功(比如404错误),Worker 就会默默地失败。
worker.postMessage()
方法的参数,就是主线程传给 Worker 的数据。它可以是各种数据类型,包括二进制数据。
接着,主线程通过worker.onmessage
指定监听函数,接收子线程发回来的消息。
上面代码中,事件对象的data
属性可以获取 Worker 发来的数据。
Worker 完成任务以后,主线程就可以把它关掉。
Worker 线程内部需要有一个监听函数,监听message
事件。
上面代码中,self
代表子线程自身,即子线程的全局对象。因此,等同于下面两种写法。
除了使用self.addEventListener()
指定监听函数,也可以使用self.onmessage
指定。监听函数的参数是一个事件对象,它的data
属性包含主线程发来的数据。self.postMessage()
方法用来向主线程发送消息。
根据主线程发来的数据,Worker 线程可以调用不同的方法,下面是一个例子。
该方法可以同时加载多个脚本。
主线程可以监听 Worker 是否发生错误。如果发生错误,Worker 会触发主线程的error
事件。
使用完毕,为了节省系统资源,必须关闭 Worker。
前面说过,主线程与 Worker 之间的通信内容,可以是文本,也可以是对象。需要注意的是,这种通信是拷贝关系,即是传值而不是传址,Worker 对通信内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是,先将通信内容串行化,然后把串行化后的字符串发给 Worker,后者再将它还原。
主线程与 Worker 之间也可以交换二进制数据,比如 File、Blob、ArrayBuffer 等类型,也可以在线程之间发送。下面是一个例子。
但是,拷贝方式发送二进制数据,会造成性能问题。比如,主线程向 Worker 发送一个 500MB 文件,默认情况下浏览器会生成一个原文件的拷贝。为了解决这个问题,JavaScript 允许主线程把二进制数据直接转移给子线程,但是一旦转移,主线程就无法再使用这些二进制数据了,这是为了防止出现多个线程同时修改数据的麻烦局面。这种转移数据的方法,叫做。这使得主线程可以快速把数据交给 Worker,对于影像处理、声音处理、3D 运算等就非常方便了,不会产生性能负担。
如果要直接转移数据的控制权,就要使用下面的写法。
通常情况下,Worker 载入的是一个单独的 JavaScript 脚本文件,但是也可以载入与主线程在同一个网页的代码。
上面是一段嵌入网页的脚本,注意必须指定``标签的type
属性是一个浏览器不认识的值,上例是app/worker
。
然后,读取这一段嵌入页面的脚本,用 Worker 来处理。
上面代码中,先将嵌入网页的脚本代码,转成一个二进制对象,然后为这个二进制对象生成 URL,再让 Worker 加载这个 URL。这样就做到了,主线程和 Worker 的代码都在同一个网页上面。
有时,浏览器需要轮询服务器状态,以便第一时间得知状态改变。这个工作可以放在 Worker 里面。
上面代码中,Worker 每秒钟轮询一次数据,然后跟缓存做比较。如果不一致,就说明服务端有了新的变化,因此就要通知主线程。
Worker 线程内部还能再新建 Worker 线程(目前只有 Firefox 浏览器支持)。下面的例子是将一个计算密集的任务,分配到10个 Worker。
上面代码中,Worker 线程内部新建了10个 Worker 线程,并且依次向这10个 Worker 发送消息,告知了计算的起点和终点。计算任务脚本的代码如下。
浏览器原生提供Worker()
构造函数,用来供主线程生成 Worker 线程。
Worker()
构造函数,可以接受两个参数。第一个参数是脚本的网址(必须遵守同源政策),该参数是必需的,且只能加载 JS 脚本,否则会报错。第二个参数是配置对象,该对象可选。它的一个作用就是指定 Worker 的名称,用来区分多个 Worker 线程。
Worker()
构造函数返回一个 Worker 线程对象,用来供主线程操作 Worker。Worker 线程对象的属性和方法如下。
Web Worker 有自己的全局对象,不是主线程的window
,而是一个专门为 Worker 定制的全局对象。因此定义在window
上面的对象和方法不是全部都可以使用。
Worker 线程有一些自己的全局属性和方法。
你电脑上的所有文档、照片、工作文件、隐私文件与及其它重要文件都已经被加密。要解密这些文件,请运行桌面上的<文件解密>工具;如果桌面上没有这个工具,请前往每一个分区的根目录下查找;如果仍然无法找到该工具,请继续阅读此帮助内容; 只有严格按照稍后提示的步骤进行,才能解密你的文件;请不要使用杀毒软件或者其它任何工具尝试恢复文件,因为这将会破坏或删除被加密的文件,导致文件永远无法恢复! 你首先要下载并安装《Tor浏览器》;请百度搜索"Tor浏览器中文版",找到下载地址,下载并安装;或者直接从官网 上下载安装; 如果你是第1次使用此浏览器,你可能还需要百度搜索"Tor浏览器使用"获取相关的使用教程; 由于Tor网络已经被封锁,你可能还需要使用国外的VPN或者为Tor浏览器配置国外代理服务器;如果你并不懂得如何操作,请在网上搜索相关的资料或者寻求专业人员的帮助; 在确认已经连接上Tor网络的前提下,在Tor浏览器中输入网址访问此网站: ,如果网站暂时无法打开,请稍后继续重试; 如果解密工具已丢失,你可以从此网站上重新下载一个;运行解密工具的时候,请确保杀毒软件处于关闭状态; 打开网站后,复制以下机器码输入并提交,然后根据网站的提示进行操作;如果成功,你将获得一个唯一的密钥,这个密钥将可以解密你所有的文件; |