1.客户端JavaScript笔记:概述

本系列内容由ZouStrong整理收录

整理自《JavaScript权威指南(第六版)》,《JavaScript高级程序设计(第三版)》

一. 客户端JavaScript

window对象是所有客户端JavaScript特性和API的主要接入点,在客户端中,它就代表了ECMAScript语言规定的全局对象

window对象代表Web浏览器窗口(或者一个框架)

window对象定义了很多属性,比如location属性,它代表location对象,该对象定义了URL的相关信息

window.location = "www.strong.com";  //页面跳转

window对象还定义了一些方法,用于显示警告信息的alert(),用于执行超时调用的setTimeout()等

setTimeout(function(){},1000) //1秒后,执行函数

这里没有显式使用window对象,因为它是全局对象,位于作用域链的顶端,它的属性和方法实际上是全局变量和全局函数,因此可以直接使用

window对象有一个最重要的属性——document,它引用document对象,代表着显示在当前窗口的文档;document对象有很多重要属性和方法

var strong = document.getElementById('strong');   //根据id值获取元素对象

而获取的Element对象又有着其他重要的属性和方法

strong.firstChild;  //返回元素的第一个子元素
strong.appendChild();   //在元素末尾添加子元素

每个Element对象都有style属性和className属性,用于修改CSS样式和修改元素上的类名

strong.style.color = "#F00";
strong.className = "strong";

Window、Document、Element对象上都有的一个重要属性就是事件处理程序相关的属性,可以让事件发生的时候以异步的方式调用指定的函数

strong.onclick = function(){
	this.style.color = "#F00";
};

其中,window对象上的load事件是最重要的事件之一,当页面中的内容(包括图片)全部加载完成的时候就会触发它,JavaScript代码通常封装在onload事件处理程序中

二. 在页面中使用JavaScript

有四种方式可以让我们在页面中使用JavaScript

  • 嵌入脚本(代码位于<script></script>之间)
  • 外部脚本(由<script>的src属性引入外部JS文件)
  • 放置在HTML事件处理程序中(由HTML事件处理程序相关属性指定)
  • 放置在URL里(这个URL使用"javascript:"协议)

方式3和4几乎不使用,方式1也很少使用,方式2最常用(这才是分离式的JavaScript)

1. 嵌入脚本

<script>
//JavaScript代码
</script>

2. 外部脚本

<script src="strong.js"></script>

使用src属性的<script>标签的行为就像引入的外部文件的内容直接出现在<script></script>之间一样

当含有src属性时,包含在<script></script>之间的所有代码都会被忽略,因此可以在这里添加代码的补充文档或者说明信息

在XHTML中,可以使用自闭合的<script src="" />

使用外部脚本的好处

  • 保证内容和行为的分离,简化HTML
  • 多个页面共用代码时,脚本放在一个外部文件中,易于维护
  • 多个页面使用同一个脚本时,只需要下载一次,可缓存
  • src属性可以是任意的url,因此可以使用另一个域中的JS文件
  • 可以从其它域载入脚本,可以让我们更好的利用缓存(例如使用Google或者百度的CDN,可以减少页面加载时间,因为类库可能已经存在于用户浏览器缓存中)

注意的是:引用外部脚本不受同源策略的限制,一旦使用src属性引入了外部脚本,就给了这个脚本完全控制你的页面的权限

3. HTML中的事件处理程序

JavaScript可以通过把函数赋值给Element对象的属性(如onclick)来注册事件处理程序

<p onclick="alert(1);"></p>

属性中可以包含任意多条JavaScript语句,语句之间使用分号或者逗号分隔

尽量不要使用 HTML中的事件处理程序,保证内容和行为的分离;如果不得不使用,应该将上面的语句封装成一个函数,放在外部脚本或者嵌入脚本中

<p onclick="strong()"></p>
<script>
function strong(){
    alert(1)
}  
</script>

属性值是一个函数调用字符串,而不是函数名字符串(不同于js中的事件处理程序),因为是字符串形式嘛,只有事件发生的时候,才回去解析字符串中的代码

4. URL中的JavaScript

在URL后面跟一个"javascript:"也可以将代码嵌入到HTML页面

javascript:URL能识别的“资源”是代码的返回值,如果代码返回undefined,那么这个资源是没有内容的

javascript:URL可以用在使用常规URL的任意地方,a标签的href属性,form的action属性,甚至window.open()的参数

<a href="javascript:new Date().toString();">时间</a>

浏览器会执行URL里的代码,并将返回的字符串作为待显示新文档的内容

<a href="javascript:alert(new Date().toString());">时间</a>

