本文主要是介绍ONE PIECE ------ javascript,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
- 带有src 属性的<script>元素不应该在其<script>和</script>标签之间再包含额外的JavaScript 代码。如果包含了嵌入的代码,则只会下载并执行外部脚本文件,嵌入的代码会被忽略!(通过<script>元素的src 属性还可以包含来自外部域的JavaScript 文件;在文档的<head>元素中包含所有JavaScript 文件,意味着必须等到全部JavaScript 代码都被下载、解析和执行完成以后,才能开始呈现页面的内容(浏览器在遇到<body>标签时才开始呈现内容)为了避免这个问题一般把JavaScript引用放在<body>内容的后面;浏览器不支持脚本或者浏览器支持脚本,但脚本被禁用的情况下,浏览器都会显示<noscript></noscript>中的内容。而在除此之外的其他情况下,浏览器不会呈现<noscript></noscript>中的内容。在使用<script>嵌入JavaScript 代码时,记住不要在代码中的任何地方出现"</script>"字符串。当浏览器遇到字符串"</script>"时,就会认为那是结束的</script>标签。而通过转义字符“\”可以解决这个问题。)
- 区分大小写!
- 标识符:就是指变量、函数、属性的名字,或者函数的参数。标识符可以是按照下列格式规则组合起来的一或多个字符:
3.1 第一个字符必须是一个字母、下划线(_)或一个美元符号($);
3.2 其他字符可以是字母、下划线、美元符号或数字。
标识符中的字母也可以包含扩展的ASCII 或Unicode 字母字符(如À和Æ),但不推荐这样做。
按照惯例,ECMAScript 标识符采用驼峰大小写格式,也就是第一个字母小写,剩下的每个单词的首字母大写。 - JavaScript中使用关键字作标识符,会导致“Identifier Expected”错误;而使用保留字作标识符可能会也可能不会导致相同的错误,具体取决于特定的引擎!最好都不要使用关键字和保留字作为标识符和属性名,以便与将来的ECMAScript 版本兼容。
eval 和arguments在严格模式下,也不能作为标识符或属性名,否则会抛出错误。 - ECMAScript 使用C 风格的注释,包括单行注释和块级注释。单行注释以两个斜杠开头,如下所示:
// 单行注释
块级注释以一个斜杠和一个星号(/*)开头,以一个星号和一个斜杠(*/)结尾,如下所示:
/*
* 这是一个多行
* (块级)注释
*/ - ECMAScript 中的语句以一个分号结尾;如果省略分号,则由解析器确定语句的结尾,虽然语句结尾的分号不是必需的,但建议任何时候都不要省略它。因为加上这个分号可以避免很多错误(例如不完整的输入),开发人员也可以放心地通过删除多余的空格来压缩ECMAScript 代码(代码行结尾处没有分号会导致压缩错误)。另外,加上分号也会在某些情况下增进代码的性能,因为这样解析器就不必再花时间推测应该在哪里插入分号了。
- 变量是松散类型的,所谓松散类型就是可以用来保存任何类型的数据。换句话说,每个变量仅仅是一个用于保存值的占位符而已。定义变量时要使用var 操作符!用var 操作符定义的变量将成为定义该变量的作用域中的局部变量。也就是说,如果在函数中使用var 定义一个变量,那么这个变量在函数退出后就会被销毁。
- 可以使用一条语句定义多个变量,只要像下面这样把每个变量(初始化或不初始化均可)用逗号分隔开即可:
var message = "hi",
found = false,
age = 29;
变量 一开始保存了一个字符串值,然后该值又被一个数字值。虽然不建议修改变量所保存值的类型,但这种操作在ECMAScript 中完全有效。 - 省略var 操作符,会创建一个全局变量!(非严格模式下)
虽然省略var 操作符可以定义全局变量,但不推荐这种做法。因为在局部作用域中定义的全局变量很难维护,而且如果有意地忽略了var 操作符,也会由于相应变量不会马上就有定义而导致不必要的混乱。给未经声明的变量赋值在严格模式下会导致抛出ReferenceError 错误。JavaScript 从来不会告诉你是否多次声明了同一个变量;遇到这种情况,它只会对后续的声明视而不见(不过,它会执行后续声明中的变量初始化)。 - 未经过初始化的变量,会保存一个特殊的值——undefined!
- "use strict";//开启严格模式
严格模式是为JavaScript 定义了一种不同的解析与执行模型。在严格模式下,ECMAScript 3 中的一些不确定的行为将得到处理,而且对某些不安全
的操作也会抛出错误。要在整个脚本中启用严格模式,可以在顶部添加代码:"use strict"。
在函数内部的上方包含这条编译指示,也可以指定函数在严格模式下执行:
function doSomething(){
"use strict";
//函数体
} - 给未经声明的变量赋值在严格模式下会导致抛出ReferenceError 错误。(严格模式下)
- 在严格模式下,不能定义名为eval 或arguments 的变量,否则会导致语法错误。
- 基本数据类型:Undefined、Null、Boolean、Number和String!
复杂数据类型——Object! - 检测给定变量的数据类型--typeof,对一个值使用typeof 操作符可能返回下列某个字符串:
"undefined"——如果这个值未定义;
"boolean"——如果这个值是布尔值;
"string"——如果这个值是字符串;
"number"——如果这个值是数值;
"object"——如果这个值是对象或null;
"function"——如果这个值是函数;
var message = "some string";
alert(typeof message); // "string"
alert(typeof(message)); // "string"
typeof 是一个操作符而不是函数,因此圆括号尽管可以使用,但不是必需的!(typeof null会返回"object") - Undefined 类型只有一个值,即特殊的undefined。在使用var 声明变量但未对其加以初始化时,这个变量的值就是undefined!
没有必要使用undefined 值显式初始化了变量因为未经初始化的值默认就会取得undefined 值。一般而言,不存在需要显式地把一个变量设置为undefined 值的情况。字面值undefined 的主要目的是用于比较,而ECMA-262 第3 版之前的版本中并没有规定这个值。第3 版引入这个值是为了正式区分空对象指针与未经初始化的变量。 - 对于尚未声明过的变量,只能执行一项操作,使用typeof 操作符检测其数据类型(对未经声明的变量调用delete 不会导致错误,但这样做没什么实际意义,而且在严格模式下确实会导致错误)。然而,对未初始化的变量执行typeof 操作符会返回undefined 值,而对未声明的变量执行typeof 操作符同样也会返回undefined,显式地初始化变量是明智的选择。如果能够做到这一点,那么当typeof 操作符返回"undefined"值时,我们就知道被检测的变量还没有被声明,而不是尚未初始化。
- Null 类型是第二个只有一个值的数据类型,这个特殊的值是null。从逻辑角度来看,null 值表示一个空对象指针,而这也正是使用typeof 操作符检测null 值时会返回"object"的原因!
- 如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为null 而不是其他值!(只要意在保存对象的变量还没有真正保存对象,就应该明确地让该变量保存null 值。这样做不仅可以体现null 作为空对象指针的惯例,而且也有助于进一步区分null 和undefined)
- undefined 值是派生自null 值的
alert(null == undefined); //true - Boolean 类型,该类型只有两个字面值:true 和false(需要注意的是,Boolean 类型的字面值true 和false 是区分大小写的。也就是说,True 和False
(以及其他的混合大小写形式)都不是Boolean 值,只是标识符。) - 转型函数Boolean(),可以对任何数据类型的值调用Boolean()函数,而且总会返回一个Boolean 值。至于返回的这个值是true 还是false,取决于要转换值的数据类型及其实际值。下表给出了各种数据类型及其对应的转换规则:(n/a(或N/A),是not applicable 的缩写,意思是“不适用”)
数据类型 转换为true的值 转换为false的值
Boolean true false
String 任何非空字符串 ""(空字符串)
Number 任何非零数字值(包括无穷大) 0和NaN
Object 任何对象 null
Undefined n/a undefined
这些转换规则对理解流控制语句(如if 语句)自动执行相应的Boolean 转换非常重要 - Number 类型,来表示整数和浮点数值(浮点数值在某些语言中也被称为双精度数值)
最基本的数值字面量格式是十进制整数,十进制整数可以像下面这样直接在代码中输入:
var intNum = 55; // 整数
除了以十进制表示外,整数还可以通过八进制(以8 为基数)或十六进制(以16 为基数)的字面值来表示。其中,八进制字面值的第一位必须是零(0),然后是八进制数字序列(0~7)。如果字面值中的数值超出了范围,那么前导零将被忽略,后面的数值将被当作十进制数值解析。请看下面的例子:
var octalNum1 = 070; // 八进制的56
var octalNum2 = 079; // 无效的八进制数值——解析为79
var octalNum3 = 08; // 无效的八进制数值——解析为8
八进制字面量在严格模式下是无效的,会导致支持的JavaScript 引擎抛出错误。
十六进制字面值的前两位必须是0x,后跟任何十六进制数字(0~9 及A~F)。其中,字母A~F
可以大写,也可以小写。如下面的例子所示:
var hexNum1 = 0xA; // 十六进制的10
var hexNum2 = 0x1f; // 十六进制的31
在进行算术计算时,所有以八进制和十六进制表示的数值最终都将被转换成十进制数值。
JavaScript 中保存数值的方式,可以保存正零(+0)和负零(-0)。正零和负零被认为相等。
浮点数值:
所谓浮点数值,就是该数值中必须包含一个小数点,并且小数点后面必须至少有一位数字。虽然小数点前面可以没有整数,但我们不推荐这种写法。以下是浮点数值的几个例子:
var floatNum1 = 1.1;
var floatNum2 = 0.1;
var floatNum3 = .1; // 有效,但不推荐
由于保存浮点数值需要的内存空间是保存整数值的两倍,因此ECMAScript 会不失时机地将浮点数值转换为整数值。显然,如果小数点后面没有跟任何数字,那么这个数值就可以作为整数值来保存。同样地,如果浮点数值本身表示的就是一个整数(如1.0),那么该值也会被转换为整数,如下面的例子所示:
var floatNum1 = 1.; // 小数点后面没有数字——解析为1
var floatNum2 = 10.0; // 整数——解析为10
默认情况下,会将那些小数点后面带有6 个零以上的浮点数值转换为以e 表示法表示的数值(例如,0.0000003 会被转换成3e-7)。
浮点数值的最高精度是17 位小数,但在进行算术计算时其精确度远远不如整数。例如,0.1 加0.2的结果不是0.3,而是0.30000000000000004。这个小小的舍入误差会导致无法测试特定的浮点数值
例如:
if (a + b == 0.3){ // 不要做这样的测试!
alert("You got 0.3.");
}
在这个例子中,我们测试的是两个数的和是不是等于0.3。如果这两个数是0.05 和0.25,或者是0.15和0.15 都不会有问题。而如前所述,如果这两个数是0.1 和0.2,那么测试将无法通过。因此,永远不要测试某个特定的浮点数值。
能够表示的最小数值保存在Number.MIN_VALUE 中——在大多数浏览器中,这个值是5e-324;能够表示的最大数值保存在Number.MAX_VALUE 中——在大多数浏览器中,这个值是1.7976931348623157e+308。如果某次计算的结果得到了一个超出JavaScript 数值范围的值,那么这个数值将被自动转换成特殊的Infinity 值。具体来说,如果这个数值是负数,则会被转换成-Infinity(负无穷),如果这个数值是正数,则会被转换成Infinity(正无穷)。
如上所述,如果某次计算返回了正或负的Infinity 值,那么该值将无法继续参与下一次的计算,因为Infinity 不是能够参与计算的数值。要想确定一个数值是不是有穷的(换句话说,是不是位于最小和最大的数值之间),可以使用isFinite()函数。这个函数在参数位于最小与最大数值之间时会返回true,如下面的例子所示:
var result = Number.MAX_VALUE + Number.MAX_VALUE;
alert(isFinite(result)); //false
尽管在计算中很少出现某些值超出表示范围的情况,但在执行极小或极大数值的计算时,检测监控这些值是可能的,也是必需的。
NaN(原书如此,但实际上只有0 除以0 才会返回NaN,正数除以0 返回Infinity,负数除以0 返回-Infinity)即非数值(Not a Number)是一个特殊的数值,这个数值用于表示一个本来要返回数值的操作数未返回数值的情况(这样就不会抛出错误了)。例如,在其他编程语言中,任何数值除以0 都会导致错误,从而停止代码执行。但在ECMAScript 中,任何数值除以0 会返回NaN,因此不会影响其他代码的执行。NaN 本身有两个非同寻常的特点,首先,任何涉及NaN 的操作(例如NaN/10)都会返回NaN,这个特点在多步计算中有可能导致问题。其次,NaN 与任何值都不相等,包括NaN 本身。例如,下面的代码会返回false:
alert(NaN == NaN); //false
针对NaN 的这两个特点,ECMAScript 定义了isNaN()函数。这个函数接受一个参数,该参数可以是任何类型,而函数会帮我们确定这个参数是否“不是数值”。isNaN()在收到一个值之后,会尝试将这个值转换为数值。某些不是数值的值会直接转换为数值,例如字符串"10"或Boolean 值。而任何不能被转换为数值的值都会导致这个函数返回true。请看下面的例子:
alert(isNaN(NaN)); //true
alert(isNaN(10)); //false(10 是一个数值)
alert(isNaN("10")); //false(可以被转换成数值10)
alert(isNaN("blue")); //true(不能转换成数值)
alert(isNaN(true)); //false(可以被转换成数值1)
有3 个函数可以把非数值转换为数值:Number()、parseInt()和parseFloat()。第一个函数,即转型函数Number()可以用于任何数据类型,而另两个函数则专门用于把字符串转换成数值。这3 个函数对于同样的输入会有返回不同的结果
Number()函数的转换规则如下:
如果是Boolean 值,true 和false 将分别被转换为1 和0。
如果是数字值,只是简单的传入和返回。
如果是null 值,返回0。
如果是undefined,返回NaN。
如果是字符串,遵循下列规则:
如果字符串中只包含数字(包括前面带正号或负号的情况),则将其转换为十进制数值,即"1"会变成1,"123"会变成123,而"011"会变成11(注意:前导的零被忽略了);
如果字符串中包含有效的浮点格式,如"1.1",则将其转换为对应的浮点数值(同样,也会忽略前导零);
如果字符串中包含有效的十六进制格式,例如"0xf",则将其转换为相同大小的十进制整数值;
如果字符串是空的(不包含任何字符),则将其转换为0;
如果字符串中包含除上述格式之外的字符,则将其转换为NaN。
如果是对象,则调用对象的valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,然后再次依照前面的规则转换返回的字符串值
由于Number()函数在转换字符串时比较复杂而且不够合理,因此在处理整数的时候更常用的是parseInt()函数。parseInt()函数在转换字符串时,更多的是看其是否符合数值模式。它会忽略字符串前面的空格,直至找到第一个非空格字符。如果第一个字符不是数字字符或者负号,parseInt()就会返回NaN;也就是说,用parseInt()转换空字符串会返回NaN(Number()对空字符返回0)。如果第一个字符是数字字符,parseInt()会继续解析第二个字符,直到解析完所有后续字符或者遇到了一个非数字字符。例如,"1234blue"会被转换为1234,因为"blue"会被完全忽略。类似地,"22.5"会被转换为22,因为小数点并不是有效的数字字符。如果字符串中的第一个字符是数字字符,parseInt()也能够识别出各种整数格式(即前面讨论的十进制、八进制和十六进制数)。也就是说,如果字符串以"0x"开头且后跟数字字符,就会将其当作一个十六进制整数;如果字符串以"0"开头且后跟数字字符,则会将其当作一个八进制数来解析在ECMAScript 3 JavaScript 引擎中,"070"被当成八进制字面量,因此转换后的值是十进制的56。而在ECMAScript 5 JavaScript 引擎中,parseInt()已经不具有解析八进制值的能力,因此前导的零会被认为无效,从而将这个值当成"70",结果就得到十进制的70。在ECMAScript 5 中,即使是在非严格模式下也会如此。为了消除在使parseInt()函数时可能导致的上述困惑,可以为这个函数提供第二个参数:转换时使用的基数(即多少进制)。如果知道要解析的值是十六进制格式的字符串,那么指定基数16 作为第二个参数,可以保证得到正确的结果不指定基数意味着让parseInt()决定如何解析输入的字符串,因此为了避免错误的解析,建议无论在什么情况下都明确指定基数(多数情况下,我们要解析的都是十进制数值,因此始终将10 作为第二个参数是非常必要的。),与parseInt()函数类似,parseFloat()也是从第一个字符(位置0)开始解析每个字符。而且也是一直解析到字符串末尾,或者解析到遇见一个无效的浮点数字字符为止。也就是说,字符串中的第一个小数点是有效的,而第二个小数点就是无效的了,因此它后面的字符串将被忽略。举例来说,"22.34.5"将会被转换为22.34。除了第一个小数点有效之外,parseFloat()与parseInt()的第二个区别在于它始终都会忽略前导的零。parseFloat()可以识别前面讨论过的所有浮点数值格式,也包括十进制整数格式。但十六进制格式的字符串则始终会被转换成0。由于parseFloat()只解析十进制值,因此它没有用第二个参数指定基数的用法。最后还要注意一点:如果字符串包含的是一个可解析为整数的数(没有小数点,或者小数点后都是零),parseFloat()会返回整数。 - String 类型
用双引号表示的字符串和用单引号表示的字符串完全相同。不过,以双引号开头的字符串也必须以双引号结尾,而以单引号开头的字符串必须以单引号结尾
String 数据类型包含一些特殊的字符字面量,也叫转义序列,用于表示非打印字符,或者具有其他用途的字符。这些字符字面量如下表所示
字 面 量 含 义
\n 换行
\t 制表
\b 空格
\r 回车
\f 进纸
\\ 斜杠
\' 单引号('),在用单引号表示的字符串中使用。例如:'He said, \'hey.\''
\" 双引号("),在用双引号表示的字符串中使用。例如:"He said, \"hey.\""
\xnn 以十六进制代码nn表示的一个字符(其中n为0~F)。例如,\x41表示"A"
\unnnn 以十六进制代码nnnn表示的一个Unicode字符(其中n为0~F)。例如,\u03a3表示希腊字符Σ
任何字符串的长度都可以通过访问其length 属性取得
字符串是不可变的,也就是说,字符串一旦创建,它们的值就不能改变。要改变某个变量保存的字符串,首先要销毁原来的字符串,然后再用另一个包含新值的字符串填充该变量
要把一个值转换为一个字符串有两种方式。第一种是使用几乎每个值都有的toString()方法
数值、布尔值、对象和字符串值(没错,每个字符串也都有一个toString()方法,该方法返回字符串的一个副本)都有toString()方法。但null 和undefined 值没有这个方法
多数情况下,调用toString()方法不必传递参数。但是,在调用数值的toString()方法时,可以传递一个参数:输出数值的基数。默认情况下,toString()方法以十进制格式返回数值的字符串表示。而通过传递基数,toString()可以输出以二进制、八进制、十六进制,乃至其他任意有效进制格式表示的字符串值(,默认的(没有参数的)输出值与指定基数10 时的输出值相同。)
在不知道要转换的值是不是null 或undefined 的情况下,还可以使用转型函数String(),这个函数能够将任何类型的值转换为字符串。String()函数遵循下列转换规则:
如果值有toString()方法,则调用该方法(没有参数)并返回相应的结果;
如果值是null,则返回"null";
如果值是undefined,则返回"undefined"。
两个用于访问字符串中特定字符的方法是:charAt()和charCodeAt()。这两个方法都接收一个参数,即基于0 的字符位置。其中,charAt()方法以单字符字符串的形式返回给定位置的那个字符(ECMAScript 中没有字符类型),如果你想得到
的不是字符而是字符编码,那么就要使用charCodeAt()了。
concat(),用于将一或多个字符串拼接起来,返回拼接得到的新字符串。虽然concat()是专门用来拼接字符串的方法,但实践中使用更多的还是加号操作符(+)。而且,使用加号操作符在大多数情况下都比使用concat()方法要简便易行(特别是在拼接多个字符串的情况下)。
ECMAScript 还提供了三个基于子字符串创建新字符串的方法:slice()、substr()和substring()。
这三个方法都会返回被操作字符串的一个子字符串,而且也都接受一或两个参数。第一个参数指定子字符串的开始位置,第二个参数(在指定的情况下)表示子字符串到哪里结束。具体来说,slice()和substring()的第二个参数指定的是子字符串最后一个字符后面的位置。而substr()的第二个参数指定的则是返回的字符个数。如果没有给这些方法传递第二个参数,则将字符串的长度作为结束位置。与concat()方法一样,slice()、substr()和substring()也不会修改字符串本身的值——它们只是返回一个基本类型的字符串值,对原始字符串没有任何影响。在传递给这些方法的参数是负值的情况下,它们的行为就不尽相同了。其中,slice()方法会将传入的负值与字符串的长度相加,substr()方法将负的第一个参数加上字符串的长度,而将负的第二个参数转换为0。最后,substring()方法会把所有负值参数都转换为0。当第二个参数是负值时,这三个方法的行为各不相同。slice()方法会把负参数加上字符串的长度。substring()方法会把第二个参数转换为0,而由于这个方法会将较小的数作为开始位置,将较大的数作为结束位置,因此最终相当于调用了substring(大,小)。substr()也会将第二个参数转换为0,这也就意味着返回包含零个字符的字符串,也就是一个空字符串。
有两个可以从字符串中查找子字符串的方法:indexOf()和lastIndexOf()。这两个方法都是从一个字符串中搜索给定的子字符串,然后返子字符串的位置(如果没有找到该子字符串,则返回-1)。这两个方法的区别在于:indexOf()方法从字符串的开头向后搜索子字符串,而lastIndexOf()方法是从字符串的末尾向前搜索子字符串。
ECMAScript 5 为所有字符串定义了trim()方法。这个方法会创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果。由于trim()返回的是字符串的副本,所以原始字符串中的前置及后缀空格会保持不变。
ECMAScript 中涉及字符串大小写转换的方法有4 个:toLowerCase()、toLocaleLowerCase()、toUpperCase()和toLocaleUpperCase()。其中,toLowerCase()和toUpperCase()是两个经典的方法,而toLocaleLowerCase()和toLocaleUpperCase()方法则是针对特定地区的实现。对有些地区来说,针对地区的方法与其通用方法得到的结果相同,但少数语言(如土耳其语)会为Unicode 大小写转换应用特殊的规则,这时候就必须使用针对地区的方法来保证实现正确的转换。
String 类型定义了几个用于在字符串中匹配模式的方法。第一个方法就是match(),在字符串上调用这个方法,本质上与调用RegExp 的exec()方法相同。match()方法只接受一个参数,要么是一个正则表达式,要么是一个RegExp 对象。
另一个用于查找模式的方法是search()。这个方法的唯一参数与match()方法的参数相同:由字符串或RegExp 对象指定的一个正则表达式。search()方法返回字符串中第一个匹配项的索引;如果没有找到匹配项,则返回-1。而且,search()方法始终是从字符串开头向后查找模式。
ECMAScript 提供了replace()方法。这个方法接受两个参数:第一个参数可以是一个RegExp 对象或者一个字符串(这个字符串不会被转换成正则表达式),第二个参数可以是一个字符串或者一个函数。如果第一个参数是字符串,那么只会替换第一个子字符串。要想替换所有子字符串,唯一的办法就是提供一个正则表达式,而且要指定全局(g)标志。replace()方法的第二个参数也可以是一个函数。在只有一个匹配项(即与模式匹配的字符串)的情况下,会向这个函数传递3 个参数:模式的匹配项、模式匹配项在字符串中的位置和原始字符串。在正则表达式中定义了多个捕获组的情况下,传递给函数的参数依次是模式的匹配项、第一个捕获组的匹配项、第二个捕获组的匹配项……,但最后两个参数仍然分别是模式的匹配项在字符串中的位置和原始字符串。这个函数应该返回一个字符串,表示应该被替换的匹配项使用函数作为replace()方法的第二个参数可以实现更加精细的替换操作。
split(),这个方法可以基于指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。分隔符可以是字符串,也可以是一个RegExp 对象(这个方法不会将字符串看成正则表达式)。split()方法可以接受可选的第二个参数,用于指定数组的大小,以便确保返回的数组不会超过既定大小。
localeCompare(),这个方法比较两个字符串,并返回下列值中的一个:
如果字符串在字母表中应该排在字符串参数之前,则返回一个负数(大多数情况下是-1,具体的值要视实现而定);
如果字符串等于字符串参数,则返回0;
如果字符串在字母表中应该排在字符串参数之后,则返回一个正数(大多数情况下是1,具体的值同样要视实现而定)。
fromCharCode():这个方法的任务是接收一或多个字符编码,然后将它们转换成一个字符串。从本质上来看,这个方法与实例方法charCodeAt()执行的是相反的操作。 - Array 类型
ECMAScript 数组的每一项可以保存任何类型的数据,。也就是说,可以用数组的第一个位置来保存字符串,用第二位置来保存数值,用第三个位置来保存对象,数组的大小是可以动态调整的,即可以随着数据的添加自动增长以容纳新增数据。
创建数组:
var colors = new Array();
创建length 值为20 的数组: var colors = new Array(20);
向Array 构造函数传递数组中应该包含的项: var colors = new Array("red", "blue", "green");
使用数组字面量表示法: colors = ["red", "blue", "green"];
给构造函数传递一个值也可以创建数组。但这时候问题就复杂一点了,因为如果传递的是数值,则会按照该数值创建包含给定项数的数组;而如果传递的是其他类型的参数,则会创建包含那个值的只有一项的数组。
读取和设置数组的值时,要使用方括号并提供相应值的基于0 的数字索引
数组的length 属性很有特点——它不是只读的。因此,通过设置这个属性,可以从数组的末尾移除项或向数组中添加新项
ECMAScript 5 新增了Array.isArray()方法。这个方法的目的是最终确定某个值到底是不是数组
调用数组的toString()方法会返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串。而调用valueOf()返回的还是数组。使用join()方法,则可以使用不同的分隔符来构建这个字符串。join()方法只接收一个参数,即用作分隔符的字符串,然后返回包含所有数组项的字符串.
push()和pop()方法:
push()方法可以接收任意数量的参数,把它们逐个添加到数组末尾,并返回修改后数组的长度。
pop()方法则从数组末尾移除最后一项,减少数组的length 值,然后返回移除的项。
unshift()与shift()方法:
unshift()方法在数组前端添加任意个项并返回新数组的长度
shift()方法能够移除数组中的第一个项并返回该项,同时将数组长度减1。
排序的方法reverse()和sort():
默认情况下,sort()方法按升序排列数组项——即最小的值位于最前面,最大的值排在最后面。为了实现排序,sort()方法会调用每个数组项的toString()转型方法,然后比较得到的字符串,以确定如何排序。即使数组中的每一项都是数值,sort()方法比较的也是字符串.sort()方法可以接收一个比较函数作为参数,以便我们指定哪个值位于哪个值的前面。
比较函数接收两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等则返回0,如果第一个参数应该位于第二个之后则返回一个正数
concat()方法可以基于当前数组中的所有项创建一个新数组。具体来说,这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。在没有给concat()方法传递参数的情况下,它只是复制当前数组并返回副本。如果传递给concat()方法的是一或多个数组,则该方法会将这些数组中的每一项都添加到结果数组中。如果传递的值不是数组,这些值就会被简单地添加到结果数组的末尾。
slice()方法它能够基于当前数组中的一或多个项创建一个新数组。slice()方法可以接受一或两个参数,即要返回项的起始和结束位置。在只有一个参数的情况下,slice()方法返回从该参数指定位置开始到当前数组末尾的所有项。如果有两个参数,该方法返回起始和结束位置之间的项——但不包括结束位置的项。注意,slice()方法不会影响原始数组。slice()方法的参数中有一个负数,则用数组长度加上该数来确定相应的位置,如果结束位置小于起始位置,则返回空数组。
splice()方法
删除:可以删除任意数量的项,只需指定2 个参数:要删除的第一项的位置和要删除的项数。
插入:可以向指定位置插入任意数量的项,只需提供3 个参数:起始位置、0(要删除的项数)和要插入的项。如果要插入多个项,可以再传入第四、第五,以至任意多个项。
替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定3 个参数:起
始位置、要删除的项数和要插入的任意数量的项。插入的项数不必与删除的项数相等。
splice()方法始终都会返回一个数组,该数组中包含从原始数组中删除的项(如果没有删除任何项,则返回一个空数组)。
ECMAScript 5 为数组实例添加了两个位置方法:indexOf()和lastIndexOf()。这两个方法都接收两个参数:要查找的项和(可选的)表示查找起点位置的索引。其中,indexOf()方法从数组的开头(位置0)开始向后查找,lastIndexOf()方法则从数组的末尾开始向前查找。这两个方法都返回要查找的项在数组中的位置,或者在没找到的情况下返回-1。在比较第一个参数与数组中的每一项时,会使用全等操作符;也就是说,要求查找的项必须严格相等(就像使用===一样)。
ECMAScript 5 为数组定义了5 个迭代方法。每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响this 的值。传入这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身。根据使用的方法不同,这个函数执行后的返回值可能会也可能不会影响方法的返回值。以下是这5 个迭代方法的作用。
every():对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true。
filter():对数组中的每一项运行给定函数,返回该函数会返回true 的项组成的数组。
forEach():对数组中的每一项运行给定函数。这个方法没有返回值。
map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
some():对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。
以上方法都不会修改数组中的包含的值。
ECMAScript 5 还新增了两个归并数组的方法:reduce()和reduceRight()。这两个方法都会迭代数组的所有项,然后构建一个最终返回的值。其中,reduce()方法从数组的第一项开始,逐个遍历到最后。而reduceRight()则从数组的最后一项开始,向前遍历到第一项。这两个方法都接收两个参数:一个在每一项上调用的函数和(可选的)作为归并基础的初始值。传给reduce()和reduceRight()的函数接收4 个参数:前一个值、当前值、项的索引和数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项. - Date 类型
ECMAScript 中的Date类型使用自UTC(Coordinated Universal Time,国际协调时间)1970 年1 月1 日午夜(零时)开始经过的毫秒数来保存日期
要创建一个日期对象,使用new 操作符和Date 构造函数即可
var now = new Date();
在调用Date 构造函数而不传递参数的情况下,新创建的对象自动获得当前日期和时间。如果想根据特定的日期和时间创建日期对象,必须传入表示该日期的毫秒数(即从UTC 时间1970 年1 月1 日午夜起至该日期止经过的毫秒数)。为了简化这一计算过程,ECMAScript 提供了两个方法:Date.parse()和Date.UTC()。
其中,Date.parse()方法接收一个表示日期的字符串参数,然后尝试根据这个字符串返回相应日期的毫秒数。ECMA-262 没有定义Date.parse()应该支持哪种日期格式,因此这个方法的行为因实现而异,而且通常是因地区而异。如果传Date.parse()方法的字符串不能表示日期,那么它会返回NaN。实际上,如果直接将表示日期的字符串传递给Date 构造函数,也会在后台调用Date.parse()。
Date.UTC()方法同样也返回表示日期的毫秒数,但它与Date.parse()在构建值时使用不同的信息。Date.UTC()的参数分别是年份、基于0 的月份(一月是0,二月是1,以此类推)、月中的哪一天(1 到31)、小时数(0 到23)、分钟、秒以及毫秒数。在这些参数中,只有前两个参数(年和月)是必需的。如果没有提供月中的天数,则假设天数为1;如果省略其他参数,则统统假设为0.如同模仿Date.parse()一样,Date 构造函数也会模仿Date.UTC(),但有一点明显不同:日期
和时间都基于本地时区而非GMT 来创建。
ECMAScript 5 添加了Data.now()方法,返回表示调用这个方法时的日期和时间的毫秒数。这个方法简化了使用Data 对象分析代码的工作。
Date 类型的valueOf()方法,则根本不返回字符串,而是返回日期的毫秒表示。因此,可以方便使用比较操作符(小于或大于)来比较日期值。从逻辑上讲,2007 年1 月1 日要早于2007 年2 月1 日,此时如果我们说前者小于后者比较符合常理。而表示2007 年1 月1 日的毫秒值小于表示2007 年2 月1 日的毫秒值,因此在首先使用小于操作符比较日期时,返回的结果是true。这样,就为我们比较日期提供了极大方便.
getTime() :返回表示日期的毫秒数;与valueOf()方法返回的值相同
setTime(毫秒) :以毫秒数设置日期,会改变整个日期
getFullYear() :取得4位数的年份(如2007而非仅07)
getUTCFullYear(): 返回UTC日期的4位数年份
setFullYear(年) :设置日期的年份。传入的年份值必须是4位数字(如2007而非仅07)
setUTCFullYear(年) :设置UTC日期的年份。传入的年份值必须是4位数字(如2007而非仅07)
getMonth(): 返回日期中的月份,其中0表示一月,11表示十二月
getUTCMonth(): 返回UTC日期中的月份,其中0表示一月,11表示十二月
setMonth(月): 设置日期的月份。传入的月份值必须大于0,超过11则增加年份
setUTCMonth(月): 设置UTC日期的月份。传入的月份值必须大于0,超过11则增加年份
getDate() :返回日期月份中的天数(1到31)
getUTCDate() :返回UTC日期月份中的天数(1到31)
setDate(日) :设置日期月份中的天数。如果传入的值超过了该月中应有的天数,则增加月份
setUTCDate(日) :设置UTC日期月份中的天数。如果传入的值超过了该月中应有的天数,则增加月份
getDay() :返回日期中星期的星期几(其中0表示星期日,6表示星期六)
getUTCDay() :返回UTC日期中星期的星期几(其中0表示星期日,6表示星期六)
getHours(): 返回日期中的小时数(0到23)
getUTCHours() :返回UTC日期中的小时数(0到23)
setHours(时) :设置日期中的小时数。传入的值超过了23则增加月份中的天数
setUTCHours(时) :设置UTC日期中的小时数。传入的值超过了23则增加月份中的天数
getMinutes() :返回日期中的分钟数(0到59)
getUTCMinutes() :返回UTC日期中的分钟数(0到59)
setMinutes(分) :设置日期中的分钟数。传入的值超过59则增加小时数
setUTCMinutes(分) :设置UTC日期中的分钟数。传入的值超过59则增加小时数
getSeconds() :返回日期中的秒数(0到59)
getUTCSeconds() :返回UTC日期中的秒数(0到59)
setSeconds(秒) :设置日期中的秒数。传入的值超过了59会增加分钟数
setUTCSeconds(秒) :设置UTC日期中的秒数。传入的值超过了59会增加分钟数
getMilliseconds() :返回日期中的毫秒数
getUTCMilliseconds() :返回UTC日期中的毫秒数
setMilliseconds(毫秒) :设置日期中的毫秒数
setUTCMilliseconds(毫秒): 设置UTC日期中的毫秒数
getTimezoneOffset() :返回本地时间与UTC时间相差的分钟数 - RegExp 类型
RegExp 构造函数,它接收两个参数:一个是要匹配的字符串模式,另一个是可选的标志字符串。
var pattern = new RegExp("[bc]at", "i");等价于var pattern1 = /[bc]at/i;//匹配第一个"bat"或"cat",不区分大小写
传递给RegExp 构造函数的两个参数都是字符串(不能把正则表达式字面量传递给RegExp 构造函数)。由于RegExp 构造函数的模式参数是字符串,所以在某些情况下要对字符进行双重转义。所有元字符都必须双重转义,那些已经转义过的字符也是如此,例如\n(字符\在字符串中通常被转义为\\,而在正则表达式字符串中就会变成\\\\)。
字面量形式来定义一个正则表达式:var expression = / pattern / flags ;
其中的模式(pattern)部分可以是任何简单或复杂的正则表达式,可以包含字符类、限定符、分组、向前查找以及反向引用。每个正则表达式都可带有一或多个标志(flags),用以标明正则表达式的行为。
正则表达式的匹配模式支持下列3 个标志。
g:表示全局(global)模式,即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止;
i:表示不区分大小写(case-insensitive)模式,即在确定匹配项时忽略模式与字符串的大小写;
m:表示多行(multiline)模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项。
因此,一个正则表达式就是一个模式与上述3 个标志的组合体。不同组合产生不同结果。
模式中使用的所有元字符都必须转义。正则表达式中的元字符包括:( [ { \ ^ $ | ) ? * + .]}
这些元字符在正则表达式中都有一或多种特殊用途,因此如果想要匹配字符串中包含的这些字符,就必须对它们进行转义。使用“\”进行转义。
var oPatternO = /.at/gi;//匹配所有以"at"结尾的3 个字符的组合,不区分大小写
var oPatternT = /\.at/gi;//匹配所有".at",不区分大小写
使用正则表达式字面量和使用RegExp 构造函数创建的正则表达式不一样。在ECMAScript 3 中,正则表达式字面量始终会共享同一个RegExp 实例,而使用构造函数创建的每一个新RegExp 实例都是一个新实例。ECMAScript 5 明确规定,使用正则表达式字面量必须像直接调用RegExp 构造函数一样,每次都创建新的RegExp 实例。
RegExp 的每个实例都具有下列属性,通过这些属性可以取得有关模式的各种信息:
global:布尔值,表示是否设置了g 标志。
ignoreCase:布尔值,表示是否设置了i 标志。
lastIndex:整数,表示开始搜索下一个匹配项的字符位置,从0 算起。
multiline:布尔值,表示是否设置了m 标志。
source:正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回。
RegExp 对象的主要方法是exec(),该方法是专门为捕获组而设计的。exec()接受一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回null。返回的数组虽然是Array 的实例,但包含两个额外的属性:index 和input。其中,index 表示匹配项在字符串中的位置,而input 表示应用正则表达式的字符串。在数组中,第一项是与整个模式匹配的字符串,其他项是与模式中的捕获组匹配的字符串(如果模式中没有捕获组,则该数组只包含一项)。对于exec()方法而言,即使在模式中设置了全局标志(g),它每次也只会返回一个匹配项。在不设置全局标志的情况下,在同一个字符串上多次调用exec()将始终返回第一个匹配项的信息。而在设置全局标志的情况下,每次调用exec()则都会在字符串中继续查找新匹配项。
正则表达式的第二个方法是test(),它接受一个字符串参数。在模式与该参数匹配的情况下返回true;否则,返回false。在只想知道目标字符串与某个模式是否匹配,但不需要知道其文本内容的情况下,使用这个方法非常方便。
RegExp 实例继承的toLocaleString()和toString()方法都会返回正则表达式的字面量,与创建正则表达式的方式无关。
正则表达式中的特殊字符 - Function 类型
函数实际上是对象。每个函数都是Function 类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。
函数通常是使用函数声明语法定义的,如下面的例子所示。
function sum (num1, num2) {
return num1 + num2;
}
这与下面使用函数表达式定义函数的方式几乎相差无几。
var sum = function(num1, num2){
return num1 + num2;
};
以上代码定义了变量sum 并将其初始化为一个函数。有读者可能会注意到,function 关键字后面没有函数名。这是因为在使用函数表达式定义函数时,没有必要使用函数名——通过变量sum 即可以引用函数。另外,还要注意函数末尾有一个分号,就像声明其他变量时一样。最后一种定义函数的方式是使用Function 构造函数。Function 构造函数可以接收任意数量的参数,但最后一个参数始终都被看成是函数体,而前面的参数则枚举出了新函数的参数。
var sum = new Function("num1", "num2", "return num1 + num2"); // 不推荐
由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变量没有什么不同。换句话说,一个函数可能会有多个名字。使用不带圆括号的函数名是访问函数指针,而非调用函数。
如果声明了两个同名函数,则是后面的函数覆盖前面的函数。解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问);至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。即使声明函数的代码在调用它的代码后面,JavaScript 引擎也能把函数声明提升到顶部。
在函数内部,有两个特殊的对象:arguments 和this。其中,arguments 是一个类数组对象,包含着传入函数中的所有参数。虽然arguments 的主要用途是保存函数参数,但这个对象还有一个名叫callee 的属性,该属性是一个指针,指向拥有这个arguments 对象的函数。函数内部的另一个特殊对象是this,this引用的是函数据以执行的环境对象——或者也可以说是this 值(当在网页的全局作用域中调用函数时,this 对象引用的就是window)。ECMAScript 5 也规范化了另一个函数对象的属性:caller。这个属性中保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为null。
当函数在严格模式下运行时,访问arguments.callee 会导致错误。ECMAScript 5 还定义了arguments.caller 属性,但在严格模式下访问它也会导致错误,而在非严格模式下这个属性始终是undefined。定义这个属性是为了分清arguments.caller 和函数的caller 属性。以上变化都是为了加强这门语言的安全性,这样第三方代码就不能在相同的环境里窥视其他代码了。
严格模式还有一个限制:不能为函数的caller 属性赋值,否则会导致错误。
函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length 和prototype。其中,length 属性表示函数希望接收的命名参数的个数。
每个函数都包含两个非继承而来的方法:apply()和call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this 对象的值。首先,apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array 的实例,也可以是arguments 对象。call()方法与apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数是this 值没有变化,变化的是其余参数都直接传递给函数。换句话说,在使用call()方法时,传递给函数的参数必须逐个列举出来。
ECMAScript 5 还定义了一个方法:bind()。这个方法会创建一个函数的实例,其this 值会被绑定到传给bind()函数的值。
函数可以通过其函数名来调用,后面还要加上一对圆括号和参数(圆括号中的参数如果有多个,可以用逗号隔开)
函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。ECMAScript 中的函数使用function 关键字来声明,后跟一组参数以及函数体。函数的基本语法如下所示:
function functionName(arg0, arg1,...,argN) {
statements
}
通过函数名来调用函数,后面还要加上一对圆括号和参数(参数如果有多个,可以用逗号隔开)。
函数在定义时不必指定是否返回值。任何函数在任何时候都可以通过return 语句后跟要返回的值来实现返回值(一个函数中可以包含多个return 语句)。
函数会在执行完return 语句之后停止并立即退出,位于return 语句之后的任何代码都永远不会执行。
return 语句也可以不带有任何返回值。在这种情况下,函数在停止执行后将返回undefined值。
严格模式对函数有一些限制:
①不能把函数命名为eval 或arguments;
②不能把参数命名为eval 或arguments;
③不能出现两个命名参数同名的情况。
如果发生以上情况,就会导致语法错误,代码无法执行。
ECMAScript 函数不介意传递进来多少个参数,也不在乎传进来参数是什么数据类型。也就是说,即便你定义的函数只接收两个参数,在调用这个函数时也未必一定要传递两个参数,可以传递一个、三个甚至不传递参数。原因是ECMAScript 中的参数在内部是用一个数组来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。如果这个数组中不包含任何元素,无所谓;如果包含多个元素,也没有问题。在函数体内可以通过arguments 对象来访问这个参数数组,从而获取传递给函数的每一个参数。arguments 对象只是与数组类似(它并不是Array 的实例),因为可以使用方括号语法访问它的每一个元素(即第一个元素是arguments[0],第二个元素是argumetns[1],以此类推),使用length 属性来确定传递进来多少个参数。(命名的参数只提供便利,但不是必需的。)
arguments 对象可以与命名参数一起使用,arguments 中的值永远与对应命名参数的值保持同步。这并不是说读取这两个值会访问相同的内存空间;它们的内存空间是独立的,但它们的值会同步。另外还要记住,如果只传入了一个参数,那么为arguments[1]设置的值不会反应到命名参数中。这是因为arguments 对象的长度是由传入的参数个数决定的,不是由定义函数时的命名参数的个数决定的。没有传递值的命名参数将自动被赋予undefined 值。跟定义了变量又没有初始化一样。
严格模式对如何使用arguments 对象做出了一些限制。重写arguments 的值会导致语法错误(代码将不会执行)。
ECMAScript 中的所有参数传递的都是值,不可能通过引用传递参数。
ECMAScript 中定义了两个名字相同的函数,则该名字只属于后定义的函数。
(function(){
//这里是块级作用域
})();
以上代码定义并立即调用了一个匿名函数。将函数声明包含在一对圆括号中,表示它实际上是一个函数表达式。而紧随其后的另一对圆括号会立即调用这个函数。
递归函数是在一个函数通过名字调用自身的情况下构成的:
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * factorial(num-1);
}
}
这是一个经典的递归阶乘函数。虽然这个函数表面看来没什么问题,但下面的代码却可能导致它出错。
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //出错!
以上代码先把factorial()函数保存在变量anotherFactorial 中,然后将factorial 变量设置为null,结果指向原始函数的引用只剩下一个。但在接下来调用anotherFactorial()时,由于必须执行factorial(),而factorial 已经不再是函数,所以就会导致错误。在这种情况下,使用arguments.callee 可以解决这个问题。
arguments.callee 是一个指向正在执行的函数的指针,因此可以用它来实现对函数
的递归调用,例如:
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
}
但在严格模式下,不能通过脚本访问arguments.callee,访问这个属性会导致错误。不过,可以使用命名函数表达式来达成相同的结果。例如:
var factorial = (function f(num){
if (num <= 1){
return 1;
} else {
return num * f(num-1);
}
});
以上代码创建了一个名为f()的命名函数表达式,然后将它赋值给变量factorial。即便把函数赋值给了另一个变量,函数的名字f 仍然有效,所以递归调用照样能正确完成。这种方式在严格模式和非严格模式下都行得通。
闭包:
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
在一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中。
闭包只能取得包含函数中任何变量的最后一个值。下面这个例子可以清晰地说明这个问题。
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
这个函数会返回一个函数数组。表面上看,似乎每个函数都应该返自己的索引值,即位置0 的函数返回0,位置1 的函数返回1,以此类推。但实际上,每个函数都返回10。因为每个函数的作用域链中都保存着createFunctions() 函数的活动对象, 所以它们引用的都是同一个变量i 。当createFunctions()函数返回后,变量i 的值是10,此时每个函数都引用着保存变量i 的同一个变量对象,所以在每个函数内部i 的值都是10。但是,我们可以通过创建另一个匿名函数强制让闭包的行为符合预期,如下所示。
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
这样没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋给数组。这里的匿名函数有一个参数num,也就是最终的函数要返回的值。在调用每个匿名函数时,我们传入了变量i。由于函数参数是按值传递的,所以就会将变量i 的当前值复制给参数num。而在这个匿名函数内部,又创建并返回了一个访问num 的闭包。这样一来,result 数组中的每个函数都有自己num 变量的一个副本,因此就可以返回各自不同的数值了。 - 基本包装类型
为了便于操作基本类型值,ECMAScript 还提供了3 个特殊的引用类型:Boolean、Number 和String。这些类型与本章介绍的其他引用类型相似,但同时也具有与各自的基本类型相应的特殊行为。实际上,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。
引用类型与基本包装类型的主要区别就是对象的生存期。使用new 操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。
当然,可以显式地调用Boolean、Number 和String 来创建基本包装类型的对象。不过,应该在绝对必要的情况下再这样做,因为这种做法很容易让人分不清自己是在处理基本类型还是引用类型的值。对基本包装类型的实例调用typeof 会返回"object",而且所有基本包装类型的对象都会被转换为布尔值true。
Object 构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。例如:
var obj = new Object("some text");
alert(obj instanceof String); //true
把字符串传给Object 构造函数,就会创建String 的实例;而传入数值参数会得到Number 的实例,传入布尔值参数就会得到Boolean 的实例。ECMAScript 中的函数在定义时不必指定是否返回值。实际上,任何函数在任何时候都可以通过return 语句后跟要返回的值来实现返回值。,位于return 语句之后的任何代码都永远不会执行。return 语句也可以不带有任何返回值。在这种情况下,函数在停止执行后将返回undefined值。这种用法一般用在需要提前停止函数执行而又不需要返回值的情况下。
严格模式对函数有一些限制:
不能把函数命名为eval 或arguments;
不能把参数命名为eval 或arguments;
不能出现两个命名参数同名的情况。
如果发生以上情况,就会导致语法错误,代码无法执行。
ECMAScript 函数不介意传递进来多少个参数,也不在乎传进来参数是什么数据类型。也就是说,即便你定义的函数只接收两个参数,在调用这个函数时也未必一定要传递两个参数。可以传递一个、三个甚至不传递参数,而解析器永远不会有什么怨言。之所以会这样,原因是ECMAScript 中的参数在内部是用一个数组来表示的。函数接收到的始终都是这个数组,而不关心数组中包含哪些参数(如果有参数的话)。如果这个数组中不包含任何元素,无所谓;如果包含多个元素,也没有问题。实际上,在函数体内可以通过arguments 对象来访问这个参数数组,从而获取传递给函数的每一个参数。其实,arguments 对象只是与数组类似(它并不是Array 的实例),因为可以使用方括号语法访问它的每一个元素(即第一个元素是arguments[0],第二个元素是argumetns[1],以此类推),使用length 属性来确定传递进来多少个参数。
ECMAScript 函数的一个重要特点:命名的参数只提供便利,但不是必需的。另外,在命名参数方面,其他语言可能需要事先创建一个函数签名,而将来的调用必须与该签名一致。但在ECMAScript 中,没有这些条条框框,解析器不会验证命名参数。通过访问arguments 对象的length 属性可以获知有多少个参数传递给了函数。arguments 对象可以与命名参数一起使用,关于arguments 的行为,还有一点比较有意思。那就是它的值永远与对应命名参数的值保持同步。
function doAdd(num1, num2) {
arguments[1] = 10;
alert(arguments[0] + num2);
}
每次执行这个doAdd()函数都会重写第二个参数,将第二个参数的值修改为10。因为arguments对象中的值会自动反映到对应的命名参数,所以修改arguments[1],也就修改了num2,结果它们的值都会变成10。不过,这并不是说读取这两个值会访问相同的内存空间;它们的内存空间是独立的,但它们的值会同步。另外还要记住,如果只传入了一个参数,那么为arguments[1]设置的值不会反应到命名参数中。这是因为arguments 对象的长度是由传入的参数个数决定的,不是由定义函数时的命名参数的个数决定的。
关于参数还要记住最后一点:没有传递值的命名参数将自动被赋予undefined 值。这就跟定义了变量但又没有初始化一样。例如,如果只给doAdd()函数传递了一个参数,则num2 中就会保存undefined 值。
严格模式对如何使用arguments 对象做出了一些限制。首先,像前面例子中那样的赋值会变得无效。也就是说,即使把arguments[1]设置为10,num2 的值仍然还是undefined。其次,重写arguments 的值会导致语法错误(代码将不会执行)。 - Object类型
创建对象:var obj = new Object() ;或者 var o = new Object; // 有效,但不推荐省略圆括号
Object 的每个实例都具有下列属性和方法:(Object 是所有对象的基础,因此所有对象都具有这些基本的属性和方法)
1. constructor:保存着用于创建当前对象的函数。对于var obj = new Object() 而言,构造函数(constructor)就是Object()
2. hasOwnProperty(propertyName):用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定(例如:o.hasOwnProperty("name"))
3. isPrototypeOf(object):用于检查传入的对象是否是传入对象的原型
4. propertyIsEnumerable(propertyName):用于检查给定的属性是否能够使用for-in 语句来枚举。与hasOwnProperty()方法一样,作为参数的属性名须 以字符串形式指定
5. toLocaleString():返回对象的字符串表示,该字符串与执行环境的地区对应
6. toString():返回对象的字符串表示
7. valueOf():返回对象的字符串、数值或布尔值表示。通常与toString()方法的返回值相同 - Global对象
Global(全局)对象可以说是ECMAScript 中最特别的一个对象了,因为不管你从什么角度上看,这个对象都是不存在的。ECMAScript 中的Global 对象在某种意义上是作为一个终极的“兜底儿对象”来定义的。换句话说,不属于任何其他对象的属性和方法,最终都是它的属性和方法。事实上,没有全局变量或全局函数;所有在全局作用域中定义的属性和函数,都是Global 对象的属性。如isNaN()、isFinite()、parseInt()以及parseFloat(),实际上全都是Global对象的方法。除此之,Global 对象还包含其他一些方法。
URI 编码方法:
Global 对象的encodeURI()和encodeURIComponent()方法可以对URI(Uniform ResourceIdentifiers,通用资源标识符)进行编码,以便发送给浏览器。有效的URI 中不能包含某些字符,例如空格。而这两个URI 编码方法就可以对URI 进行编码,它们用特殊的UTF-8 编码替换所有无效的字符,从而让浏览器能够接受和理解。其中,encodeURI()主要用于整个URI(例如,http://www.wrox.com/illegal value.htm),而encode-URIComponent()主要用于对URI 中的某一段(例如前面URI 中的illegal value.htm)进行编码。它们的主要区别在于,encodeURI()不会对本身属于URI 的特殊字符进行编码,例如冒号、正斜杠、问号和井字号;而encodeURIComponent()则会对它发现的任何非标准字符进行编码。
decodeURI()和decodeURIComponent()。其中,decodeURI()只能对使用encodeURI()替换的字符进行解码。
decodeURIComponent()能够解码使用encodeURIComponent()编码的所有字符,即它可以解码任何特殊字符的编码。
URI 方法encodeURI()、encodeURIComponent()、decodeURI()和decode-URIComponent()用于替代已经被ECMA-262 第3 版废弃的escape()和unescape()方法。URI 方法能够编码所有Unicode 字符,而原来的方法只能正确地编码ASCII 字符。因此在开发实践中,一定要使用URI 方法,不要使用escape()和unescape()方法。
eval()方法:
eval()方法就像是一个完整的ECMAScript 解析器,它只接受一个参数,即要执行的ECMAScript (或JavaScript)
字符串。看下面的例子:
eval("alert('hi')");
这行代码的作用等价于下面这行代码:
alert("hi");
当解析器发现代码中调用eval()方法时,它会将传入的参数当作实际的ECMAScript 语句来解析,然后把执行结果插入到原位置。通过eval()执行的代码被认为是包含该次调用的执行环境的一部分,因此被执行的代码具有与该执行环境相同的作用域链。
Global 对象还包含一些属性,其中一部分属性已经在本书前面介绍过了。例如,特殊的值undefined、NaN 以及Infinity 都是Global 对象的属性。此外,所有原生引用类型的构造函数,像Object 和Function,也都是Global 对象的属性。
ECMAScript 5 明确禁止给undefined、NaN 和Infinity 赋值,这样做即使在非严格模式下也会导致错误。 - window 对象
ECMAScript 虽然没有指出如何直接访问Global 对象,但Web 浏览器都是将这个全局对象作为window 对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了window对象的属性。 - 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的平方根(即2的平方根的倒数)
Math.SQRT2 :2的平方根
min()和max()方法用于确定一组数值中的最小值和最大值。这两个方法都可以接收任意多个数值参数
要找到数组中的最大或最小值,可以像下面这样使用apply()方法。
var values = [1, 2, 3, 4, 5, 6, 7, 8];
var max = Math.max.apply(Math, values);
这个技巧是把Math 对象作为apply()的第一个参数,从而正确地设置this 值。然后,可以将任何数组作为第二个参数。
将小数值舍入为整数的几个方法Math.ceil()、Math.floor()和Math.round():
这三个方法分别遵循下列舍入规则:
Math.ceil()执行向上舍入,即它总是将数值向上舍入为最接近的整数;
Math.floor()执行向下舍入,即它总是将数值向下舍入为最接近的整数;
Math.round()执行标准舍入,即它总是将数值四舍五入为最接近的整数(这也是我们在数学课上学到的舍入规则)。
random()方法:
Math.random()方法返回大于等于0 小于1 的一个随机数。
用下面的公式,就可以利用Math.random()从某个整数范围内随机选择一个值:
值 = Math.floor(Math.random() * 可能值的总数 + 第一个可能的值)
选择一个1到10 之间的数值:var num = Math.floor(Math.random() * 10 + 1);
可以通过一个函数来计算可能值的总数和第一个可能的值,例如:
function selectFrom(lowerValue, upperValue) {
var choices = upperValue - lowerValue + 1;
return Math.floor(Math.random() * choices + lowerValue);
}
var num = selectFrom(2, 10);
aler t(num); // 介于 2 和10 之间(包括 2 和 10)的一个数值
函数selectFrom()接受两个参数:应该返回的最小值和最大值。而用最大值减最小值再加1 得到了可能值的总数,然后它又把这些数值套用到了前面的公式中。这样,通过调用selectFrom(2,10)就可以得到一个介于2 和10 之间(包括2 和10)的数值了。
Math.abs(num): 返回num 的绝对值
Math.asin(x) :返回x 的反正弦值
Math.exp(num) :返回Math.E 的num 次幂
Math.atan(x): 返回x 的反正切值
Math.log(num) :返回num 的自然对数
Math.atan2(y,x): 返回y/x 的反正切值
Math.pow(num,power) :返回num 的power 次幂
Math.cos(x) :返回x 的余弦值
Math.sqrt(num): 返回num 的平方根
Math.sin(x): 返回x 的正弦值
Math.acos(x): 返回x 的反余弦值
Math.tan(x) :返回x 的正切值 - 递增和递减操作符(仅用于操作变量)
递增和递减操作符各有两个版本:前置型和后置型。顾名思义,前置型应该位于要操作的变量之前,而后置型则应该位于要操作的变量之后。
执行前置递增和递减操作时,变量的值都是在语句被求值以前改变的。
执行后置递增和递减操作时,在包含它们的语句被求值之后才执行的。 - 一元加操作符以一个加号(+)表示,放在数值前面,对数值不会产生任何影响:
var num = 25;
num = +num; // 仍然是25
在对非数值应用一元加操作符时,该操作符会像Number()转型函数一样对这个值执行转换。 - 一元减操作符主要用于表示负数
var num = 25;
num = -num; // 变成了-25
当应用于非数值时,一元减操作符遵循与一元加操作符相同的规则,最后再将得到的数值转换为负数。 - ECMAScript 中的所有数值都以IEEE-754 64 位格式存储,但位操作符并不直接操作64 位的值。而是先将64 位的值转换成32 位的整数,然后执行操作,最后再将结果转换回64 位。对于有符号的整数,32 位中的前31 位用于表示整数的值。第32 位用于表示数值的符号:0 表示正数,1 表示负数。这个表示符号的位叫做符号位,符号位的值决定了其他位数值的格式。其中,正数以纯二进制格式存储,31 位中的每一位都表示2 的幂。第一位(叫做位0)表示20,第二位表示21,以此类推。没有用到的位以0 填充, 即忽略不计。负数同样以二进制码存储,但使用的格式是二进制补码。计算一个数值的二进制补码,需要经过下列3 个步骤:
(1) 求这个数值绝对值的二进制码;
(2) 求二进制反码,即将0 替换为1,将1 替换为0;
(3) 得到的二进制反码加1。(要注意的是,在处理有符号整数时,是不能访问位31 的。)
在以二进制字符串形式输出一个负数时,我们看到的只是这个负数绝对值的二进制码前面加上了一个负号。如下面的例子所示:
var num = -18;
alert(num.toString(2)); // "-10010"
当对数值应用位操作符时,后台会发生如下转换过程:64 位的数值被转换成32位数值,然后执行位操作,最后再将32 位的结果转换回64 位数值。这样,表面上看起来就好像是在操作32 位数值,就跟在其他语言中以类似方式执行二进制操作一样。但这个转换过程也导致了一个严重的副效应,即在对特殊的NaN 和Infinity 值应用位操作时,这两个值都会被当成0 来处理。如果对非数值应用位操作符,会先使用Number()函数将该值转换为一个数值(自动完成),然后再应用位操作。得到的结果将是一个数值。 - 按位非操作符由一个波浪线(~)表示,执行按位非的结果就是返回数值的反码。按位非操作的本质:操作数的负值减1。
- 按位与操作符由一个和号字符(&)表示,它有两个操作符数。
从本质上讲,按位与操作就是将两个数值的每一位对齐,然后根据下表中的规则,对相同位置上的两个数执行AND 操作:
第一个数值的位 第二个数值的位 结 果
1 1 1
1 0 0
0 1 0
0 0 0
按位与操作只在两个数值的对应位都是1 时才返回1,任何一位是0,结果都是0。 - 按位或操作符由一个竖线符号(|)表示,同样也有两个操作数。
第一个数值的位 第二个数值的位 结 果
1 1 1
1 0 1
0 1 1
0 0 0
按位或操作在有一个位是1 的情况下就返回1,而只有在两个位都是0 的情况下才返回0 - 按位异或操作符由一个插入符号(^)表示,也有两个操作数
第一个数值的位 第二个数值的位 结 果
1 1 0
1 0 1
0 1 1
0 0 0
在两个数值对应位上只有一个1 时才返回1,如果对应的两位都是1 或都是0,则返回0。(相同得0,不同得1) - 左移操作符由两个小于号(<<)表示,这个操作符会将数值的所有位向左移动指定的位数。
var oldValue = 2; // 等于二进制的10
var newValue = oldValue << 5;
向左移位后,原数值的右侧多出了5 个空位。左移操作会以0 来填充这些空位。
左移不会影响操作数的符号位。换句话说,如果将-2 向左移动5 位,结果将是-64,而非64。 - 有符号的右移操作符由两个大于号(>>)表示,这个操作符会将数值向右移动,但保留符号位(即正负号标记)。有符号的右移操作与左移操作恰好相反。
在移位过程中,原数值中会出现空位。空位出现在原数值的左侧,此时ECMAScript 会用符号位的值来填充所有空位,以便得到一个完整的值。 - 无符号右移操作符由3 个大于号(>>>)表示,这个操作符会将数值的所有32 位都向右移动。对正数来说,无符号右移的结果与有符号右移相同。
但是对负数来说,情况就不一样了。首先,无符号右移是以0 来填充空位,而不是像有符号右移那样以符号位的值来填充空位。 - 布尔操作符一共有3 个:非(NOT)、与(AND)和或(OR)
- 逻辑非操作符由一个叹号(!)表示,可以应用于ECMAScript 中的任何值。无论这个值是什么数据类型,这个操作符都会返回一个布尔值。逻辑非操作符首先会将它的操作数转换为一个布尔值,然后再对其求反。逻辑非操作符遵循下列规则:
如果操作数是一个对象,返回false;
如果操作数是一个空字符串,返回true;
如果操作数是一个非空字符串,返回false;
如果操作数是数值0,返回true;
如果操作数是任意非0 数值(包括Infinity),返回false;
如果操作数是null,返回true;
如果操作数是NaN,返回true;
如果操作数是undefined,返回true。
逻辑非操作符也可以用于将一个值转换为与其对应的布尔值。而同时使用两个逻辑非操作符,实际上就会模拟Boolean()转型函数的行为。其中,第一个逻辑非操作会基于无论什么操作数返回一个布尔值,而第二个逻辑非操作则对该布尔值求反,于是就得到了这个值真正对应的布尔值。当然,最终结果与对这个值使用Boolean()函数相同 - 逻辑与操作符由两个和号(&&)表示,有两个操作数
逻辑与的真值表如下:
第一个操作数 第二个操作数 结 果
true true true
true false false
false true false
false false false
逻辑与操作可以应用于任何类型的操作数,而不仅仅是布尔值。在有一个操作数不是布尔值的情况下,逻辑与操作就不一定返回布尔值;此时,它遵循下列规则:
如果第一个操作数是对象,则返回第二个操作数;
如果第二个操作数是对象,则只有在第一个操作数的求值结果为true 的情况下才会返回该对象;
如果两个操作数都是对象,则返回第二个操作数;
如果有一个操作数是null,则返回null;
如果有一个操作数是NaN,则返回NaN;
如果有一个操作数是undefined,则返回undefined。
逻辑与操作属于短路操作,即如果第一个操作数能够决定结果,那么就不会再对第二个操作数求值。对于逻辑与操作而言,如果第一个操作数是false,则无论第二个操作数是什么值,结果都不再可能是true 了。
var found = true;
var result = (found && someUndefinedVariable); // 这里会发生错误
alert(result); // 这一行不会执行
在上面的代码中,当执行逻辑与操作时会发生错误,因为变量someUndefinedVariable 没有声明。由于变量found 的值是true,所以逻辑与操作符会继续对变量someUndefinedVariable 求值。但someUndefinedVariable 尚未定义,因此就会导致错误。这说明不能在逻辑与操作中使用未定义的值。
var found = false;
var result = (found && someUndefinedVariable); // 不会发生错误
alert(result); // 会执行("false")
在这个例子中,警告框会显示出来。无论变量someUndefinedVariable 有没有定义,也永远不会对它求值,因为第一个操作数的值是false。而这也就意味着逻辑与操作的结果必定是false,根本用不着再对&&右侧的操作数求值了。在使用逻辑与操作符时要始终铭记它是一个短路操作符。 - 逻辑或操作符由两个竖线符号(||)表示,有两个操作数
逻辑或的真值表如下:
第一个操作数 第二个操作数 结 果
true true true
true false true
false true true
false false false
与逻辑与操作相似,如果有一个操作数不是布尔值,逻辑或也不一定返回布尔值;此时,它遵循下列规则:
如果第一个操作数是对象,则返回第一个操作数;
如果第一个操作数的求值结果为false,则返回第二个操作数;
如果两个操作数都是对象,则返回第一个操作数;
如果两个操作数都是null,则返回null;
如果两个操作数都是NaN,则返回NaN;
如果两个操作数都是undefined,则返回undefined。
与逻辑与操作符相似,逻辑或操作符也是短路操作符。也就是说,如果第一个操作数的求值结果为true,就不会对第二个操作数求值了。
var found = true;
var result = (found || someUndefinedVariable); // 不会发生错误
alert(result); // 会执行("true")
这个例子跟前面的例子一样,变量someUndefinedVariable 也没有定义。但是,由于变量found的值是true,而变量someUndefinedVariable 永远不会被求值,因此结果就会输出"true"。如果像下面这个例子一样,把found 的值改为false,就会导致错误:
var found = false;
var result = (found || someUndefinedVariable); // 这里会发生错误
alert(result); // 这一行不会执行 - ECMAScript 定义了3 个乘性操作符:乘法、除法和求模。在操作数为非数值的情况下会执行自动的类型转换。如果参与乘性计算的某个操作数不是数值,后台会先使用Number()转型函数将其转换为数值。
- 乘法操作符由一个星号(*)表示,用于计算两个数值的乘积。
在处理特殊值的情况下,乘法操作符遵循下列特殊的规则:
1.如果操作数都是数值,执行常规的乘法计算,即两个正数或两个负数相乘的结果还是正数,而如果只有一个操作数有符号,那么结果就是负数。如果乘积超过了数值的表示范围,则返回Infinity 或-Infinity;
2.如果有一个操作数是NaN,则结果是NaN;
3.如果是Infinity 与0 相乘,则结果是NaN;
4.如果是Infinity 与非0 数值相乘,则结果是Infinity 或-Infinity,取决于有符号操作数的符号;
5.如果是Infinity 与Infinity 相乘,则结果是Infinity;
6.如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则。 - 除法操作符由一个斜线符号(/)表示,执行第二个操作数除第一个操作数的计算
与乘法操作符类似,除法操作符对特殊的值也有特殊的处理规则。这些规则如下:
1.如果操作数都是数值,执行常规的除法计算,即两个正数或两个负数相除的结果还是正数,而
2.如果只有一个操作数有符号,那么结果就是负数。如果商超过了ECMAScript 数值的表示范围,则返回Infinity 或-Infinity;
3.如果有一个操作数是NaN,则结果是NaN;
4.如果是Infinity 被Infinity 除,则结果是NaN;
5.如果是零被零除,则结果是NaN;
6.如果是非零的有限数被零除,则结果是Infinity 或-Infinity,取决于有符号操作数的符号;
7.如果是Infinity 被任何非零数值除,则结果是Infinity 或-Infinity,取决于有符号操作数的符号;
8.如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则。 - 求模(余数)操作符由一个百分号(%)表示
与另外两个乘性操作符类似,求模操作符会遵循下列特殊规则来处理特殊的值:
1.如果操作数都是数值,执行常规的除法计算,返回除得的余数;
2.如果被除数是无穷大值而除数是有限大的数值,则结果是NaN;
3.如果被除数是有限大的数值而除数是零,则结果是NaN;
4.如果是Infinity 被Infinity 除,则结果是NaN;
5.如果被除数是有限大的数值而除数是无穷大的数值,则结果是被除数;
6.如果被除数是零,则结果是零;
7.如果有一个操作数不是数值,则在后台调用Number()将其转换为数值,然后再应用上面的规则。 - 加法操作符(+)
如果两个操作符都是数值,执行常规的加法计算,然后根据下列规则返回结果:
1.如果有一个操作数是NaN,则结果是NaN;
2.如果是Infinity 加Infinity,则结果是Infinity;
3.如果是-Infinity 加-Infinity,则结果是-Infinity;
4.如果是Infinity 加-Infinity,则结果是NaN;
5.如果是+0 加+0,则结果是+0;
6.如果是-0 加-0,则结果是-0;
7.如果是+0 加-0,则结果是+0。
不过,如果有一个操作数是字符串,那么就要应用如下规则:
1.如果两个操作数都是字符串,则将第二个操作数与第一个操作数拼接起来;
2.如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接起来。
3.如果有一个操作数是对象、数值或布尔值,则调用它们的toString()方法取得相应的字符串值,然后再应用前面关于字符串的规则。对于undefined 和null,则分别调用String()函数并取得字符串"undefined"和"null"。 - 减法操作符(-)
如果两个操作符都是数值,则执行常规的算术减法操作并返回结果;
1.如果有一个操作数是NaN,则结果是NaN;
2.如果是Infinity 减Infinity,则结果是NaN;
3.如果是-Infinity 减-Infinity,则结果是NaN;
4.如果是Infinity 减-Infinity,则结果是Infinity;
5.如果是-Infinity 减Infinity,则结果是-Infinity;
6.如果是+0 减+0,则结果是+0;
7.如果是+0 减-0,则结果是-0;
8.如果是-0 减-0,则结果是+0;
9.如果有一个操作数是字符串、布尔值、null 或undefined,则先在后台调用Number()函数将其转换为数值,然后再根据前面的规则执行减法计算。如果转换的结果是NaN,则减法的结果就是NaN;
10.如果有一个操作数是对象,则调用对象的valueOf()方法以取得表示该对象的数值。如果得到的值是NaN,则减法的结果就是NaN。如果对象没有valueOf()方法,则调用其toString()方法并将得到的字符串转换为数值。 - 关系操作符小于(<)、大于(>)、小于等于(<=)和大于等于(>=) 返回布尔值
1.如果两个操作数都是数值,则执行数值比较。
2.如果两个操作数都是字符串,则比较两个字符串对应的字符编码值。
3.如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较。
4.如果一个操作数是对象,则调用这个对象的valueOf()方法,用得到的结果按照前面的规则执行比较。如果对象没有valueOf()方法,则调用toString()方法,并用得到的结果根据前面的规则执行比较。
5.如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较。
6.任何操作数与NaN 进行关系比较,结果都是false。 - 相等操作符
相等和不相等
ECMAScript 中的相等操作符由两个等于号(==)表示,如果两个操作数相等,则返回true。而不相等操作符由叹号后跟等于号(!=)表示,如果两个操作数不相等,则返回true。这两个操作符都会先转换操作数(通常称为强制转型),然后再比较它们的相等性。在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:
1.如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值——false 转换为0,而true 转换为1;
2.如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
3.如果一个操作数是对象,另一个操作数不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较;
这两个操作符在进行比较时则要遵循下列规则。
null 和undefined 是相等的。
要比较相等性之前,不能将null 和undefined 转换成其他任何值。
如果有一个操作数是NaN,则相等操作符返回false,而不相等操作符返回true。
重要提示:
即使两个操作数都是NaN,相等操作符也返回false;因为按照规则,NaN 不等于NaN。
如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true;否则,返回false。
表 达 式 值 表 达 式 值
null == undefined true true == 1 true
"NaN" == NaN false true == 2 false
5 == NaN false undefined == 0 false
NaN == NaN false null == 0 false
NaN != NaN true "5"==5 true
false == 0 true
全等和不全等
除了在比较之前不转换操作数之外,全等和不全等操作符与相等和不相等操作符没有什么区别。全等操作符由3 个等于号(===)表示,它只在两个操作数未经转换就相等的情况下返回true。
不全等操作符由一个叹号后跟两个等于号(!==)表示,它在两个操作数未经转换就不相等的情况下返回true。
null == undefined 会返回true,因为它们是类似的值;但null === undefined 会返回false,因为它们是不同类型的值。
由于相等和不相等操作符存在类型转换问题,而为了保持代码中数据类型的完整性,我们推荐使用全等和不全等操作符。 - 条件操作符
variable = boolean_expression ? true_value : false_value;
这行代码的含义就是基于对boolean_expression 求值的结果,决定给变量variable赋什么值。如果求值结果为true,则给变量variable 赋true_value 值;如果求值结果为false,则给变量variable 赋false_value 值。 - 赋值操作符(=)
作用就是把右侧的值赋给左侧的变量
算术操作符(以及个别的其他操作符)都有对应的复合赋值操作符。这些操作符如下所示:
乘/赋值(*=);
除/赋值(/=);
模/赋值(%=);
加/赋值(+=);
减/赋值(-=);
左移/赋值(<<=);
有符号右移/赋值(>>=);
无符号右移/赋值(>>>=)
设计这些操作符的主要目的就是简化赋值操作。使用它们不会带来任何性能的提升。 - 逗号操作符
逗号操作符可以在一条语句中执行多个操作
var iNum1=1, iNum2=2,iNum3=3;
逗号操作符多用于声明多个变量;但除此之外,逗号操作符还可以用于赋值。在用于赋值时,逗号操作符总会返回表达式中的最后一项,如下面的例子所示:
var iNum= (5, 1, 4, 8, 0); // num 的值为0
由于0 是表达式中的最后一项,因此num 的值就是0。 - 运算符优先级,从高到低,相同优先级的运算符按从左至右的顺序计算
- if语句
if (condition) {
statement1
}
else {
statement2
}
其中的condition(条件)可以是任意表达式;而且对这个表达式求值的结果不一定是布尔值。ECMAScript 会自动调用Boolean()转换函数将这个表达式的结果转换为一个布尔值。如果对condition求值的结果是true,则执行statement1(语句1),如果对condition 求值的结果是false,则执行statement2(语句2)。而且这两个语句既可以是一行代码,也可以是一个代码块(以一对花括号括起来的多行代码) - do-while语句
do {
statement
} while (expression);
do-while 语句是一种后测试循环语句,即只有在循环体中的代码执行之后,才会测试出口条件。
换句话说,在对条件表达式求值之前,循环体内的代码至少会被执行一次。 - while语句
while 语句属于前测试循环语句,也就是说,在循环体内的代码被执行之前,就会对出口条件求值。
因此,循环体内的代码有可能永远不会被执行
while(expression) {
statement
} - for语句
for 语句也是一种前测试循环语句,但它具有在执行循环之前初始化变量和定义循环后要执行的代码的能力。
for (initialization; expression; post-loop-expression) {
statement
}
有必要指出的是,在for 循环的变量初始化表达式中,也可以不使用var 关键字。该变量的初始
化可以在外部执行,例如:
var count = 10;
for (var i = 0; i < count; i++){
alert(i);
}
var count = 10;
var i;
for (i = 0; i < count; i++){
alert(i);
}
for 语句中的初始化表达式、控制表达式和循环后表达式都是可选的。将这三个表达式全部省略,就会创建一个无限循环,例如:
for (;;) { // 无限循环
doSomething();
}
而只给出控制表达式实际上就把for 循环转换成了while 循环 - for-in语句
for-in 语句是一种精准的迭代语句,可以用来枚举对象的属性
for (property in expression) {
statement
}
ECMAScript 对象的属性没有顺序。因此,通过for-in 循环输出的属性名的顺序是不可预测的。具体来讲,所有属性都会被返回一次,但返回的先后次序可能会因浏览器而异。但是,如果表示要迭代的对象的变量值为null 或undefined,for-in 语句会抛出错误。ECMAScript 5 更正了这一行为;对这种情况不再抛出错误,而只是不执行循环体。建议在使用for-in 循环之前,先检测确认该对象的值不是null 或undefined。 - label语句
使用label 语句可以在代码中添加标签,以便将来使用。以下是label 语句的语法:
label: statement
下面是一个示例:
start: for (var i=0; i < count; i++) {
alert(i);
}
这个例子中定义的start 标签可以在将来由break 或continue 语句引用。加标签的语句一般都要与for 语句等循环语句配合使用。 - break和continue语句
break 和continue 语句用于在循环中精确地控制代码的执行。其中,break 语句会立即退出循环,
强制继续执行循环后面的语句。而continue 语句虽然也是立即退出循环,但退出循环后会从循环的顶
部继续执行。 - with语句
with 语句的作用是将代码的作用域设置到一个特定的对象中。with 语句的语法如下:
with (expression) statement;
定义with 语句的目的主要是为了简化多次编写同一个对象的工作,如下面的例子所示:
var qs = location.search.substring(1);
var hostName = location.hostname;
var url = location.href;
上面几行代码都包含location 对象。如果使用with 语句,可以把上面的代码改写成如下所示:
with(location){
var qs = search.substring(1);
var hostName = hostname;
var url = href;
}
WithStatementExample01.htm
在这个重写后的例子中,使用with 语句关联了location 对象。这意味着在with 语句的代码块内部,每个变量首先被认为是一个局部变量,而如果在局部环境中找不到该变量的定义,就会查询location 对象中是否有同名的属性。如果发现了同名属性,则以location 对象属性的值作为变量的值。
严格模式下不允许使用with 语句,否则将视为语法错误。
由于大量使用with 语句会导致性能下降,同时也会给调试代码造成困难,因此在开发大型应用程序时,不建议使用with 语句。 - switch语句
switch 语句与if 语句的关系最为密切,而且也是在其他语言中普遍使用的一种流控制语句。
ECMAScript 中switch 语句的语法与其他基于C 的语言非常接近,如下所示:
switch (expression) {
case value: statement
break;
case value: statement
break;
case value: statement
break;
case value: statement
break;
default: statement
}
switch 语句中的每一种情形(case)的含义是:“如果表达式等于这个值(value),则执行后面的
语句(statement)”。而break 关键字会导致代码执行流跳出switch 语句。如果省略break 关键字,
就会导致执行完当前case 后,继续执行下一个case。最后的default 关键字则用于在表达式不匹配前
面任何一种情形的时候,执行机动代码(因此,也相当于一个else 语句)。
可以在
switch 语句中使用任何数据类型,无论是字符串,还是对象。每个case 的值不一定是常量,可以是变量,甚至是表达式。
switch 语句在比较值时使用的是全等操作符,因此不会发生类型转换(例如,字符串"10"不等于数值10)。 - 闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多,建议只在绝对必要时再考虑使用闭包。大家还是要慎重使用闭包。
- HTML 中样式有3 种不同的方式:
①<link/>元素包含外部样式表文件
②使用<style/>元素定义嵌入式样式
③使用style 特性定义针对特定元素的样式
支持style 特性的HTML 元素在JavaScript 中都有一个对应的style 属性,包含着通过HTML 的style 特性指定的所有样式信息,但不包含与外部样式表或嵌入样式表经层叠而来的样式。在style 特性中指定的任何CSS 属性都将表现为这个style 对象的相应属性。对于使用短划线(分隔不同的词汇,例如background-image --> backgroundImage)的CSS 属性名,必须将其转换成驼峰大小写形式,才能通过JavaScript 来访问。
其中一个不能直接转换的CSS 属性就是float。由于float 是JavaScript 中的保留字,因此不能用作属性名。
“DOM2 级样式”规范规定样式对象上相应的属性名应该是cssFloat;Firefox、Safari、Opera 和Chrome 都支持这个属性,而IE支持的则是styleFloat。
在标准模式下,所有度量值都必须指定一个度量单位。在混杂模式下,可以将style.width 设置为"20",浏览器会假设它是"20px";但在标准模式下,将style.width 设置为"20"会导致被忽略——因为没有度量单位。SO,最好始终都指定度量单位。
要从元素的样式中移除某个CSS 属性,需要使用removeProperty()方法。
在不确定某个给定的CSS 属性拥有什么默认值的情况下,就可以使用这个方法。只要移除相应的属性,就可以为元素应用默认值。 - JSON:ECMAScript 5 对解析JSON 的行为进行规范,定义了全局对象JSON。
JSON 对象有两个方法:stringify()和parse()。在最简单的情况下,这两个方法分别用于把JavaScript 对象序列化为JSON字符串和把JSON 字符串解析为原生JavaScript 值。
在序列化JavaScript 对象时,所有函数及原型成员都会被有意忽略,不体现在结果中。此外,值为undefined 的任何属性也都会被跳过。结果中最终都是值为有效JSON 数据类型的实例属性。
如果传给JSON.parse()的字符串不是有效的JSON,该方法会抛出错误。
var book = {
title: "Luo",
city:"Yang"
};
var jsonText = JSON.stringify(book);
默认情况下,JSON.stringify()输出的JSON 字符串不包含任何空格字符或缩进
var bookCopy = JSON.parse(jsonText);
注意,虽然book 与bookCopy 具有相同的属性,但它们是两个独立的、没有任何关系的对象。
JSON.stringify()除了要序列化的JavaScript 对象外,还可以接收另外两个参数,这两个参数用于指定以不同的方式序列化JavaScript 对象。第一个参数是个过滤器,可以是一个数组,也可以是一个函数;第二个参数是一个选项,表示是否在JSON 字符串中保留缩进。
JSON.parse()方法也可以接收另一个参数,该参数是一个函数,将在每个键值对儿上调用。它接收两个参数,一个键和一个值,需要返回一个值。如果函数返回undefined,则表示要从结果中删除相应的键;如果返回其他值,则将该值插入到结果中。 - 创建对象
⑴工厂模式:
这种模式抽象了创建具体对象的过程,在ECMAScript 中无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象的细节,如:
function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
函数createPerson()能够根据接受的参数来构建一个包含所有必要信息的Person 对象。可以无数次地调用这个函数,而每次它都会返回一个包含三个属性一个方法的对象。工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
⑵构造函数模式:
创建自定义的构造函数,从而定义自定义对象类型的属性和方法。例如,可以使用构造函数模式将前面的例子重写如下。
function Person(name, age, job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
与工厂模式的不同之处有:
①没有显式地创建对象
②直接将属性和方法赋给了this 对象
③没有return 语句
此外,还应该注意到函数名Person 使用的是大写字母P。按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。主要是为了区别于ECMAScript 中的其他函数;因为构造函数本身也是函数,只不过可以用来创建对象而已。要创建Person 的新实例,必须使用new 操作符。以这种方式调用构造函数实际上会经历以下4
个步骤:
① 创建一个新对象;
② 将构造函数的作用域赋给新对象(因此this 就指向了这个新对象);
③ 执行构造函数中的代码(为这个新对象添加属性);
④ 返回新对象。
在前面例子的最后,person1 和person2 分别保存着Person 的一个不同的实例。这两个对象都有一个constructor(构造函数)属性,该属性指向Person,如下所示。
alert(person1.constructor == Person); //true
alert(person2.constructor == Person); //true
对象的constructor 属性最初是用来标识对象类型的。但是,提到检测对象类型,还是instanceof操作符要更可靠一些。我们在这个例子中创建的所有对象既是Object 的实例,同时也是Person的实例,这一点通过instanceof 操作符可以得到验证。
alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。
构造函数与其他函数的唯一区别,就在于调用它们的方式不同。不过,构造函数毕竟也是函数,不存在定义构造函数的特殊语法。任何函数,只要通过new 操作符来调用,那它就可以作为构造函数;而任何函数,如果不通过new 操作符来调用,那它跟普通函数也不会有什么两样。
// 当作构造函数使用
var person = new Person("Nicholas", 29, "Software Engineer");
person.sayName(); //"Nicholas"
// 作为普通函数调用
Person("Greg", 27, "Doctor"); // 添加到window
window.sayName(); //"Greg"
使用构造函数的主要问题就是每个方法都要在每个实例上重新创建一遍。
⑶原型模式:
创建的每个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。换句话说,不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。(创建对象时变量前面的下划线[如:_iText]是一种常用的记号,用于表示只能通过对象方法访问的属性)
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
person1.sayName(); //"Nicholas"
var person2 = new Person();
person2.sayName(); //"Nicholas"
alert(person1.sayName == person2.sayName); //true
与构造函数模式不同的是,新对象的这些属性和方法是由所有实例共享的。
只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包含一个指向prototype 属性所在函数的指针。就拿前面的例子来说,Person.prototype. constructor 指向Person。而通过这个构造函数,我们还可继续为原型对象添加其他属性和方法。创建了自定义的构造函数之后,其原型对象默认只会取得constructor 属性;至于其他方法,则
都是从Object 继承而来的。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。。ECMA-262 第5 版中管这个指针叫[[Prototype]]。要明确的真正重要的一点就是,这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。
可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。从本质上讲,如果[[Prototype]]指向调用isPrototypeOf()方法的对象(Person.prototype),那么这个方法就返回true,如下所示:
alert(Person.prototype.isPrototypeOf(person1)); //true
alert(Person.prototype.isPrototypeOf(person2)); //true
这里,我们用原型对象的isPrototypeOf()方法测试了person1 和person2。因为它们内部都有一个指向Person.prototype 的指针,因此都返回了true。
ECMAScript 5 增加了一个新方法,叫Object.getPrototypeOf(),在所有支持的实现中,这个方法返回[[Prototype]]的值。例如:
alert(Object.getPrototypeOf(person1) == Person.prototype); //true
alert(Object.getPrototypeOf(person1).name); //"Nicholas"
每当代码读取某个对象的某个属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先从对象实例本身开始。如果在实例中找到了具有给定名字的属性,则返回该属性的值;如果没有找到,则继续搜索指针指向的原型对象,在原型对象中查找具有给定名字的属性。如果在原型对象中找到了这个属性,则返回该属性的值。
虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性。来看下面的例子。
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name = "Greg";
alert(person1.name); //"Greg"——来自实例
alert(person2.name); //"Nicholas"——来自原型
当为对象实例添加一个属性时,这个属性就会屏蔽原型对象中保存的同名属性;换句话说,添加这个属性只会阻止我们访问原型中的那个属性,但不会修改那个属性。即使将这个属性设置为null,也只会在实例中设置这个属性,而不会恢复其指向原型的连接。不过,使用delete 操作符则可以完全删除实例属性,从而让我们能够重新访问原型中的属性,如下所示。
function Person(){
}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.name = "Greg";
alert(person1.name); //"Greg"——来自实例
alert(person2.name); //"Nicholas"——来自原型
delete person1.name;
alert(person1.name); //"Nicholas"——来自原型
使用hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。这个方法(不要忘了它是从Object 继承来的)只在给定属性存在于对象实例中时,才会返回true。
in 操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。alert("name" in person1);
同时使用hasOwnProperty()方法和in 操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中。由于in 操作符只要通过对象能够访问到属性就返回true,hasOwnProperty()只在属性存在于实例中时才返回true,因此只要in 操作符返回true 而hasOwnProperty()返回false,就可以确定属性是原型中的属性。
要取得对象上所有可枚举的实例属性,可以使用ECMAScript 5 的Object.keys()方法。这个方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
如果想要得到所有实例属性,无论它是否可枚举,都可以使用Object.getOwnPropertyNames()方法。
用一个包含所有属性和方法的对象字面量来重写整个原型对象,如下面的例子所示:
function Person(){
}
Person.prototype = {
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};
每创建一个函数,就会同时创建它的prototype 对象,这个对象也会自动获得constructor 属性。而我们在这里使用的语法,本质上完全重写了默认的prototype 对象,因此constructor 属性也就变成了新对象的constructor 属性(指向Object 构造函数),不再指向Person 函数。此时,尽管instanceof操作符还能返回正确的结果,但通过constructor 已经无法确定对象的类型了,如下所示:
var friend = new Person();
alert(friend instanceof Object); //true
alert(friend instanceof Person); //true
alert(friend.constructor == Person); //false
alert(friend.constructor == Object); //true
用instanceof 操作符测试Object 和Person 仍然返回true,但constructor 属性则等于Object 而不等于Person 了。如果constructor 的值真的很重要,可以像下面这样特意将它设置回适当的值。
function Person(){
}
Person.prototype = {
constructor : Person,
name : "Nicholas",
age : 29,
job: "Software Engineer",
sayName : function () {
alert(this.name);
}
};
以这种方式重设constructor 属性会导致它的[[Enumerable]]特性被设置为true。默认情况下,原生的constructor 属性是不可枚举的。
var friend = new Person();
Person.prototype.sayHi = function(){
alert("hi");
};
friend.sayHi(); //"hi"(没有问题!)
function Person(){
}
var friend = new Person();
Person.prototype = {
constructor: Person,
sayHi : function () {
alert("hi");
}
};
friend.sayHi (); //error
可以随时为原型添加属性和方法,并且修改能够立即在所有对象实例中反映出来,但如果是重写整个原型对象,那么情况就不一样了。重写原型对象切断了现有原型与任何之前已经存在的对象实例之间的联系;它们引用的仍然是最初的原型。
所有原生引用类型(Object、Array、String,等等)都在其构造函数的原型上定义了方法。例如,在Array.prototype 中可以找到sort()方法,而在String.prototype 中可以找到substring()方法,如下所示。
alert(typeof Array.prototype.sort); //"function"
alert(typeof String.prototype.substring); //"function"
通过原生对象的原型,不仅可以取得所有默认方法的引用,而且也可以定义新方法。可以像修改自定义对象的原型一样修改原生对象的原型,因此可以随时添加方法。下面的代码就给基本包装类型String 添加了一个名为startsWith()的方法。
String.prototype.startsWith = function (text) {
return this.indexOf(text) == 0;
};
var msg = "Hello world!";
alert(msg.startsWith("Hello")); //true
原型模式的最大问题是由其共享的本性所导致的。原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性倒也说得过去,毕竟通过在实例上添加一个同名属性,可以隐藏原型中的对应属性。然而,对于包含引用类型值的属性来说,问题就比较突出了。
function Person(){
}
Person.prototype = {
constructor: Person,
name : "Nicholas",
age : 29,
job : "Software Engineer",
friends : ["Shelby", "Court"],
sayName : function () {
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
alert(person1.friends); //"Shelby,Court,Van"
alert(person2.friends); //"Shelby,Court,Van"
alert(person1.friends === person2.friends); //true
有两种方式来确定原型和实例之间的关系:第一种方式是使用instanceof 操作符,第二种方式是使用isPrototypeOf()方法。 - window 对象
BOM 的核心对象是window,它表示浏览器的一个实例。在浏览器中,window 对象有双重角色,它既是通过JavaScript 访问浏览器窗口的一个接口,又是ECMAScript 规定的Global 对象。在全局作用域中声明的变量、函数都会变成window 对象的属性和方法。
定义全局变量与在window 对象上直接定义属性的差别:
①全局变量不能通过delete 操作符删除,而直接在window 对象上的定义的属性可以
②尝试访问未声明的变量会抛出错误,但是通过查询window 对象,可以知道某个可能未声明的变量是否存在。例如:
//这里会抛出错误,因为oldValue 未定义
var newValue = oldValue;
//这里不会抛出错误,因为这是一次属性查询
//newValue 的值是undefined
var newValue = window.oldValue;
用来确定和修改window 对象位置的属性和方法有很多。IE、Safari、Opera 和Chrome 都提供了screenLeft 和screenTop 属性,分别用于表示窗口相对于屏幕左边和上边的位置。Firefox 则在screenX 和screenY 属性中提供相同的窗口位置信息,Safari 和Chrome 也同时支持这两个属性。Opera虽然也支持screenX 和screenY 属性,但与screenLeft 和screenTop 属性并不对应,因此建议大家不要在Opera 中使用它们。
使用下列代码可以跨浏览器取得窗口左边和上边的位置:
var leftPos = (typeof window.screenLeft == "number") ?
window.screenLeft : window.screenX;
var topPos = (typeof window.screenTop == "number") ?
window.screenTop : window.screenY;
使用moveTo()和moveBy()方法倒是有可能将窗口精确地移动到一个新位置。这两个方法都接收两个参数,其中moveTo()接收的是新位置的x 和y 坐标值,而moveBy()接收的是在水平和垂直方向上移动的像素数。
使用resizeTo()和resizeBy()方法可以调整浏览器窗口的大小。这两个方法都接收两个参数,其中resizeTo()接收浏览器窗口的新宽度和新高度,而resizeBy()接收新窗口与原窗口的宽度和高度之差。
取得页面视口的大小,如下所示:
var pageWidth = window.innerWidth,
pageHeight = window.innerHeight;
if (typeof pageWidth != "number"){
if (document.compatMode == "CSS1Compat"){
pageWidth = document.documentElement.clientWidth;
pageHeight = document.documentElement.clientHeight;
} else {
pageWidth = document.body.clientWidth;
pageHeight = document.body.clientHeight;
}
}
在以上代码中,我们首先将window.innerWidth 和window.innerHeight 的值分别赋给了pageWidth 和pageHeight。然后检查pageWidth 中保存的是不是一个数值;如果不是,则通过检查document.compatMode来确定页面是否处于标准模式。如果是,则分别使用document.documentElement.clientWidth 和 document.documentElement.client-Height 的值。否则,就使用document.body.clientWidth 和document.body.clientHeight 的值。
使用window.open()方法既可以导航到一个特定的URL,也可以打开一个新的浏览器窗口。这个方法可以接收4 个参数:要加载的URL、窗口目标、一个特性字符串以及一个表示新页面是否取代浏览器历史记录中当前加载页面的布尔值。通常只须传递第一个参数,最后一个参数只在不打开新窗口的情况下使用。如果为window.open()传递了第二个参数,而且该参数是已有窗口或框架的名称,那么就会在具有该名称的窗口或框架中加载第一个参数指定的URL。第二个参数也可以是一个特殊的窗口名称:_self、_parent、_top 或_blank。如果给window.open()传递的第二个参数并不是一个已经存在的窗口或框架,那么该方法就会根据在第三个参数位置上传入的字符串创建一个新窗口或新标签页。如果没有传入第三个参数,那么就会打开一个带有全部默认设置(工具栏、地址栏和状态栏等)的新浏览器窗口(或者打开一个新标签页——根据浏览器设置)。在不打开新窗口的情况下,会忽略第三个参数。第三个参数是一个逗号分隔的设置字符串,表示在新窗口中都显示哪些特性。下表列出了可以出现在这个字符串中的设置选项。 - setTimeout()
超时调用需要使用window 对象的setTimeout()方法,它接受两个参数:要执行的代码和以毫秒表示的时间(即在执行代码前需要等待多少毫秒)。其中,第一个参数可以是一个包含JavaScript 代码的字符串(就和在eval()函数中使用的字符串一样),也可以是一个函数。例如,下面对setTimeout()的两次调用都会在一秒钟后显示一个警告框。
//不建议传递字符串!
setTimeout("alert('Hello world!') ", 1000);
//推荐的调用方式
setTimeout(function() {
alert("Hello world!");
}, 1000);
虽然这两种调用方式都没有问题,但由于传递字符串可能导致性能损失,因此不建议以字符串作为第一个参数。
第二个参数是一个表示等待多长时间的毫秒数,但经过该时间后指定的代码不一定会执行。JavaScript 是一个单线程序的解释器,因此一定时间内只能执行一段代码。为了控制要执行的代码,就有一个JavaScript 任务队列。这些任务会按照将它们添加到队列的顺序执行。setTimeout()的第二个参数告诉JavaScript 再过多长时间把当前任务添加到队列中。如果队列是空的,那么添加的代码会立即执行;如果队列不是空的,那么它就要等前面的代码执行完了以后再执行。调用setTimeout()之后,该方法会返回一个数值ID,表示超时调用。这个超时调用ID 是计划执行代码的唯一标识符,可以通过它来取消超时调用。要取消尚未执行的超时调用计划,可以调用clearTimeout()方法并将相应的超时调用ID 作为参数传递给它,如下所示。
//设置超时调用
var timeoutId = setTimeout(function() {
alert("Hello world!");
}, 1000);
//注意:把它取消
clearTimeout(timeoutId);
只要是在指定的时间尚未过去之前调用clearTimeout(),超时调用的代码都是在全局作用域中执行的,因此函数中this 的值在非严格模式下指向window 对象,在严格模式下是undefined。 - setInterval()
按照指定的时间间隔重复执行代码,直至间歇调用被取消或者页面被卸载。它接受的参数与setTimeout()相同:要执行的代码(字符串或函数)和每次执行之前需要等待的毫秒数。下面来看一个例子。
//不建议传递字符串!
setInterval ("alert('Hello world!') ", 10000);
//推荐的调用方式
setInterval (function() {
alert("Hello world!");
}, 10000);
调用setInterval()方法同样也会返回一个间歇调用ID,该ID 可用于在将来某个时刻取消间歇调用。要取消尚未执行的间歇调用,可以使用clearInterval()方法并传入相应的间歇调用ID。取消间歇调用的重要性要远远高于取消超时调用,因为在不加干涉的情况下,间歇调用将会一直执行到页面卸载。 - 浏览器通过alert()、confirm()和prompt()方法可以调用系统对话框向用户显示消息。系统对话框与在浏览器中显示的网页没有关系,也不包含HTML。它们的外观由操作系统及(或)浏览器设置决定,而不是由CSS 决定。此外,通过这几个方法打开的对话框都是同步和模态的。也就是说,显示这些对话框的时候代码会停止执行,而关掉这些对话框后代码又会恢复执行。
alert()方法:这个方法接受一个字符串并将其显示给用户。具体来说,调用alert()方法的结果就是向用户显示一个系统对话框,其中包含指定的文本和一个OK(“确定”)按钮。使用alert()生成的“警告”对话框向用户显示一些他们无法控制的消息,例如错误消息。而用户只能在看完消息后关闭对话框。
confirm()方法:从向用户显示消息的方面来看,这种“确认”对话框很像是一个“警告”对话框。但二者的主要区别在于“确认”对话框除了显示OK 按钮外,还会显示一个Cancel(“取消”)按钮,两个按钮可以让用户决定是否执行给定的操作。为了确定用户是单击了OK 还是Cancel,可以检查confirm()方法返回的布尔值:true 表示单击了OK,false 表示单击了Cancel 或单击了右上角的X 按钮。
prompt()方法:这是一个“提示”框,用于提示用户输入一些文本。提示框中除了显示OK 和Cancel 按钮之外,还会显示一个文本输入域,以供用户在其中输入内容。prompt()方法接受两个参数:要显示给用户的文本提示和文本输入域的默认值(可以是一个空字符串)。如果用户单击了OK 按钮,则prompt()返回文本输入域的值;如果用户单击了Cancel 或没有单击OK 而是通过其他方式关闭了对话框,则该方法返回null。
还有两个可以通过JavaScript 打开的对话框,即“查找”和“打印”。这两个对话框都是异步显示的,能够将控制权立即交还给脚本。这两个对话框与用户通过浏览器菜单的“查找”和“打印”命令打开的对话框相同。而在JavaScript 中则可以像下面这样通过window 对象的find()和print()方法打开它们。
//显示“打印”对话框
window.print();
//显示“查找”对话框
window.find(); - location 对象
提供了与当前窗口中加载的文档有关的信息,还提供了一些导航功能。事实上,location 对象是很特别的一个对象,因为它既是window 对象的属性,也是document 对象的属性;换句话说,window.location 和document.location 引用的是同一个对象。location 对象的用处不只表现在它保存着当前文档的信息,还表现在它将URL 解析为独立的片段,让开发人员可以通过不同的属性访问这些片段。下表列出了location 对象的所有属性(注:省略了每个属性前面的location 前缀)。
尽管location.search 返回从问号到URL 末尾的所有内容,但却没有办法逐个访问其中的每个查询字符串参数。为此,可以像下面这样创建一个函数,用以解析查询字符串,然后返回包含所有参数的一个对象:
function getQueryStringArgs(){
//取得查询字符串并去掉开头的问号
var qs = (location.search.length > 0 ? location.search.substring(1) : ""),
//保存数据的对象
args = {},
//取得每一项
items = qs.length ? qs.split("&") : [],
item = null,
name = null,
value = null,
//在for 循环中使用
i = 0,
len = items.length;
//逐个将每一项添加到args 对象中
for (i=0; i < len; i++){
item = items[i].split("=");
name = decodeURIComponent(item[0]);//因为查询字符串应该是被编码过的
value = decodeURIComponent(item[1]);
if (name.length) {
args[name] = value;
}
}
return args;
}
location.assign("http://www.wrox.com");可以立即打开新URL 并在浏览器的历史记录中生成一条记录。如果是将location.href或window.location 设置为一个URL 值,也会以该值调用assign()方法。例如,下列两行代码与显式调用assign()方法的效果完全一样。
window.location = "http://www.wrox.com";
location.href = "http://www.wrox.com";
每次修改location 的属性(hash 除外),页面都会以新URL 重新加载。
replace()这个方法只接受一个参数,即要导航到的URL;结果虽然会导致浏览器位置改变,但不会在历史记录中生成新记
录。在调用replace()方法之后,用户不能回到前一个页面。
reload()这个方法作用是重新加载当前显示的页面。如果调用reload()时不传递任何参数,页面就会以最有效的方式重新加载。也就是说,如果页面自上次请求以来并没有改变过,页面就会从浏览器缓存中重新加载。如果要强制从服务器重新加载,则需要像下面这样为该方法传递参数true。
location.reload(); //重新加载(有可能从缓存中加载)
location.reload(true); //重新加载(从服务器重新加载)
位于reload()调用之后的代码可能会也可能不会执行,这要取决于网络延迟或系统资源等因素。为此,最好将reload()放在代码的最后一行。
navigator 对象却是所有支持JavaScript 的浏览器所共有的。与其他BOM 对象的情况一样,每个浏览器中的navigator 对象也都有一套自己的属性。
appName: 完整的浏览器名称
cookieEnabled: 表示cookie是否启用
onLine: 表示浏览器是否连接到了因特网
platform: 浏览器所在的系统平台
userAgent: 浏览器的用户代理字符串
vendor: 浏览器的品牌
plugins: 浏览器中安装的插件信息的数组
go()方法可以在用户的历史记录中任意跳转,可以向后也可以向前。这个方法接受一个参数,表示向后或向前跳转的页面数的一个整数值。负数表示向后跳转(类似于单击浏览器的“后退”按钮),正数表示向前跳转(类似于单击浏览器的“前进”按钮)。
//后退一页
history.go(-1);
//前进一页
history.go(1);
//前进两页
history.go(2);
也可以给go()方法传递一个字符串参数,此时浏览器会跳转到历史记录中包含该字符串的第一个位置——可能后退,也可能前进,具体要看哪个位置最近。如果历史记录中不包含该字符串,那么这个方法什么也不做,例如:
//跳转到最近的wrox.com 页面
history.go("wrox.com");
可以使用两个简写方法back()和forward()来代替go()。顾名思义,这两个方法可以模仿浏览器的“后退”和“前进”按钮。
//后退一页
history.back();
//前进一页
history.forward();
history 对象还有一个length 属性,保存着历史记录的数量。这个数量包括所有历史记录,即所有向后和向前的记录。对于加载到窗口、标签页或框架中的第一个页面而言,history.length 等于0。通过像下面这样测试该属性的值,可以确定用户是否一开始就打开了你的页面。
if (history.length == 0){
//这应该是用户打开窗口后的第一个页面
}
虽然history 并不常用,但在创建自定义的“后退”和“前进”按钮,以及检测当前页面是不是用户历史记录中的第一个页面时,还是必须使用它。 - 以下是完整的用户代理字符串检测脚本,包括检测呈现引擎、平台、Windows 操作系统、移动设备和游戏系统。
var client = function () {
//呈现引擎
var engine = {
ie: 0,
gecko: 0,
webkit: 0,
khtml: 0,
opera: 0,
//完整的版本号
ver: null
};
//浏览器
var browser = {
//主要浏览器
ie: 0,
firefox: 0,
safari: 0,
konq: 0,
opera: 0,
chrome: 0,
//具体的版本号
ver: null
};
//平台、设备和操作系统
var system = {
win: false,
mac: false,
x11: false,
//移动设备
iphone: false,
ipod: false,
ipad: false,
ios: false,
android: false,
nokiaN: false,
winMobile: false,
//游戏系统
wii: false,
ps: false
};
//检测呈现引擎和浏览器
var ua = navigator.userAgent;
if (window.opera) {
engine.ver = browser.ver = window.opera.version();
engine.opera = browser.opera = parseFloat(engine.ver);
} else if (/AppleWebKit\/(\S+)/.test(ua)) {
engine.ver = RegExp["$1"];
engine.webkit = parseFloat(engine.ver);
//确定是Chrome 还是Safari
if (/Chrome\/(\S+)/.test(ua)) {
browser.ver = RegExp["$1"];
browser.chrome = parseFloat(browser.ver);
} else if (/Version\/(\S+)/.test(ua)) {
browser.ver = RegExp["$1"];
browser.safari = parseFloat(browser.ver);
} else {
//近似地确定版本号
var safariVersion = 1;
if (engine.webkit < 100) {
safariVersion = 1;
} else if (engine.webkit < 312) {
safariVersion = 1.2;
} else if (engine.webkit < 412) {
safariVersion = 1.3;
} else {
safariVersion = 2;
}
browser.safari = browser.ver = safariVersion;
}
} else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)) {
engine.ver = browser.ver = RegExp["$1"];
engine.khtml = browser.konq = parseFloat(engine.ver);
} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) {
engine.ver = RegExp["$1"];
engine.gecko = parseFloat(engine.ver);
//确定是不是Firefox
if (/Firefox\/(\S+)/.test(ua)) {
browser.ver = RegExp["$1"];
browser.firefox = parseFloat(browser.ver);
}
} else if (/MSIE ([^;]+)/.test(ua)) {
engine.ver = browser.ver = RegExp["$1"];
engine.ie = browser.ie = parseFloat(engine.ver);
}
//检测浏览器
browser.ie = engine.ie;
browser.opera = engine.opera;
//检测平台
var p = navigator.platform;
system.win = p.indexOf("Win") == 0;
system.mac = p.indexOf("Mac") == 0;
system.x11 = (p == "X11") || (p.indexOf("Linux") == 0);
//检测Windows 操作系统
if (system.win) {
if (/Win(?:dows )?([^do]{2})\s?(\d+\.\d+)?/.test(ua)) {
if (RegExp["$1"] == "NT") {
switch (RegExp["$2"]) {
case "5.0":
system.win = "2000";
break;
case "5.1":
system.win = "XP";
break;
case "6.0":
system.win = "Vista";
break;
case "6.1":
system.win = "7";
break;
default:
system.win = "NT";
break;
}
} else if (RegExp["$1"] == "9x") {
system.win = "ME";
} else {
system.win = RegExp["$1"];
}
}
}
//移动设备
system.iphone = ua.indexOf("iPhone") > -1;
system.ipod = ua.indexOf("iPod") > -1;
system.ipad = ua.indexOf("iPad") > -1;
system.nokiaN = ua.indexOf("NokiaN") > -1;
//windows mobile
if (system.win == "CE") {
system.winMobile = system.win;
} else if (system.win == "Ph") {
if (/Windows Phone OS (\d+.\d+)/.test(ua)) {;
system.win = "Phone";
system.winMobile = parseFloat(RegExp["$1"]);
}
}
//检测iOS 版本
if (system.mac && ua.indexOf("Mobile") > -1) {
if (/CPU (?:iPhone )?OS (\d+_\d+)/.test(ua)) {
system.ios = parseFloat(RegExp.$1.replace("_", "."));
} else {
system.ios = 2; //不能真正检测出来,所以只能猜测
}
}
//检测Android 版本
if (/Android (\d+\.\d+)/.test(ua)) {
system.android = parseFloat(RegExp.$1);
}
//游戏系统
system.wii = ua.indexOf("Wii") > -1;
system.ps = /playstation/i.test(ua);
//返回这些对象
return {
engine: engine,
browser: browser,
system: system
};
}();
这篇关于ONE PIECE ------ javascript的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!