JavaScript 诞生于 1995 年。
一个完整的 JavaScript 实现由下列三个不同的部分组成:
ECMA-262 标准没有参照 web 浏览器,它规定了这门语言的下列组成部分:语法、类型、语句、关键字、保留字、操作符、对象。
文档对象模型(DOM,Document Object Model)是针对 XML 但经过扩展用于 HTML 的应用程序编程接口(API)。
浏览器对象模型(BOM,Browser Object Model)从根本上将,BOM 只处理浏览器窗口和框架,但人们习惯上也把所有针对浏览器的 JavaScript 扩展算作 BOM 的一部分,下面就是一些这样的扩展:
JavaScript 是一种专门与网页交互而设计的脚本语言,由下列三个部分组成:
向 HTML 页面中插入 JavaScript 的主要方法就是使用 <script>
元素。
使用 <script>
元素的方式有两种:直接在页面中嵌入 JavaScript 代码和包含外部 JavaScript 文件。
注意:使用 <script>
引入外部 JavaScript 文件时,不要在标签内添加额外的 JS 代码,如果添加了将会被忽略。
按照传统的做法,所有 <script>
元素都应该放在页面中 <head>
元素中,这种做法的目的就是把所有外部文件(CSS文件和JavaScript文件)的引用都放在相同的地方。
可是这种做法有一种缺点就是,必须要等到全部 JS 代码都被下载、解析和执行完成以后,才能开始出现页面的内容(浏览器在遇到 <body>
标签时才开始出现内容)。对于需要很多 JS 代码的页面来说,这会导致页面出现明显的延迟,而在延迟期间页面是一片空白。所以,为了避免这个问题,我们应该把 JS 引用放在 <body>
结束标签前面,即:
<body>
<!-- html代码 -->
<script type="text/javascript" src="index.js"></script>
</body>
<script>
标签的 defer 属性用途是表明脚本在执行时不会影响页面的构造。也就是说,脚本会被延迟到整个页面都解析完毕后再运行。因此,在 <script>
元素中设置 defer 属性,相当于告诉浏览器立即下载,但延迟执行。
假设把 <script>
元素放在 <head>
元素之中,并加上 defer 属性,但其中包含的脚本将延迟到浏览器遇到 </html>
标签后再执行。HTML5 规范要求脚本按照它们出现的先后顺序执行,因此第一个延迟脚本会先于第二个延迟脚本执行,而这两个脚本会先于 DOMContentLoaded 事件执行。但在现实中,延迟脚本不一定会按顺序执行,也不一定会在 DOMContentLoaded 事件触发前执行,因此最好只包含一个延迟脚本。
defer 属性只适用于外部脚本文件。
HTML5 为 <script>
元素定义了 async 属性。与 defer 属性类似,都用于改变处理脚本的行为,也只适用于外部脚本文件,并告诉浏览器立即下载文件。但不同的是,标记为 async 的脚本并不保证按照指定它们的先后顺序执行。
例如,在 <head>
元素中放两个 <script>
元素。第二个脚本文件可能会在第一个脚本文件前执行。指定 async 属性的目的是不让页面等待两个脚本下载和执行,从而异步加载页面其他内容。因此,建议异步脚本不要在加载期间修改 DOM。
异步脚本一定会在页面的 load 事件前执行,但可能会在 DOMContentLoaded 事件触发前或后执行。
使用外部文件的优点:
两种文档模式:混杂模式(quirks mode)、标准模式(standards mode)。
这个元素用以在不支持 JS 的浏览器中显示替代的内容。
包含 <noscript>
元素中的内容只有在下面的情况才会显示出来:
ECMAScript 中的一切(变量、函数名和操作符)都区分大小写。
标识符就是指变量、函数、属性的名字,或函数的参数。
标识符按照以下规则组合:
不能把关键字、保留字、true、false、null 用作标识符。
// 单行注释
/*
这是多行注释
这是多行注释
*/
严格模式是为 JS 定义了一种不同的解析与执行模型。在严格模式下,一些不确定的行为将得到处理,对某些不安全的操作会抛出错误。
要在整个脚本中启用严格模式,可以在顶部添加下列代码:
"use strict";
它是一个编译指示(pragma),用来告诉支持 JS 引擎切换到严格模式。
可以指定函数在严格模式下执行:
function doSomething(){
"use strict";
//函数体
}
ECMAScript 中语句以一个分号结尾。如果省略分号,则由解析器确定语句的结尾。
虽然语句结尾的分号并不是必须的,但最好还是不要省略。因为加上分号可以避免很多错误,也会在某些情况下增进代码性能。
关键字可用于表示控制语句的开始或结束,或用于执行特定操作等。关键字不能用作标识符。
ECMA-262 还描述了一组不能用作标识符的保留字。
ECMAScript 的变量是松散类型的,就是可以用来保存任何类型的数据。
定义变量时要使用 var 操作符(var是一个关键字),后跟变量名(即标识符)。
如果省略 var 操作符可以定义全局变量,但这也不是我们所推荐的。因为在局部作用域中定义的全局变量很难维护,而且如果有意地忽略了 var 操作符,也会由于相应变量不会马上就有定义而导致不必要的混乱。给未经声明的变量赋值在严格模式下会抛出 ReferenceError 错误。
简单数据类型(基本数据类型):undefined、null、boolean、number、string、symbol(ES 6新引入的数据类型)。
复杂数据类型:object。
typeof 用来检测给定变量的数据类型,只适用于基本数据类型的类型判断。
对一个值使用 typeof 操作符可能返回下列某个字符串:
undefined 类型只有一个值,即特殊的 undefined。在使用 var 声明变量但未对其加以初始化时,这个变量的值就是 undefined。
对未初始化的变量执行 typeof 操作符会返回 undefined 值,而对未声明的变量执行 typeof 操作符同样也会返回 undefined 值。
null 类型也只有一个值,这个特殊的值是 null。
使用 typeof 操作符检测 null 值时会返回 “object” :
var car = null
alert(typeof car); // "object"
boolean 类型只有两个值:true、false。
注意:boolean 类型的值是区分大小写的,True、False(以及其他的混合大小写形式)都不是 boolean 的值,只是标识符。
要将一个值转换为其对应的 boolean 值,可以调用转型函数 Boolean()。
八进制字面值的第一位必须是零(0),然后是八进制数字序列(0~7)。如果字面值中的数值超出了范围,那前导零将被忽略,后面的数值将被当作十进制数解析。
十六进制字面值的前两位必须是0x,后面跟任何十六进制数字(09及AF),其他A~F可以大写,也可以小写。
在进行算术计算时,所有以八进制、十六进制表示的数值最后将被转换成十进制数值。
浮点数值就是该数值中必须包含一个小数点,并且小数点后面必须至少有一位数字。
ECMAScript 能够表示的最小数值存在 Number.MIN_VALUE 中,在大多数浏览器中,这个值是 5e-324。
ECMAScript 能够表示的最大数值存在 Number.MAX_VALUE 中,在大多数浏览器中,这个值是 1.7976931348623157e+308。
如果计算结果超出了 JavaScript 数值范围时,那么这个数值将被自动转换成 Infinity 值。如果这个数值为负数,将被转换成 -Infinity(负无穷),如果这个数值为正数,将被转换成 Infinity(正无穷)。
即非数值是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误)。
有两个特点。首先,任何涉及 NaN 的操作都会返回 NaN,这个特点在多步计算中有可能导致问题。其次,NaN 与任何值都不相等,包括 NaN 本身。
alert(NaN == NaN); // false
针对这两个特点,ECMAScript 定义了 isNaN() 函数。这个函数接受一个参数,该参数可以是任何类型,而函数会帮我们确定这个参数是否“不是数值”。某些不是数值的值会直接转换为数值。
alert(isNaN(NaN)); // true
alert(isNaN(10)); // false(10是数值)
alert(isNaN("10")); // false(可以被转换成数值10)
alert(isNaN("blue")); // true(不能转换为数值)
alert(isNaN(true)); // false(可以被转换成数值1)
有三个函数可以把非数值转换为数值:Number()、parseInt()、parseFloat()。
Number() 函数可以用于任何数据类型,另外两个函数则专门用于把字符串转为数值。这三个函数对于同样的输入会有返回不同的结果。
Number() 函数的转换规则:
string 类型用于表示由零或多个 16 位 Unicode 字符组成的字符序列,即字符串。
String 数据类型包含一些特殊的字符字面量,也叫转义序列。
\n 换行
\t 制表
\b 退格
\r 回车
\f 进纸
\\ 斜杠
' 单引号(')
" 双引号(")
ECMAScript 的字符串是不可变的,也就是说,字符串一旦创建,它们的值就不能改变。如果要改变某个变量保存的字符串,首先要摧毁原来的字符串,然后再用另一个包含新值得字符串填充该变量。
把一个值转换为一个字符串有两种方式。第一种是使用几乎每个值都有得 toString() 方法。
在不知道要转换的值是不是 null 或 undefined 的情况时,还可以使用转型函数 String(),这个函数能够将任何类型的值转换为字符串。
String()函数遵循下列转换规则:
ECMAScript 中的对象其实就是一组数据和功能的集合。
对象可以通过执行 new 操作符后跟要创建的对象类型的名称来创建。而创建 Object 类型的实例并为其添加属性和(或)方法,就可以创建自定义对象。
Object 的每个实例都具有下列属性和方法:
操作符包括算术操作符(如加号和减号)、位操作符、关系操作符、相等操作符。
只能操作一个值的操作符叫一元操作符。
前置型、后置型。前置型应该位于要操作的变量之前,后置型应该位于要操作的变量之后。
执行前置递增和递减操作时,变量的值都是在语句被求值以前改变的。
后置的递增和递减操作是在包含它们的语句被求值之后才执行的。
在应用于不同的值时,递增和递减操作符遵循下列规则:
一元加操作符以一个加号(+)表示,放在数值前面,对数值不会产生任何影响。
一元减操作符主要用于表示负数。
位操作符不直接操作 64 位的值,而是先将 64 位的值转换成 32 位的整数,然后执行操作,最后再将结果转换回 64 位。
对于有符号的整数,32 位中的前 31 位用于表示整数的值。第 32 位表示数值的符号:0表示正数,1表示负数。这个表示符号的位叫符号位。
负数以二进制码存储,使用的格式是二进制补码,计算一个数值的二进制补码,经过以下步骤:
如果对非数值应用位操作符,会先使用 Number() 函数将该值转换为一个数值,然后再应用位操作,得到的结果是一个数值。
按位非操作符由一个波浪线(~)表示。
执行按位非得结果就是返回数值得反码。
按位非操作的本质:操作数的负值减 1。
按位非是在数值表示的最底层执行操作,因此速度更快。
按位与操作符由一个和号字符(&)表示,它由两个操作符数。
按位与操作只在两个数值的对应位都是 1 时才返回 1,任何一位是 0,结果都是 0。
按位或操作符由一个竖线(|)表示,也有两个操作符。
按位或操作在有一个位是 1 的情况下就返回 1,而只有在两个位都是 0 的情况下才返回 0。
按位异或操作符由一个插入符号(^)表示,也有两个操作符。
按位异或操作在两个数值对应位上只有一个 1 时才返回 1,如果对应的两位都是 1 或 0,则返回 0。
左移操作符由两个小于号(<<)表示,这个操作符会将数值的所有位向左移动指定的位数。
在向左移位后,原数值的右侧就多出了空位,就用 0 来填充空位。
左移不会影响操作数的符号位。
有符号的右移操作符由两个大于号(>>)表示,这个操作符会将数值向右移动,但保留符号位(即正负号标记)。
在右移过程中,原数值出现空位,用符号位的值来填充空位。
无符号右移操作符由 3 个大于号(>>>)表示,这个操作符会将数值的所有 32 位都向右移动。对正数来说,无符号右移的结果与有符号右移相同。
对于负数来说,无符号右移是以 0 来填充空位。
布尔操作符一共有 3 个:非(NOT)、与(AND)、或(OR)。
逻辑非操作符由一个叹号(!)表示。逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再对其求反。
逻辑非操作符遵循以下规则:
逻辑非操作符也可以用于将一个值转换为与其对应的布尔值,用同时使用两个逻辑非操作符(!!)。
逻辑与操作符由两个和号(&&)表示,有两个操作数。
在有一个操作数不是布尔值的情况下,逻辑与操作不一定返回布尔值,此时遵循下列规则:
逻辑与操作属于短路操作,即第一个操作数能够决定结果,那么就不会再对第二个操作数求值。
逻辑或操作符由两个竖线符号(||)表示,也有两个操作数。
遵循以下规则:
逻辑或操作符也是短路操作符,如果第一个操作数的求值结果为 true,就不会对第二个操作数求值。
3 个乘性操作符:乘法、除法、求模。
乘法操作符由一个星号(*)表示,用于计算两个数值的乘积。
在处理特殊值情况下,遵循下列特殊规则:
除法操作符由一个斜线符号(/)表示,执行第二个操作数除第一个操作数的计算。
在处理特殊值情况下,遵循下列特殊规则:
求模(余数)操作符由一个百分号(%)表示。
在处理特殊值情况下,遵循下列特殊规则:
加法操作符(+)
如果两个操作符都是数值,执行常规的加法计算,然后根据下列规则返回结果:
如果有一个操作符是字符串,就要应用如下规则:
如果有一个操作数是对象、数值、布尔值,则调用它们的 toString() 方法取得相应的字符串值,然后再应用关于字符串的规则。
对于 undefined 和 null,则分别调用 String() 函数并取得字符串“undefined”和“null”。
减法操作符(-)
一样需要遵循如下特殊规则:
小于(<)、大于(>)、小于等于(<=)和大于等于(>=)这几个关系操作符用于对两个值进行比较,这几个操作符都返回一个布尔值。
当关系操作符的操作数使用了非数值时,要进行数据转换或完成某些操作。以下就是其规则:
ES 提供两组操作符:相等和不相等——先转换再比较,全等和不全等——仅比较不转换。
相等操作符由两个等于号(==)表示,如果两个操作数相等,返回 true。
不相等操作符由叹号后跟等于号(!=)表示,如果两个操作数不相等,返回 false。
这两个操作符都会先转换操作数(通常称为强制转型),然后比较它们的相等性。
在转换不同数据类型时,遵循下列规则:
这两个操作符在比较时遵循下列规则:
全等操作符由 3 个等于号(===)表示,它只在两个操作数未经转换就相等的情况下返回 true。
不全等操作符由一个叹号后跟两个等于号(!==)表示,它在两个操作数未经转换就不相等的情况下返回 true。
注意:null == undefined 返回 true,因为它们是类似的值;但 null === undefined 会返回 false。
条件操作符遵循与 Java 中的条件操作符相同的语法形式。
简单的赋值操作符由等于号(=)表示,其作用就是把右侧的值赋给左侧的变量。
如果在等于号(=)前面再添加乘性操作符、加性操作符或位操作符,就可以完成复合赋值操作。
每个主要算术操作符都有对应的复合赋值操作符,这些操作符如下:
使用逗号操作符可以在一条语句中执行多个操作。
逗号操作符多用于声明多个变量,还可以用于赋值。用于赋值时,逗号操作符总会返回表达式中的最后一项。
语句通常使用一或多个关键字来完成给定任务。
语法如下:
if(condition){
statement1
}else{
statement2
}
其中的 condition(条件)可以是任意表达式。如果对 condition 求值的结果为 true,则执行 statement1(语句1),否则执行 statement2(语句2)。
do-while 语句是一种后测试循环语句,即只有在循环体中的代码执行之后,才会测试出口条件。换句话说,在对条件表达式求值之前,循环体内的代码至少会被执行一次。语法如下:
do{
statement
}while(expression);
while 语句属于前测试循环语句,也就是说,在循环体内的代码被执行之前,就会对出口条件求值。因此,循环体内的代码有可能永远不会被执行。语法如下:
while(expression){
statement
}
for 语句也是一种前测试循环语句,但它具有在执行循环之前初始化变量和定义循环后要执行的代码能力。语法如下:
for(initialization;expression;post-loop-expression){
statement
}
在 for 循环的变量初始化表达式中,也可以不使用 var 关键字,该变量的初始化可以在外部执行。
for 语句中的初始化表达式、控制表达式和循环后表达式都是可选的,如果将三个表达式全部省略,就会创建一个无限循环。
for-in 语句是一种精准的迭代语句,可以用来枚举对象的属性。语法如下:
for(property in expression){
statement
}
如果表示要迭代的对象的变量值为 null 或 undefined,for-in 语句会抛出错误。ES5 更正了这一行为,对这种情况不再抛出错误,而只是不执行循环体。为了保证最大限度的兼容性,建议在使用 for-in 循环之前,先检测该对象的值不是 null 或 undefined。
使用 label 语句可以在代码中添加标签,以便将来使用。语法如下:
label: statement
示例:
start:for(var i = 0; i < count; i++){
alert(i);
}
break 和 continue 语句用于在循环中精确地控制代码的执行。
break 语句会立即退出循环,强制继续执行循环后面的语句。continue 语句虽然也是立即退出循环,但退出循环后会从循环的顶部继续执行。
with 语句的作用是将代码的作用域设置到一个特定的对象中。语法如下:
with(expression) statement;
定义 with 语句的目的主要是为了简化多次编写同一个对象的工作,实例:
var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;
以上代码可以使用 with 语句改写成如下:
with(location){
var qs = search.substring(1);
var hostName = hostname;
var url = href;
}
注意:在严格模式下不允许使用 with 语句,否则将视为语法错误。
由于大量使用 with 语句会导致性能下降,同时也会给调试代码造成困难,因此在开发大型应用程序时,不建议使用 with 语句。
switch 语句与 if 语句的关系最为密切。语法如下:
switch(expression){
case value:statement
break;
case value:statement
break;
case value:statement
break;
default:statement
}
含义为:如果表达式等于这个值(value),则执行后面的语句。而 break 关键字会导致代码执行流跳出 switch 语句。如果省略 break,就会导致执行完当前 case 后,继续执行下一个 case。最后的 default 关键字则用于在表达式不匹配前面任何一种情形时,执行机动代码(也相当于 else 语句)。
switch 语句在比较值时使用的是全等操作符,因此不会发生类型转换。
通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。
ES 中的函数使用 function 关键字来声明,后跟一组参数以及函数体。语法如下:
function functionName(arg0,arg1,...,argN){
statements
}
ES 中的函数在定义时不必指定是否返回值。实际上,任何函数在任何时候都可以通过 return 语句后跟要返回的值来实现返回值。示例如下:
function sum(num1,num2){
return num1 + num2;
}
这个 sum() 函数的作用是把两个值加起来返回一个结果。
调用这个函数的代码如下:
var result = sum(5,10);
这个函数会在执行完 return 语句之后停止并立即退出,因此位于 return 之后的任何代码将不会被执行。
另外,return 语句也可以不带有任何返回值。函数在停止执行后将返回 undefined 值,这种用法一般用在需要提前停止函数执行而又不需要返回值的情况下使用。
严格模式对函数有一些限制,如下:
在函数体内可以通过 arguments 对象来访问这个参数数组,从而获取传递给函数的每一个参数。
ES 函数一个重要特定:命名的参数只提供便利,但不是必需的。
没有传递值的命名参数将自动被赋予 undefined 值。
ES 中的所有参数传递的都是值,不可能通过引用传递参数。
ES 函数不能像传统意义上那样实现重载。ES 函数没有签名,因为其参数是由包含零或多个值的数组来表示的。而没有函数签名,真正的重载是不可能做到的。
如果在 ES 中定义了两个名字相同的函数,则该名字只属于后定义的函数。
下面简要总结 ES 中的基本要素:
ES变量可能包含两种不同数据类型的值:基本类型值和引用类型值
。
基本类型值指的是简单的数据段,而引用类型值指那些可能由多个值构成的对象。
引用类型的值是保存在内存中的对象。JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操 作对象的引用而不是实际的对象。因此,引用类型的值是按引用访问的。
定义基本类型值和引用类型值的方式是类似的:创建一个变量并为该变量赋值。
我们不能给基本类型的值添加属性,只能给引用类型值动态地添加属性。
除了保存的方式不同之外,在从一个变量向另一个变量复制基本类型值和引用类型值时,也存在不同。如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上,例如:
var num1 = 5;
var num2 = num1;
以上代码中,num1中保存的值为5.当使用num1的值来初始化num2时,num2中也保存了值5.但num2中的5与num1的5是完全独立的,该值只是num1中5的一个副本。此后,这两个变量可以参与任何操作而不会相互影响。
当从一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份到为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会音响另一个变量,例如:
var obj1 = new Object();
var obj2 = obj1;
obj1.name = "zww"
alert(obj2.name); //"zww"
以上代码中,变量obj1保存了一个对象的新实例。然后这个值被复制到了obj2中。obj1和obj2都指向同一个对象。这样,当为obj1添加name属性后,可以通过obj2来访问这个属性。
ES中所有函数的参数都是按值传递的。也就是说,把函数外部的值复制给函数内部的参数,就和把值从一个变量复制到另一个变量一样。例子:
function addTen(num){
num += 10;
return num;
}
var count = 20;
var result = addTen(count);
alert(count); //20,没有变化
alert(result); //30
以上代码中,函数addTen()有一个参数num,而参数实际上是函数的局部变量。在调用函数时,变量count作用参数被传递给函数,这个变量值为20。于是数值20被复制给参数num以便在addTen()中使用。在函数内部,参数num值被加上了10,这一变化不会影响函数外部的count变量。
要检测一个变量是不是基本数据类型,typeof操作符是最佳的工具。
typeof操作符是确定一个变量是字符串、数值、布尔值,还是undefined的最佳工具,如果变量值是null或对象,则会返回"object"。
var s = "zww";
var b = true;
var i = 19;
var u;
var n = null;
var o = new Object();
alert(typeof s); //string
alert(typeof b); //boolean
alert(typeof i); //number
alert(typeof u); //undefined
alert(typeof n); //object
alert(typeof o); //object
因此,ES提供了instanceof操作符,语法如下:
result = variable instanceof constructor
如果变量是给定引用类型的实例,那么instanceof操作符就会返回true。
执行环境
定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象
,环境中定义的所有变量和函数都保存在这个对象中。
每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。
虽然执行环境的类型总共只有两种:全局和局部(函数),但还是有其他办法延长作用域链的。因为有些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。在两种情况下会发生这种情况。具体的说就是当执行流进入下列任何一个语句时,作用域链就会得到加长:
这两个语句都会在作用域链的前端添加一个变量对象。对with语句来说,会将指定的对象添加到作用域链中。对catch来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
使用var声明的变量会自动被添加到最接近的环境中。在函数内部,最接近的环境就是函数的局部环境;在with语句中,最接近的环境是函数环境。如果初始化变量时没有使用var声明,该变量会自动被添加到全局环境。例如:
function add(num1,num2){
var sum = num1 + num2;
return sum;
}
var result = add(10,20); //30
alert(sum); //由于sum不是有效的变量,因此会导致错误
以上代码中,函数add()定义了一个名为sum的局部变量,该变量包含加法的操作结果。虽然结果值从函数中返回了,但变量sum在函数外部是访问不到的。如果省略例子中var关键字,那么add()执行完毕后,sum也将可以访问到:
function add(num1,num2){
sum = num1 + num2;
return sum;
}
var result = add(10,20); //30
alert(sum); //30
当在某个环境中为了读取或写入而引用一个标识符时,必须通过搜索来确定该标识符实际代表什么。搜索过程从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符。如果在局部环境中找到了该标识符,就停止搜索,变量就绪。如果在局部环境中没有找到,则继续沿作用域链向上搜索。搜索过程将一直追溯到全局环境的变量对象。如果在全局环境中没有找到标识符,则意味着该变量尚未声明。
JavaScript具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。
JavaScript中最常用的垃圾收集方式是标记清除
。当变量进入环境时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则讲其标记为“离开环境”。
另一种不太常见的垃圾收集策略叫引用计数
。含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得另外一个值,则这个值的引用次数减1。当这个值的引用次数变为0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。
垃圾收集器是周期性运行的。
IE的垃圾收集器是根据内存分配量运行的,具体一点说就是256个变量、4096个对象(或数组)字面量和数组元素(slot)或者64KB的字符串,达到上述任何一个临界值,垃圾收集器就会运行。
在IE中,调用window.CollectGarbage()方法会立即执行垃圾收集。
JavaScript在进行内存管理及垃圾收集时,最主要的一个问题是,分配给web浏览器的可用内存数量通常要比分配给桌面应用程序的少。这样做的目的主要是出于安全方面考虑,目的是防止运行JavaScript的网页耗尽全部系统内存而导致系统崩溃。内存限制问题不仅会影响给变量分配内存,同时还会影响调用栈以及在一个线程中能够同时执行的语句数量。
因此,确保占用最少的内存可以让页面获得更好的性能。而优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据。一旦数据不再有用,最好通过将其值设置为null来释放其引用,这个做法叫做解除引用
。这一做法适用于大多数全局变量和全局对象的属性。局部变量会在它们离开执行环境时自动被解除引用。
JavaScript变量可以用来保存两种类型的值:基本类型值和引用类型值。
基本类型值和引用类型值具有以下特点:
所有变量都存在于一个执行环境(也称作用域)当中,这个执行环境决定了变量的生命周期,以及哪一部分代码可以访问其中的变量。
以下是关于执行环境的几点总结:
JavaScript是一门具有自动垃圾收集机制的编程语言,开发人员不必关心内存分配和回收问题,可以对JavaScript的垃圾收集例程做如下总结:
创建Object实例的两种方式:
var person = new Object();
person.name = "ZWW";
person.age = 22;
var person = {
name: "ZWW",
age: 22
};
在对象字面量中,使用逗号来分隔不同的属性,并且在最后一个属性后面不能添加逗号。
在使用对象字面量时,属性名也可以使用字符串;如果留空其花括号,则可以定义只包含默认属性和方法的对象。
联系客服