浏览器会执行URL里的代码,由于没有返回值(alert返回undefined)作为新文档的内容,所以只会弹出警告信息,就好像在a元素上添加了onclick事件处理程序

如果要确保javascript:URL不会覆盖当前文档,可以用void运算符强制返回undefined值

<a href="javascript:void window.open('about:blank')">时间</a>

javascript:URL也应该避免使用

三. 代码的执行

对于同一个页面来说,所有的JavaScript代码(包括了上面四种方式引入的)拥有相同的全局执行环境,它们公用同一个全局window对象,因此可以看到相同的Document对象,共享相同的全局函数和变量

如果页面中包含一个内联框架(iframe元素),嵌入的文档中的JavaScript代码和被嵌入的文档中的JavaScript代码就拥有不同的全局对象
(但是两个窗体之间可以交互,后述)

JavaScript代码的执行有两个阶段

  • 首先,页面加载文档,执行<script>元素里的JavaScript代码(包括外部和嵌入的),代码按照它们在文档中出现的顺序执行(代码会从上往下,并按照它们在条件、循环以及其它控制语句中出现的顺序执行)
  • 整个页面加载完成,并且所有脚本执行完成后,JavaScript代码的执行进入第二阶段——这个阶段是异步的事件驱动的,浏览器会在事件发生的时候,调用预先绑定好的事件处理程序,借以响应异步发生的事件。因为这个阶段是异步和事件驱动的,因此可能很长时间都处于不活动状态

事件驱动阶段发生的第一个事件——load事件,当发生在window上时,表示文档已经全部载入完成,并且可以操作(此时,所有的CSS,图片,脚本文件都已经加载完成——图片不一定下载完成)

客户端JavaScript都有一个单线程执行模型,同一时间只能有一段代码在执行,没有并发性

1. 同步、异步和延迟脚本

当解析器遇到<script>的时候,就会停止对HTML的解析,转而下载(如果是外部文件的话)和执行JavaScript,完成之后才会继续解析后续的页面——默认情况下,脚本的执行是同步和阻塞的

可以使用<script>的defer属性和async属性来改变脚本的执行方式

  • defer属性表示当解析器遇到<script>的时候,会立即下载脚本,但是脚本的执行会延迟到页面全部加载完成之后
  • async属性表示当解析器遇到<script>的时候,会立即下载脚本并执行,但是不会阻碍后续页面的加载
  • 两个属性只对拥有src属性的<script>有效(外部脚本)
  • 同时使用这两个属性,浏览器会遵从async属性

2. 事件驱动

事件是浏览器窗口或者文档上发生的事情,它们可能是自己触发的,也可能是由使用者手动触发的

  • 事件都有名字:load、click、mousedown、keyup
  • 事件还有目标:指明了事件发生的对象
  • 事件还有响应:指明了事件发生时要调用的函数(事件处理程序、事件监听器、回调),事件处理程序的名字就是"on+事件名"

除此之外,事件发生时,会有一个对象传递给事件处理程序,这个对象包含了事件的详细信息

事件还会冒泡(IE中是捕获)等等...

3. 客户端JavaScript线程模型

JavaScript规范并没有线程机制,但是客户端JavaScript却像严格的单线程一样工作

编写代码时,可以确保两个事件处理程序不会同时执行,也不必担心在操作文档的时候会有其他代码同时修改文档

单线程意味着浏览器必须在脚本和事件处理程序执行的时候,停止其他代码的响应,并且如果代码自行了密集计算任务,可能会使浏览器失去响应,误以为浏览器崩溃

HTML5新增了一种并发的控制方式(后述)

4. 客户端JavaScript时间线

客户端从请求页面到显示完成,进行了很多工作,本节只涉及JavaScript部分

  • 1 浏览器创建Document对象,并且从上往下开始解析Web页面,此时document.readyState属性值为'loading'
  • 2.1 当解析器遇到没有defer属性和async属性的<script>时,会停止对HTML页面的解析,转而下载和执行JavaScript代码,这时,可以使用document.write()将文本插入到输入流中,当解析器继续解析HTML页面时,这些文本就会成为文档的一部分——同步脚本可以操作在它之前出现的元素
  • 2.2 当解析器遇到包含async属性的<script>时,会立即下载脚本并执行,与此同时,HTML页面继续解析,不受任何影响——异步脚本可以操作在它之前出现的元素,并且也有可能操作在它之后出现的元素
  • 当解析器遇到包含defer属性的<script>时,会立即下载脚本(不会执行),然后HTML页面继续解析, 直到文档载入和解析完成之后,脚本才会执行——异步脚本必然可以操作在它之前和之后出现的元素
  • 当文档完成解析,此时document.readyState属性值为'interactive'

