1、什么是JavaScript
1-一、JavaScript实现
虽然JavaScript和ECMAScript基本上是同义词,但JavaScript远不限于ECMA-262所定义的那样。完整的JavaScript实现包含:前端
- 核心(ECMAScript)
- 文档对象模型(DOM)
- 浏览器对象模型(BOM)
Web浏览器只是ECMAScript实现可能存在的一种宿主环境(hostenvironment)。宿主环境提供ECMAScript的基准实现和与环境自身交互必需的扩展。扩展(好比DOM)使用ECMAScript核心类型和语法,提供特定于环境的额外功能。其余宿主环境还有服务器端JavaScript平台Node.js和即将被淘汰的Adobe Flash。
在基本层面,描述这门语言:语法,类型,语句,关键字,保留字,操做符,全局对象
java
ECMAScript只是对实现这个规范描述的全部方面的一门语言的称
呼。JavaScript实现了ECMAScript,而Adobe ActionScript一样也实现了
ECMAScript。
node
1-二、DOM
文档对象模型(DOM,Document Object Model)是一个应用编程接口(API),用于在HTML中使用扩展的XML。DOM将整个页面抽象为一组分层节点。HTML或XML页面的每一个组成部分都是一种节
点,包含不一样的数据。
正则表达式
DOM经过建立表示文档的树,让开发者能够为所欲为地控制网页的内容和结构。使用DOM API,能够轻松地删除、添加、替换、修改节点。算法
1-三、BOM
IE3和Netscape Navigator 3提供了浏览器对象模型(BOM) API,用于支持访问和操做浏览器的窗口。使用BOM,开发者能够操控浏览器显示页面以外的部分。而BOM真正独一无二的地方,固然也是问题最多的地方,就是它是惟一一个没有相关标准的JavaScript实现。HTML5改变了这个局面,这个版本的HTML以正式规范的形式涵盖了尽量多的BOM特性。因为HTML5的出现,以前不少与BOM有关的问题都迎刃而解了。express
整体来讲,BOM主要针对浏览器窗口和子窗口(frame),不过
人们一般会把任何特定于浏览器的扩展都归在BOM的范畴内。下面就是这样一些扩展:
编程
- 弹出新浏览器窗口的能力;
- 移动、缩放和关闭浏览器窗口的能力;
- navigator 对象,提供关于浏览器的详尽信息;
- location 对象,提供浏览器加载页面的详尽信息;
- screen 对象,提供关于用户屏幕分辨率的详尽信息;
- performance 对象,提供浏览器内存占用、导航行为和时间统
- 计的详尽信息;
- 对cookie的支持;
- 其余自定义对象,如 XMLHttpRequest 和IE的
- ActiveXObject
2、HTML中的JavaScript
2-一、script
元素
将JavaScript插入HTML的主要方法是使用 script 元素。这
个元素是由网景公司创造出来,并最先在Netscape Navigator 2中实现
的。后来,这个元素被正式加入到HTML规范。 script 元素有下
列8个属性。
数组
-
async
:可选。表示应该当即开始下载脚本,但不能阻止其余页面动做,好比下载资源或等待其余脚本加载。只对外部脚本文件有效。浏览器 -
charset :可选。使用 src 属性指定的代码字符集。这个属性不多使用,由于大多数浏览器不在意它的值。安全
-
crossorigin :可选。配置相关请求的CORS(跨源资源共享)设置。默认不使用CORS。crossorigin=“anonymous” 配置文件请求没必要设置凭据标志。 crossorigin=“use-credentials” 设置凭据标志,意
味着出站请求会包含凭据。 -
defer :可选。表示在文档解析和显示完成后再执行脚本是没有问题的。只对外部脚本文件有效。在IE7及更早的版本中,对行内脚本也能够指定这个属性。
-
integrity :可选。容许比对接收到的资源和指定的加密签名以验证子资源完整性(SRI,Subresource Intergrity)。若是接收到的资源的签名与这个属性指定的签名不匹配,则页面会报错,脚本不会执行。这个属性能够用于确保内容分发网络(CDN,Content Delivery Network)不会提供恶意内容。
-
language :废弃。最初用于表示代码块中的脚本语言(如 “JavaScript” 、 “JavaScript1.2” 或 “VBScript” )。大多数浏览器都会忽略这个属性,不该该再使用它。
-
src :可选。表示包含要执行的代码的外部文件。
-
type :可选。代替 language ,表示代码块中脚本语言的内容类型(也称MIME类型)。
包含在 script 内的代码会被从上到下解释
2-二、
使用了 src 属性的 script 元素不该该再在 script 和 /script 标签中再包含其余JavaScript代码。若是二者都提供的话,则浏览器只会下载并执行脚本文件,从而忽略行内代码。
script 元素的一个最为强大、同时也备受争议的特性是,它能够包含来自外部域的JavaScript文件。跟 img 元素很像, script 元素的 src 属性能够是一个完整的URL,并且这个URL指向的资源能够跟包含它的HTML页面不在同一个域中
2-三、文档模式
可使用 doctype 切换文档模式。最初的文档模式有两种:混杂模式(quirks mode)和标准模式(standards mode)。前者让IE像IE5同样(支持一些非标准的特性),后者让IE具备兼容标准的行为。虽然这两种模式的主要区别只体如今经过CSS渲染的内容方面,但对JavaScript也有一些关联影响,或称为反作用。本书会常常提到这些反作用。
IE初次支持文档模式切换之后,其余浏览器也跟着实现了。随着浏览器的广泛实现,又出现了第三种文档模式:准标准模式(almost
standards mode)。这种模式下的浏览器支持不少标准的特性,可是没有标准规定得那么严格。主要区别在于如何对待图片元素周围的空白(在表格中使用图片时最明显)。
混杂模式在全部浏览器中都以省略文档开头的 doctype 声明做为开关。这种约定并不合理,由于混杂模式在不一样浏览器中的差别很是大,不使用黑科技基本上就没有浏览器一致性可言。
2-四、noscript 元素
针对早期浏览器不支持JavaScript的问题,须要一个页面优雅降级的处理方案。最终, 元素出现,被用于给不支持JavaScript的浏览器提供替代内容。虽然现在的浏览器已经100%支持JavaScript,但对于禁用JavaScript的浏览器来讲,这个元素仍然有它的用处。
元素能够包含任何能够出如今 中的HTML元素,
- 浏览器不支持脚本
- 浏览器对脚本的支持被关闭
3、语法基础
3-一、语法
首先要知道的是,ECMAScript中一切都区分大小写。不管是变量、函数名仍是操做符,都区分大小写。换句话说,变量 test 和变量 Test 是两个不一样的变量。相似地, typeof 不能做为函数名,由于它是一个关键字(后面会介绍)。但 Typeof 是一个彻底有效的函数名。
3-二、标识符
所谓标识符,就是变量、函数、属性或函数参数的名称。标识符能够由一或多个下列字符组成:
- 第一个字符必须是一个字母、下划线( _ )或美圆符号( $ );
- 剩下的其余字符能够是字母、下划线、美圆符号或数字。
标识符中的字母能够是扩展ASCII(Extended ASCII)中的字母,也能够是Unicode的字母字符,如À和Æ(但不推荐使用)。
按照惯例,ECMAScript标识符使用驼峰大小写形式,即第一个单词的首字母小写,后面每一个单词的首字母大写
3-三、严格模式
ECMAScript 5增长了严格模式(strict mode)的概念。严格模式是一种不一样的JavaScript解析和执行模型,ECMAScript 3的一些不规范写法在这种模式下会被处理,对于不安全的活动将抛出错误
3-四、关键字与保留字
ECMA-262描述了一组保留的关键字,这些关键字有特殊用途,好比表示控制语句的开始和结束,或者执行特定的操做
ECMA-262第6版规定的全部关键字:
break do in typeof case else instanceof var catch export new void class extends return while const finally super with continue for switch yield debugger function this default if throw delete import try
规范中也描述了一组将来的保留字,一样不能用做标识符或属性名。虽然保留字在语言中没有特定用途,但它们是保留给未来作关键字用的
3-五、变量
ECMAScript变量是松散类型的,意思是变量能够用于保存任何类型的数据。每一个变量只不过是一个用于保存任意值的命名占位符。有3个关键字能够声明变量: var 、 const 和 let 。其中, var 在CMAScript的全部版本中均可以使用,而 const 和 let 只能在ECMAScript 6及更晚的版本中使用
3-六、var关键字
-
var声明做用域
关键的问题在于,使用 var 操做符定义的变量会成为包含它的函数的局部变量。好比,使用 var 在一个函数内部定义一个变量,就意味着该变量将在函数退出时被销毁 -
var 声明提高
使用 var 时,下面的代码不会报错。这是由于使用这个关键字声明的变量会自动提高到函数做用域顶部
3-七、let 声明
let 跟 var 的做用差很少,但有着很是重要的区别。最明显的区别是, let 声明的范围是块做用域,而 var 声明的范围是函数做用域。
-
暂时性死区
let 与 var 的另外一个重要的区别,就是 let 声明的变量不会在做用域中被提高。 -
全局声明
与 var 关键字不一样,使用 let 在全局做用域中声明的变量不会成为 window 对象的属性( var 声明的变量则会) -
条件声明
在使用 var 声明变量时,因为声明会被提高,JavaScript引擎会自动将多余的声明在做用域顶部合并为一个声明。由于 let 的做用域是块,因此不可能检查前面是否已经使用 let 声明过同名变量,同时也就不可能在没有声明的状况下声明它 -
for 循环中的 let 声明
在 let 出现以前, for 循环定义的迭代变量会渗透到循环体外部 -
const 声明
const 的行为与 let 基本相同,惟一一个重要的区别是用它声明变量时必须同时初始化变量,且尝试修改 const 声明的变量会致使运行时错误。
3-八、声明风格及最佳实践
ECMAScript 6增长 let 和 const 从客观上为这门语言更精确地声明做用域和语义提供了更好的支持。行为怪异的 var 所形成的各类问题,已经让JavaScript社区为之苦恼了不少年。随着这两个新关键字的出现,新的有助于提高代码质量的最佳实践也逐渐显现。
- 不使用 var
有了 let 和 const ,大多数开发者会发现本身再也不须要 var了。限制本身只使用 let 和 const 有助于提高代码质量,由于变量有了明确的做用域、声明位置,以及不变的值。 - const 优先, let 次之
使用 const 声明可让浏览器运行时强制保持变量不变,也可让静态代码分析工具提早发现不合法的赋值操做。所以,不少开发者认为应该优先使用 const 来声明变量,只在提早知道将来会有修改时,再使用 let 。这样可让开发者更有信心地推断某些变量的值永远不会变,同时也能迅速发现因意外赋值致使的非预期行为。
4、数据类型
ECMAScript有6种简单数据类型(也称为原始类型):Undefined 、 Null 、 Boolean 、 Number 、 String 和Symbol 。 Symbol (符号)是ECMAScript 6新增的。还有一种复杂数据类型叫 Object (对象)。 Object 是一种无序名值对的集合。由于在ECMAScript中不能定义本身的数据类型,全部值均可以用上述7种数据类型之一来表示。只有7种数据类型彷佛不足以表示所有数据。但ECMAScript的数据类型很灵活,一种数据类型能够看成多种数据类型来使用
4-一、typeof 操做符
由于ECMAScript的类型系统是松散的,因此须要一种手段来肯定任意变量的数据类型。 typeof 操做符就是为此而生的。对一个值使用 typeof 操做符会返回下列字符串之一:
“undefined” 表示值未定义;
“boolean” 表示值为布尔值;
“string” 表示值为字符串;
“number” 表示值为数值;
“object” 表示值为对象(而不是函数)或 null ;
“function” 表示值为函数;
“symbol” 表示值为符号。
4-二、Undefined 类型
Undefined 类型只有一个值,就是特殊值 undefined 。当使用 var 或 let 声明了变量但没有初始化时,就至关于给变量赋予了 undefined 值
即便未初始化的变量会被自动赋予 undefined 值,但咱们仍然建议在声明变量的同时进行初始化。这样,当 typeof 返回 “undefined” 时,你就会知道那是由于给定的变量还没有声明,而不是声明了但未初始化。
4-三、Null 类型
Null 类型一样只有一个值,即特殊值 null 。逻辑上讲,null 值表示一个空对象指针,这也是给 typeof 传一个 null 会返回 “object” 的缘由
在定义未来要保存对象值的变量时,建议使用 null 来初始化,不要使用其余值。这样,只要检查这个变量的值是否是 null 就能够知道这个变量是否在后来被从新赋予了一个对象的引用
用等于操做符( == )比较 null 和 undefined 始终返回true 。但要注意,这个操做符会为了比较而转换它的操做数。
即便 null 和 undefined 有关系,它们的用途也是彻底不同的。如前所述,永远没必要显式地将变量值设置为 undefined 。但null 不是这样的。任什么时候候,只要变量要保存对象,而当时又没有那个对象可保存,就要用 null 来填充该变量。这样就能够保持null 是空对象指针的语义,并进一步将其与 undefined 区分开来。
null 是一个假值。所以,若是须要,能够用更简洁的方式检测它。不过要记住,也有不少其余可能的值一样是假值。因此必定要明确本身想检测的就是 null 这个字面值,而不只仅是假值
4-四、Boolean 类型
Boolean (布尔值)类型是ECMAScript中使用最频繁的类型之一,有两个字面值: true 和 false 。这两个布尔值不一样于数值,所以 true 不等于1, false 不等于0
注意:布尔值字面量 true 和 false 是区分大小写的,所以True 和 False (及其余大小混写形式)是有效的标识符,但不是布尔值。
4-五、Number 类型
ECMAScript中最有意思的数据类型或许就是 Number 了。Number 类型使用IEEE 754格式表示整数和浮点值(在某些语言中也叫双精度值)。不一样的数值类型相应地也有不一样的数值字面量格式
-
浮点值
要定义浮点值,数值中必须包含小数点,并且小数点后面必须至
少有一个数字。虽然小数点前面不是必须有整数,但推荐加上。 -
值的范围
因为内存的限制,ECMAScript并不支持表示这个世界上的全部数值。ECMAScript能够表示的最小数值保存在Number.MIN_VALUE 中,这个值在多数浏览器中是5e-324;能够表示的最大数值保存在 Number.MAX_VALUE 中,这个值在多数浏览器中是1.797 693 134 862 315 7e+308。若是某个计算获得的数值结果超出了JavaScript能够表示的范围,那么这个数值会被自动转换为一个特殊的 Infinity (无穷)值。任何没法表示的负数以 -Infinity (负无穷大)表示,任何没法表示的正数以 Infinity (正无穷大)表示。若是计算返回正 Infinity 或负 Infinity ,则该值将不能再进一步用于任何计算。这是由于 Infinity 没有可用于计算的数值表示形式。要肯定一个值是否是有限大(即介于JavaScript能表示的最小值和最大值之间),可使用 isFinite() 函数 -
NaN
有一个特殊的数值叫 NaN ,意思是“不是数值”(Not a Number),用于表示原本要返回数值的操做失败了(而不是抛出错误)。好比,用0除任意数值在其余语言中一般都会致使错误,从而停止代码执行。但在ECMAScript中,0、+0或-0相除会返回NaN -
数值转换
有3个函数能够将非数值转换为数值: Number() 、parseInt() 和 parseFloat() 。 Number() 是转型函数,可用于任何数据类型。后两个函数主要用于将字符串转换为数值。对于一样的参数,这3个函数执行的操做也不一样。
4-六、 NaN
有一个特殊的数值叫 NaN ,意思是“不是数值”(Not aNumber),用于表示原本要返回数值的操做失败了(而不是抛出错误)。
4-七、数值转换
有3个函数能够将非数值转换为数值: Number() 、 parseInt() 和 parseFloat() 。 Number() 是转型函数,可用于任何数据类型。后两个函数主要用于将字符串转换为数值。对于一样的参数,这3个函数执行的操做也不一样。Number() 函数基于以下规则执行转换。布尔值, true 转换为1, false 转换为0。
数值,直接返回。null ,返回0。 undefined ,返回 NaN 。
4-八、String 类型
String (字符串)数据类型表示零或多个16位Unicode字符序列。字符串可使用双引号(")、单引号(’)或反引号(`)标示,
-
字符字面量
字符串数据类型包含一些字符字面量,用于表示非打印字符或有其余用途的字符 -
字符串的特色
ECMAScript中的字符串是不可变的(immutable),意思是一旦建立,它们的值就不能变了。要修改某个变量中的字符串值,必须先销毁原始的字符串,而后将包含新值的另外一个字符串保存到该变量 -
转换为字符串
有两种方式把一个值转换为字符串。首先是使用几乎全部值都有的 toString() 方法。这个方法惟一的用途就是返回当前值的字符串等价物 -
模板字面量
ECMAScript 6新增了使用模板字面量定义字符串的能力。与使用单引号或双引号不一样,模板字面量保留换行字符,能够跨行定义字符串 -
字符串插值
模板字面量最经常使用的一个特性是支持字符串插值,也就是能够在一个连续定义中插入一个或多个值。技术上讲,模板字面量不是字符串,而是一种特殊的JavaScript句法表达式,只不过求值后获得的是字符串。模板字面量在定义时当即求值并转换为字符串实例,任何插入的变量也会从它们最接近的做用域中取值 -
模板字面量标签函数
模板字面量也支持定义标签函数(tag function),而经过标签函数能够自定义插值行为。标签函数会接收被插值记号分隔后的模板和对每一个表达式求值的结果。标签函数自己是一个常规函数,经过前缀到模板字面量来应用自定义行为, -
原始字符串
使用模板字面量也能够直接获取原始的模板字面量内容(如换行符或Unicode字符),而不是被转换后的字符表示。为此,可使用默认的 String.raw 标签函数
5、操做符
ECMA-262描述了一组可用于操做数据值的操做符,包括数学操做符(如加、减)、位操做符、关系操做符和相等操做符等。ECMAScript中的操做符是独特的,由于它们可用于各类值,包括字符串、数值、布尔值,甚至还有对象。在应用给对象时,操做符一般会调用 valueOf() 和 / 或 toString() 方法来取得能够计算的值。3.5.1 一元操做符只操做一个值的操做符叫一元操做符(unary operator)。一元操做符是ECMAScript中最简单的操做符。
5-一、一元操做符
- 递增 / 递减操做符
递增和递减操做符直接照搬自C语言,但有两个版本:前缀版和后缀版。顾名思义,前缀版就是位于要操做的变量前头,后缀版就是位于要操做的变量后头。前缀递增操做符会给数值加1,把两个加号( ++ )放到变量前头便可
2.一元加和减
一元加和减操做符对大多数开发者来讲并不陌生,它们在ECMAScript中跟在高中数学中的用途同样。一元加由一个加号( + )表示,放在变量前头,对数值没有任何影响
5-二、位操做符
-
按位非
按位非操做符用波浪符( ~ )表示,它的做用是返回数值的一补数。按位非是ECMAScript中为数很少的几个二进制数学操做符之一 -
按位与
按位与操做符用和号( & )表示,有两个操做数。本质上,按位与就是将两个数的每个位对齐,而后基于真值表中的规则,对每一位执行相应的与操做。 -
按位或
按位或操做符用管道符( | )表示,一样有两个操做数。 -
按位异或
按位异或用脱字符( ^ )表示,一样有两个操做数 -
左移
左移操做符用两个小于号( << )表示,会按照指定的位数将数值的全部位向左移动。好比,若是数值2(二进制10)向左移5位,就会获得64(二进制1000000) -
有符号右移
有符号右移由两个大于号( >> )表示,会将数值的全部32位都向右移,同时保留符号(正或负)。有符号右移其实是左移的逆运算 -
无符号右移
无符号右移用3个大于号表示( >>> ),会将数值的全部32位都向右移。对于正数,无符号右移与有符号右移结果相同
5-三、布尔操做符
-
逻辑非
逻辑非操做符由一个叹号( ! )表示,可应用给ECMAScript中的任何值。这个操做符始终返回布尔值,不管应用到的是什么数据类型。逻辑非操做符首先将操做数转换为布尔值,而后再对其取反 -
逻辑与
逻辑与操做符由两个和号( && )表示,应用到两个值 -
逻辑或
逻辑或操做符由两个管道符( || )表示
5-四、乘性操做符
-
乘法操做符
乘法操做符由一个星号( * )表示,能够用于计算两个数值的乘积。 -
除法操做符
除法操做符由一个斜杠( / )表示,用于计算第一个操做数除以第二个操做数的商 -
取模操做符
- 取模(余数)操做符由一个百分比符号( % )表示
- 若是操做数是数值,则执行常规除法运算,返回余数。
- 若是被除数是无限值,除数是有限值,则返回 NaN 。
- 若是被除数是有限值,除数是0,则返回 NaN 。
- 若是是 Infinity 除以 Infinity ,则返回 NaN 。
- 若是被除数是有限值,除数是无限值,则返回被除数。
- 若是被除数是0,除数不是0,则返回0。
- 若是有不是数值的操做数,则先在后台用 Number() 函数将其转换为数值,而后再应用上述规则。
6、语句
ECMA-262描述了一些语句(也称为流控制语句),而ECMAScript中的大部分语法都体如今语句中。语句一般使用一或多个关键字完成既定的任务。语句能够简单,也能够复杂。简单的如告诉函数退出,复杂的如列出一堆要重复执行的指令。
6-一、do-while 语句
do-while 语句是一种后测试循环语句,即循环体中的代码执
行后才会对退出条件进行求值。换句话说,循环体内的代码至少执行
一次
6-二、while 语句
while 语句是一种先测试循环语句,即先检测退出条件,再执行循环体内的代码。所以, while 循环体内的代码有可能不会执行
7、函数
函数对任何语言来讲都是核心组件,由于它们能够封装语句,而后在任何地方、任什么时候间执行。ECMAScript中的函数使用function 关键字声明,后跟一组参数,而后是函数体。
ECMAScript中的函数不须要指定是否返回值。任何函数在任什么时候间均可以使用 return 语句来返回函数的值,用法是后跟要返回的值
函数 sum() 会将两个值相加并返回结果。注意,除了 return语句以外没有任何特殊声明代表该函数有返回值
严格模式对函数也有一些限制:
函数不能以 eval 或 arguments 做为名称;
函数的参数不能叫 eval 或 arguments ;
两个函数的参数不能叫同一个名称。
8、变量,做用域与内存
4-一、原始值与引用值
ECMAScript变量能够包含两种不一样类型的数据:原始值和引用值。原始值(primitive value)就是最简单的数据,引用值(referencevalue)则是由多个值构成的对象。
在把一个值赋给变量时,JavaScript引擎必须肯定这个值是原始值仍是引用值。上一章讨论了6种原始值: Undefined 、 Null 、Boolean 、 Number 、 String 和 Symbol 。保存原始值的变量是按值(by value)访问的,由于咱们操做的就是存储在变量中的实际值。
引用值是保存在内存中的对象。与其余语言不一样,JavaScript不容许直接访问内存位置,所以也就不能直接操做对象所在的内存空间。在操做对象时,实际上操做的是对该对象的引用(reference)而非实
际的对象自己。为此,保存引用值的变量是按引用(by reference)访问的
注意:在不少语言中,字符串是使用对象表示的,所以被认为是引用类型。ECMAScript打破了这个惯例。
-
动态属性
原始值和引用值的定义方式很相似,都是建立一个变量,而后给它赋一个值。不过,在变量保存了这个值以后,能够对这个值作什么,则大有不一样。对于引用值而言,能够随时添加、修改和删除其属性和方法 -
复制值
除了存储方式不一样,原始值和引用值在经过变量复制时也有所不一样。在经过变量把一个原始值赋值到另外一个变量时,原始值会被复制到新变量的位置。
- 在把引用值从一个变量赋给另外一个变量时,存储在变量中的值也会被复制到新变量所在的位置。区别在于,这里复制的值其实是一个指针,它指向存储在堆内存中的对象。操做完成后,两个变量实际上指向同一个对象,所以一个对象上面的变化会在另外一个对象上反映出来
- 传递参数
ECMAScript中全部函数的参数都是按值传递的。这意味着函数外的值会被复制到函数内部的参数中,就像从一个变量复制到另外一个变量同样。若是是原始值,那么就跟原始值变量的复制同样,若是是引用值,那么就跟引用值变量的复制同样。对不少开发者来讲,这一块可能会很差理解,毕竟变量有按值和按引用访问,而传参则只有按值传递
在按值传递参数时,值会被复制到一个局部变量(即一个命名参数,或者用ECMAScript的话说,就是 arguments 对象中的一个槽位)。在按引用传递参数时,值在内存中的位置会被保存在一个局部变量,这意味着对本地变量的修改会反映到函数外部
- 肯定类型
前一章提到的 typeof 操做符最适合用来判断一个变量是否为原始类型。更确切地说,它是判断一个变量是否为字符串、数值、布尔值或 undefined 的最好方式。若是值是对象或 null ,那么typeof 返回 “object”
按照定义,全部引用值都是 Object 的实例,所以经过instanceof 操做符检测任何引用值和 Object 构造函数都会返回 true 。相似地,若是用 instanceof 检测原始值,则始终会返回 false ,由于原始值不是对象。
注意 typeof 操做符在用于检测函数时也会返回 “function” 。当在Safari(直到Safari 5)和Chrome(直到Chrome 7)中用于检测正则表达式时,因为实现细节的缘由,typeof 也会返回 “function” 。ECMA-262规定,任何实现内部 [[Call]] 方法的对象都应该在 typeof 检测时返回 “function” 。由于上述浏览器中的正则表达式实现了这个方法,因此 typeof 对正则表达式也返回 “function” 。在IE和Firefox中, typeof 对正则表达式返回 “object” 。
4-二、执行上下文与做用域
执行上下文(以上简称“上下文”)的概念在JavaScript中是颇为重要的。变量或函数的上下文决定了它们能够访问哪些数据,以及它们的行为。每一个上下文都有一个关联的变量对象(variable object),而
这个上下文中定义的全部变量和函数都存在于这个对象上。虽然没法经过代码访问变量对象,但后台处理数据会用到它。
全局上下文是最外层的上下文。根据ECMAScript实现的宿主环境,表示全局上下文的对象可能不同。在浏览器中,全局上下文就是咱们常说的 window 对象(第12章会详细介绍),所以全部经过
var 定义的全局变量和函数都会成为 window 对象的属性和方法。使用 let 和 const 的顶级声明不会定义在全局上下文中,但在做用域链解析上效果是同样的。上下文在其全部代码都执行完毕后会被销毁,包括定义在它上面的全部变量和函数(全局上下文在应用程序退出前才会被销毁,好比关闭网页或退出浏览器)。
每一个函数调用都有本身的上下文。当代码执行流进入函数时,函数的上下文被推到一个上下文栈上。在函数执行完以后,上下文栈会弹出该函数上下文,将控制权返还给以前的执行上下文。ECMAScript
程序的执行流就是经过这个上下文栈进行控制的。上下文中的代码在执行的时候,会建立变量对象的一个做用域链(scope chain)。这个做用域链决定了各级上下文中的代码在访问变量和函数时的顺序。代码正在执行的上下文的变量对象始终位于做用域链的最前端。若是上下文是函数,则其活动对象(activationobject)用做变量对象。活动对象最初只有一个定义变量:arguments 。(全局上下文中没有这个变量。)做用域链中的下一个变量对象来自包含上下文,再下一个对象来自再下一个包含上下文。以此类推直至全局上下文;全局上下文的变量对象始终是做用域链的最后一个变量对象
-
做用域链加强
虽然执行上下文主要有全局上下文和函数上下文两种( eval()调用内部存在第三种上下文),但有其余方式来加强做用域链。某些语句会致使在做用域链前端临时添加一个上下文,这个上下文在代码
执行后会被删除。 -
变量声明
ES6以后,JavaScript的变量声明经历了翻天覆地的变化。直到
ECMAScript 5.1, var 都是声明变量的惟一关键字。ES6不只增长了
let 和 const 两个关键字,并且还让这两个关键字压倒性地超越
var 成为首选。
-
使用 var 的函数做用域声明
在使用 var 声明变量时,变量会被自动添加到最接近的上下文。在函数中,最接近的上下文就是函数的局部上下文。在with 语句中,最接近的上下文也是函数上下文。若是变量未经声明就被初始化了,那么它就会自动被添加到全局上下文
注意:未经声明而初始化变量是JavaScript编程中一个很是常见的错误,会致使不少问题。为此,读者在初始化变量以前必定要先声明变量。在严格模式下,未经声明就初始化变量会报错
var 声明会被拿到函数或全局做用域的顶部,位于做用域中全部代码以前。这个现象叫做“提高”(hoisting)。提高让同一做用域中的代码没必要考虑变量是否已经声明就能够直接使用。但是在实践中,提高也会致使合法却奇怪的现象,即在变量声明以前使用变量。 -
使用 let 的块级做用域声明
ES6新增的 let 关键字跟 var 很类似,但它的做用域是块级的,这也是JavaScript中的新概念。块级做用域由最近的一对包含花括号 {} 界定。换句话说, if 块、 while 块、 function块,甚至连单独的块也是 let 声明变量的做用域。 -
使用 const 的常量声明
除了 let ,ES6同时还增长了 const 关键字。使用 const 声明的变量必须同时初始化为某个值。一经声明,在其生命周期的任什么时候候都不能再从新赋予新值
注意 开发实践代表,若是开发流程并不会所以而受很大影响,就应该尽量地多使用 const 声明,除非确实须要一个未来会从新赋值的变量。这样能够从根本上保证提早发现从新赋值致使的bug
4-三、垃圾回收
JavaScript是使用垃圾回收的语言,也就是说执行环境负责在代码执行时管理内存。在C和C++等语言中,跟踪内存使用对开发者来讲是个很大的负担,也是不少问题的来源。JavaScript为开发者卸下了这个负担,经过自动内存管理实现内存分配和闲置资源回收。基本思路很简单:肯定哪一个变量不会再使用,而后释放它占用的内存。这个过程是周期性的,即垃圾回收程序每隔必定时间(或者说在代码执行过程当中某个预约的收集时间)就会自动运行。垃圾回收过程是一个近似且不完美的方案,由于某块内存是否还有用,属于“不可断定的”问题,意味着靠算法是解决不了的。
咱们以函数中局部变量的正常生命周期为例。函数中的局部变量会在函数执行时存在。此时,栈(或堆)内存会分配空间以保存相应的值。函数在内部使用了变量,而后退出。此时,就再也不须要那个局部变量了,它占用的内存能够释放,供后面使用。这种状况下显然再也不须要局部变量了,但并非全部时候都会这么明显。垃圾回收程序必须跟踪记录哪一个变量还会使用,以及哪一个变量不会再使用,以便回收内存。如何标记未使用的变量也许有不一样的实现方式。不过,在浏览器的发展史上,用到过两种主要的标记策略:标记清理和引用计数。
-
标记清理
JavaScript最经常使用的垃圾回收策略是标记清理(mark-and-sweep)。当变量进入上下文,好比在函数内部声明一个变量时,这个变量会被加上存在于上下文中的标记。而不在上下文中的变量,逻辑上讲,永远不该该释放它们的内存,由于只要上下文中的代码在运行,就有可能用到它们。当变量离开上下文时,也会被加上离开上下文的标记。
给变量加标记的方式有不少种。好比,当变量进入上下文时,反转某一位;或者能够维护“在上下文中”和“不在上下文中”两个变量列表,能够把变量从一个列表转移到另外一个列表。标记过程的实现并不重要,关键是策略。
垃圾回收程序运行的时候,会标记内存中存储的全部变量(记住,标记方法有不少种)。而后,它会将全部在上下文中的变量,以及被在上下文中的变量引用的变量的标记去掉。在此以后再被加上标记的变量就是待删除的了,缘由是任何在上下文中的变量都访问不到它们了。随后垃圾回收程序作一次内存清理,销毁带标记的全部值并收回它们的内存。
到了2008年,IE、Firefox、Opera、Chrome和Safari都在本身的JavaScript实现中采用标记清理(或其变体),只是在运行垃圾回收的频率上有所差别。 -
引用计数
另外一种没那么经常使用的垃圾回收策略是引用计数(referencecounting)。其思路是对每一个值都记录它被引用的次数。声明变量并给它赋一个引用值时,这个值的引用数为1。若是同一个值又被赋给另外一个变量,那么引用数加1。相似地,若是保存对该值引用的变量被其余值给覆盖了,那么引用数减1。当一个值的引用数为0时,就说明没办法再访问到这个值了,所以能够安全地收回其内存了。垃圾回收程序
下次运行的时候就会释放引用数为0的值的内存。引用计数最先由Netscape Navigator 3.0采用,但很快就遇到了严重的问题:循环引用。所谓循环引用,就是对象A有一个指针指向对象B,而对象B也引用了对象A。
- 把变量设置为 null 实际上会切断变量与其以前引用值之间的关系。当下次垃圾回收程序运行时,这些值就会被删除,内存也会被回收。
为了补救这一点,IE9把BOM和DOM对象都改为了JavaScript对象,这同时也避免了因为存在两套垃圾回收算法而致使的问题,还消除了常见的内存泄漏现象。
- 内存泄漏
写得很差的JavaScript可能出现难以察觉且有害的内存泄漏问题。在内存有限的设备上,或者在函数会被调用不少次的状况下,内存泄漏多是个大问题。JavaScript中的内存泄漏大部分是由不合理的引用致使的
JavaScript变量能够保存两种类型的值:原始值和引用值。原始值多是如下6种原始数据类型之一: Undefined 、 Null 、Boolean 、 Number 、 String 和 Symbol 。原始值和引用值有如下特色。原始值大小固定,所以保存在栈内存上。
从一个变量到另外一个变量复制原始值会建立该值的第二个副本。引用值是对象,存储在堆内存上。包含引用值的变量实际上只包含指向相应对象的一个指针,而不是对象自己。从一个变量到另外一个变量复制引用值只会复制指针,所以结果是两个变量都指向同一个对象。
typeof 操做符能够肯定值的原始类型,而 instanceof 操做符用于确保值的引用类型。任何变量(无论包含的是原始值仍是引用值)都存在于某个执行上下文中(也称为做用域)。这个上下文(做用域)决定了变量的生命周期,以及它们能够访问代码的哪些部分。执行上下文能够总结以下。
执行上下文分全局上下文、函数上下文和块级上下文。
代码执行流每进入一个新上下文,都会建立一个做用域链,用于搜索变量和函数。
函数或块的局部上下文不只能够访问本身做用域内的变量,并且也能够访问任何包含上下文乃至全局上下文中的变量。
全局上下文只能访问全局上下文中的变量和函数,不能直接访问局部上下文中的任何数据。
变量的执行上下文用于肯定何时释放内存。
JavaScript是使用垃圾回收的编程语言,开发者不须要操心内存分配和回收。JavaScript的垃圾回收程序能够总结以下。
离开做用域的值会被自动标记为可回收,而后在垃圾回收期间被删除。
主流的垃圾回收算法是标记清理,即先给当前不使用的值加上标记,再回来回收它们的内存。
引用计数是另外一种垃圾回收策略,须要记录值被引用了多少次。
JavaScript引擎再也不使用这种算法,但某些旧版本的IE仍然会受这种算法的影响,缘由是JavaScript会访问非原生JavaScript对象(如DOM元素)。
引用计数在代码中存在循环引用时会出现问题。
解除变量的引用不只能够消除循环引用,并且对垃圾回收也有帮助。为促进内存回收,全局对象、全局对象的属性和循环引用都应该在不须要时解除引用。
9、基本引用类型
引用值(或者对象)是某个特定引用类型的实例。在ECMAScript中,引用类型是把数据和功能组织到一块儿的结构,常常被人错误地称做“类”。虽然从技术上讲JavaScript是一门面向对象语言,但ECMAScript缺乏传统的面向对象编程语言所具有的某些基本结构,包括类和接口。引用类型有时候也被称为对象定义,由于它们描述了本身的对象应有的属性和方法。
注意 引用类型虽然有点像类,但跟类并非一个概念。为避免混淆,本章后面不会使用术语“类”。
对象被认为是某个特定引用类型的实例。新对象经过使用 new 操做符后跟一个构造函数(constructor)来建立。构造函数就是用来建立新对象的函数
9-一、Date
ECMAScript的 Date 类型参考了Java早期版本中的java.util.Date 。为此, Date 类型将日期保存为自协调世界时(UTC,Universal Time Coordinated)时间1970年1月1日午夜(零时)至今所通过的毫秒数。使用这种存储格式, Date 类型能够精确表示1970年1月1日以前及以后285 616年的日期。
-
要建立日期对象,就使用 new 操做符来调用 Date 构造函数:
let now = new Date();
-
在不给 Date 构造函数传参数的状况下,建立的对象将保存当前日期和时间。要基于其余日期和时间建立日期对象,必须传入其毫秒表示(UNIX纪元1970年1月1日午夜以后的毫秒数)。ECMAScript为此提供了两个辅助方法: Date.parse() 和 Date.UTC() 。
Date.parse() 方法接收一个表示日期的字符串参数,尝试将这个字符串转换为表示该日期的毫秒数。ECMA-262第5版定义了Date.parse() 应该支持的日期格式,填充了第3版遗留的空白
-
继承的方法
与其余类型同样, Date 类型重写了 toLocaleString() 、toString() 和 valueOf() 方法。但与其余类型不一样,重写后这些方法的返回值不同。 Date 类型的 toLocaleString() 方法返回与浏览器运行的本地环境一致的日期和时间。这一般意味着格式中包含针对时间的AM(上午)或PM(下午),但不包含时区信息(具体格式可能因浏览器而不一样)。 toString() 方法一般返回带时区信息的日期和时间,而时间也是以24小时制(0~23)表示的。 -
日期格式化方法
- Date 类型有几个专门用于格式化日期的方法,它们都会返回字符串:
- toDateString() 显示日期中的周几、月、日、年(格式特定于实现);
- toTimeString() 显示日期中的时、分、秒和时区(格式特定于实现);
- toLocaleDateString() 显示日期中的周几、月、日、年(格式特定于实现和地区);
- toLocaleTimeString() 显示日期中的时、分、秒(格式特定于实现);
- toUTCString() 显示完整的UTC日期(格式特定于实现)。
- 这些方法的输出与 toLocaleString() 和 toString() 同样,会因浏览器而异。所以不能用于在用户界面上一致地显示日期。
注意 还有一个方法叫 toGMTString() ,这个方法跟toUTCString() 是同样的,目的是为了向后兼容。不过,规范建议新代码使用 toUTCString() 。
- 日期/时间组件方法
Date 类型剩的方法(见下表)直接涉及取得或设置日期值的特定部分。注意表中“UTC日期”,指的是没有时区偏移(将日期转换为GMT)时的日期。
方法 | 说明 |
---|---|
getTime() | 返回日期的毫秒表示;与valueOf() 相同 |
setTime(milliseconds) | 设置日期的毫秒表示,从而修改整个日期 |
getFullYear() | 返回4位数年(即2019而不是19) |
getUTCFullYear() | 返回UTC日期的4位数年 |
setFullYear( year ) | 设置日期的年( year 必须是4位数) |
导setUTCFullYear( year ) | 设置UTC日期的年( year 必须是4位数) |
getMonth() | 返回日期的月(0表示1月,11表示12月) |
getUTCMonth() | 返回UTC日期的月(0表示1月,11表示12月) |
setMonth( month) | ( month 为大于0的数值,大于11加年) |
setUTCMonth( month) | 设置UTC日期的月( month 为大于0的数值,大于11加年) |
getDate() | 返回日期中的日(1~31) |
getUTCDate() | 返回UTC日期中的日(1~31) |
setDate( date) | 设置日期中的日(若是 date 大于该月天数,则加月) |
setUTCDate( date | 设置UTC日期中的日(若是date 大于该月天数,则加月) |
getDay() | 返回日期中表示周几的数值(0表示周日,6表示周六) |
getUTCDay() | 返回UTC日期中表示周几的数值(0表示周日,6表示周六) |
getHours() | 返回日期中的时(0~23) |
getUTCHours() | 返回UTC日期中的时(0~23) |
setHours( hours ) | 设置日期中的时(若是 hours大于23,则加日) |
setUTCHours( hours ) | 设置UTC日期中的时(若是hours 大于23,则加日) |
getMinutes() | 返回日期中的分(0~59) |
getUTCMinutes() | 返回UTC日期中的分(0~59) |
setMinutes( minutes) | 设置日期中的分(若是minutes 大于59,则加时) |
setUTCMinutes( minutes ) | 设置UTC日期中的分(若是minutes 大于59,则加时) |
getSeconds() | 返回日期中的秒(0~59) |
getUTCSeconds() | 返回UTC日期中的秒(0~59) |
setSeconds( seconds ) | 设置日期中的秒(若是seconds 大于59,则加分) |
setUTCSeconds( seconds) | 设置UTC日期中的秒(若是seconds 大于59,则加分) |
getMilliseconds() | 返回日期中的毫秒 |
getUTCMilliseconds() | 返回UTC日期中的毫秒 |
setMilliseconds( milliseconds) | 设置日期中的毫秒 |
setUTCMilliseconds( milliseconds ) | 设置UTC日期中的毫秒 |
getTimezoneOffset() | 返回以分钟计的UTC与本地时区的偏移量(如美国EST即“东部标准时间”返回300,进入夏令时的地区可能有所差别) |
9-二、RegExp
ECMAScript经过 RegExp 类型支持正则表达式。正则表达式使用相似Perl的简洁语法来建立:
let expression = /pattern/flags;
这个正则表达式的 pattern (模式)能够是任何简单或复杂的正则表达式,包括字符类、限定符、分组、向前查找和反向引用。每一个正则表达式
能够带零个或多个 flags (标记),用于控制正则表达式的行为。下面给
出了表示匹配模式的标记
- g :全局模式,表示查找字符串的所有内容,而不是找到第一个匹配的内容就结束。
- i :不区分大小写,表示在查找匹配时忽略 pattern 和字符串的大小写。
- m :多行模式,表示查找到一行文本末尾时会继续查找。
- y :粘附模式,表示只查找从 lastIndex 开始及以后的字符串。
- u :Unicode模式,启用Unicode匹配。
- s : dotAll 模式,表示元字符 . 匹配任何字符(包括 \n 或\r )。
- RegExp 实例属性
每一个 RegExp 实例都有下列属性,提供有关模式的各方面信息。
- global :布尔值,表示是否设置了 g 标记。
- ignoreCase :布尔值,表示是否设置了 i 标记。
- unicode :布尔值,表示是否设置了 u 标记。
- sticky :布尔值,表示是否设置了 y 标记。
- lastIndex :整数,表示在源字符串中下一次搜索的开始位置,始终从0开始。
- multiline :布尔值,表示是否设置了 m 标记。
- dotAll :布尔值,表示是否设置了 s 标记。
- source :正则表达式的字面量字符串(不是传给构造函数的模式字符串),没有开头和结尾的斜杠。
- flags :正则表达式的标记字符串。始终以字面量而非传入构造函数的字符串模式形式返回(没有先后斜杠)。
-
RegExp 实例方法
RegExp 实例的主要方法是 exec() ,主要用于配合捕获组使用。这个方法只接收一个参数,即要应用模式的字符串。若是找到了匹配项,则返
回包含第一个匹配信息的数组;若是没找到匹配项,则返回 null 。返回
的数组虽然是 Array 的实例,但包含两个额外的属性: index 和
input 。 index 是字符串中匹配模式的起始位置, input 是要查找的
字符串。这个数组的第一个元素是匹配整个模式的字符串,其余元素是与表
达式中的捕获组匹配的字符串。若是模式中没有捕获组,则数组只包含一个
元素。 -
RegExp 构造函数属性
RegExp 构造函数自己也有几个属性。(在其余语言中,这种属性被称为静态属性。)这些属性适用于做用域中的全部正则表达式,并且会根据最后执行的正则表达式操做而变化。这些属性还有一个特色,就是能够经过两种不一样的方式访问它们。换句话说,每一个属性都有一个全名和一个简写。下表列出了 RegExp 构造函数的属性。
全名 | 简写 | 说明 |
---|---|---|
input | $_ | 最后搜索的字符串 |
lastMatch | $& | 最后匹配的文本 |
lastParen | $+ | 最后匹配的捕获组 |
leftContext | $` | input 字符串中出如今lastMatch 前面的文本 |
rightContext | $’ | input 字符串中出如今lastMatch 后面的文本 |
- 模式局限
虽然ECMAScript对正则表达式的支持有了长足的进步,但仍然缺乏Perl
语言中的一些高级特性。参考Regular-Expressions.info网站
9-三、原始值包装类型
为了方便操做原始值,ECMAScript提供了3种特殊的引用类型:Boolean 、 Number 和 String 。这些类型具备本章介绍的其余引用类型同样的特色,但也具备与各自原始类型对应的特殊行为。每当用到某个原始值的方法或属性时,后台都会建立一个相应原始包装类型的对象,从而暴露出操做原始值的各类方法。
- Boolean
Boolean 是对应布尔值的引用类型。要建立一个 Boolean 对象,就使用 Boolean 构造函数并传入 true 或 false
待更新。。。
9-四、单例内置对象
ECMA-262对内置对象的定义是“任何由ECMAScript实现提供、与宿主
环境无关,并在ECMAScript程序开始执行时就存在的对象”。这就意味着,
开发者不用显式地实例化内置对象,由于它们已经实例化好了。前面咱们已
经接触了大部份内置对象,包括 Object 、 Array 和 String 。本节介
绍ECMA-262定义的另外两个单例内置对象: Global 和 Math 。
-
Global
Global 对象是ECMAScript中最特别的对象,由于代码不会显式地访问它。ECMA-262规定 Global 对象为一种兜底对象,它所针对的是不属于任何对象的属性和方法。事实上,不存在全局变量或全局函数这种东西。在全局做用域中定义的变量和函数都会变成 Global 对象的属性 。本书前面介绍的函数,包括 isNaN() 、 isFinite() 、 parseInt() 和parseFloat() ,实际上都是 Global 对象的方法。除了这些,Global 对象上还有另一些方法。 -
URL编码方法
encodeURI() 和 encodeURIComponent() 方法用于编码统一资源标识符(URI),以便传给浏览器。有效的URI不能包含某些字符,好比空格。使用URI编码方法来编码URI可让浏览器可以理解它们,同时又以特殊的UTF-8编码替换掉全部无效字符。ecnodeURI() 方法用于对整个URI进行编码,好比 “www.wrox.com/illegal value.js” 。而encodeURIComponent() 方法用于编码URI中单独的组件,好比前面URL中的 “illegal value.js” 。这两个方法的主要区别是,encodeURI() 不会编码属于URL组件的特殊字符,好比冒号、斜杠、问号、井号,而 encodeURIComponent() 会编码它发现的全部非标准字符 -
eval() 方法
最后一个方法多是整个ECMAScript语言中最强大的了,它就是eval() 。这个方法就是一个完整的ECMAScript解释器,它接收一个参数,即一个要执行的ECMAScript(JavaScript)字符串。
4.Global 对象属性
Global 对象有不少属性,其中一些前面已经提到过了。像undefined 、 NaN 和 Infinity 等特殊值都是 Global 对象的属性。此外,全部原生引用类型构造函数,好比 Object 和Function ,也都是 Global 对象的属性。
下表列出了全部这些属性:
属性 | 说明 |
---|---|
undefined | 特殊值undefined |
NaN | 特殊值NaN |
Infinity | 特殊值Infinity |
Object | Object的构造函数 |
Array | Array的构造函数 |
Function | Function的构造函数 |
Boolean | Boolean的构造函数 |
String | String的构造函数 |
Number | Number的构造函数 |
Date | Date的构造函数 |
RegExp | RegExp的构造函数 |
Symbol | Symbol的构造函数 |
Error | Error的构造函数 |
EvalError | EvalError的构造函数 |
RangeError | RangeError的构造函数 |
ReferenceError | ReferenceError 的构造函数 |
SyntaxError | SyntaxError的构造函数 |
TypeError | TypeError 的构造函数 |
URIError | URIError的构造函数 |
-
window 对象
虽然ECMA-262没有规定直接访问 Global 对象的方式,但浏览器将window 对象实现为 Global 对象的代理。所以,全部全局做用域中声明的变量和函数都变成了 window 的属性 -
Math
ECMAScript提供了 Math 对象做为保存数学公式、信息和计算的地方。 Math 对象提供了一些辅助计算的属性和方法。
注意: Math 对象上提供的计算要比直接在JavaScript实现的快得多,因
为 Math 对象上的计算使用了JavaScript引擎中更高效的实现和处理器指
令。但使用 Math 计算的问题是精度会因浏览器、操做系统、指令集和
硬件而异。
- Math 对象属性
Math 对象有一些属性,主要用于保存数学中的一些特殊值
属性 | 说明 |
---|---|
Math.E | 天然对数的基数e的值 |
Math.LN10 | 10为底的天然对数 |
Math.LN2 | 2为底的天然对数 |
Math.LOG2E | 以2为底e的对数 |
Math.LOG10E | 以10为底e的对数 |
Math.PI | π的值 |
Math.SQRT1_2 | 1/2的平方根 |
Math.SQRT2 | 2的平方根 |
- min() 和 max() 方法
Math 对象也提供了不少辅助执行简单或复杂数学计算的方法。min() 和 max() 方法用于肯定一组数值中的最小值和最大值。这两个方法都接收任意多个参数
待更新。。。