注:由于异步脚本有可能在页面解析完成之后执行,而延迟脚本肯定在页面解析完成之后执行,都有可能访问整个文档,因此在这两种脚本里,禁用document.write()方法

  • 浏览器在Document对象上触发DOMContentLoaded事件,这标志着程序从同步脚本阶段转换到异步事件驱动阶段,此时,还有异步脚本或延迟脚本可能没有执行完成
  • 此时,文档解析完成,但是可能还有其他内容在载入(如图片),当所有这些内容都完成载入,并且所有异步脚本都完成载入和执行,document.readyState属性值为'complete',浏览器就会触发window对象上的load事件
  • 从此时起,会调用异步事件,以异步响应用户输入事件,网络事件,计时器过期等

不同的浏览器实现细节略有不同,但所有浏览器都支持load事件,因此,这是决定文档是否完全载入并且可以操作的最通用技术

小结

document.write()方法可以用在两个方面:页面载入过程中用实时脚本创建页面内容,以及用延时脚本创建本窗口或新窗口的内容

在载入页面后,浏览器输出流自动关闭。在此之后,任何一个对当前页面进行操作的document.write()方法将打开—个新的输出流,它将清除当前页面内容(包括源文档的任何变量或值)

window.onload = function(){
	document.write("重写整个页面");
};

<script type="text/javascript">
    var div=document.createElement("div");
    div.innerHTML="strong";
    //如果直接document.body.appendChild(div); 则失败.
     
    //document.write("sssss");  //添加这行,document.body.appendChild(div) 成功.
    document.body.appendChild(div);
</script>

四. 安全性

所有的浏览器都包含了JavaScript解析器,一旦载入了页面,就有可能让任何JavaScript代码运行,很显然,这里存在着安全隐患

1. JavaScript不能做什么

浏览器针对恶意代码的第一条防线就是不支持某些功能

  • 客户端 JavaScript没有权限来写入或者删除计算机上的任意文件或者列出任意目录,因此确保了不会删除用户数据或者写入病毒
  • 客户端 JavaScript没有任何通用的网络能力,因此不能够基于浏览器写出一个服务器,浏览器和浏览器之间无法直接进行通信

浏览器针对恶意代码的第二条防线就是限制某些功能

  • JavaScript程序可以打开一个新的浏览器窗口(window.open()),由于这个功能被广告商滥用,所以很多浏览器限制了这个功能——只有为了响应鼠标单击这样的用户触发事件的时候,才能使用它
  • JavaScript程序可以关闭自己打开的浏览器窗口,但是不允许不经过确认就关闭其他的窗口
  • JavaScript程序不能读取不同域下的文档的内容,除非这个文档包含这个脚本——这可以防止脚本窃取其他文档的信息

此外不同的浏览器还有不同的安全策略,部分浏览器还可以根据用户设置来增强或减弱限制

2. 同源策略

同源策略是对JavaScript代码能够操作哪些Web内容的一条完整的安全限制,当页面使用多个iframe元素或者打开其他浏览器窗口的时候,这一策略通常会发挥作用

同源策略负责管理窗口或者窗体中的JavaScript代码和其它窗口的交互(例如访问cookie等)——脚本只能读取和所属文档来源相同的窗口或者文档的属性

这里的来源相同是指——协议、主机和端口都相同

脚本本身的来源和同源策略并不相关(不受同源策略的限制),这里的来源是脚本所在的文档的来源

主机A上的一个文档引入了来自主机B上的一个脚本,如果脚本打开一个新窗口,并载入主机A上的另一个文档,脚本对这个文档也具有完全的访问权限,但是如果脚本打开一个新窗口,并载入主机B上的文档,同源策略就会发挥作用,组织脚本访问这个文档

不严格的同源策略

同源策略对那些使用多个子域名的大网站带来了问题,由于同源策略的限制,a.strong.com上的脚本不能和b.strong.com上的文档进行通信

为了支持这种类型的子域名,可以试用 document.domain属性将域名设置为strong.com,这样这两个窗口就不受同源策略的限制,可以互相通信了

不严格的同源策略的第二项技术就是跨域资源共享(后述)

第三项技术就是跨文档信息,使用window.postMessage()(后述)

3. 跨站脚本

跨站脚本,也就是攻击者向目标站点注入HTML标签或者脚本

如果页面是动态产生的,这些文档内容是基于用户输入的,并且没有移除用户内容中的HTML标签的话,那么整个页面就很容易遭到跨站脚本的攻击

因为如果用户输入的是,script标签,并且引入了人外部JS时,就容易产生问题

你可能感兴趣的:(JavaScript)