《JavaScript高级程序设计》第五章,引用类型。
ECMAScript中,引用类型(也称对象定义)是一种数据结构,用于将数据和功能组织在一起。它常被称为类,但并不妥当,事实上虽然ECMAScript是面向对象语言,却不具备传统面向对象语言支持的类和接口等基本结构。ECMAScript提供了很多原生引用类型,如Object等。
Object类型
创建
创建Object实例有两种方式:
- Object构造函数
1 | var person = new Object(); |
- 对象字面量表示法
1 | var person = { |
在使用对象字面量语法时,属性名可以是字符串。下面例子中对象有三个属性:name、age、5,不过此处数值属性名会自动转为字符串。
1 | // 属性名为字符串 |
在使用对象字面量语法时,如果留空其花括号,则可以定义只包含默认属性和方法的对象。
1 | // 花括号为空 |
使用建议
当需要向函数传入大量可选参数时,适合使用字面量语法来传递参数。一般来讲命名参数虽然容易处理,但在有多个可选参数的情况下不灵活。因此建议对必须值使用命名参数,对可选参数使用对象字面量。
1 | function displayInfo(args) { |
访问属性
两种访问属性方法:点表示法、方括号语法。
1 | alert(person.name); |
Array类型
与其他语言不同之处在于ECMAScript的数组每一项数据类型可以不一样,也可以动态调整数组长度。
创建
创建数组有两种方式:
- Array构造函数
1 | // 不传参数 |
- 数组字面量表示法
1 | // 包含数组项的方括号表示 |
若设置值时索引大于数组的length,则在数组会自动增加到该索引值加1的长度。length属性不是只读的,可以通过修改该属性来移除末尾项或添加新项。
1 | var colors = ["red", "blue", "green"]; |
检测数组
我们需要用些方法确定某个对象是否为数组。对于一个网页或一个全局作用域,使用instanceof操作符即可得到结果。
1 | if(value instanceof Array) { |
instanceof操作符存在的问题在于,它假定单一的全局执行环境。当网页包含多个框架,就存在两个以上不同的全局执行环境,从而存在两个以上不同版本的Array构造函数。为此ECMAScript5新增了Array.isArray()
方法,它能最终确定某值是否为数组而不管其所处的全局执行环境。
1 | if(Array.isArray(value)) { |
转换方法
第三章曾说Object的每个实例都有一些属性和方法,包括toLocaleString()
、toString()
、valueOf()
。
数组的
toLocaleString()
返回由每个项的字符串形式拼接,以逗号分隔的字符串。对每个项,其字符串形式为调用每一项的toLocaleString()
得到的值。数组的
toString()
返回由每个项的字符串形式拼接,以逗号分隔的字符串。对每个项,其字符串形式为调用每一项的toString()
得到的值。数组的
valueOf()
返回数组本身。
1 | var colors = ["red", "blue", "green"]; |
不过要注意的是,如果将内容传递给alert()
,由于它接收字符串参数,因此它会在后台调用toString()
方法,因此会得到直接调用toString()
方法相同的结果(对于以上例子来说,若均传入alert()
函数中,则均得到”red,blue,green”)。
可以在对象中自定义上述方法:
1 | var person = { |
可以使用join()
指定构建字符串中使用的分隔符:
1 | var colors = ["red", "blue", "green"]; |
如果数组某项为null或undefined,该值在join()
、toLocaleString()
、toString()
、valueOf()
时返回的都是空字符串。
栈方法
ECMAScript为数组提供了push()
和pop()
方法,以便类似栈行为。push()
接收任意数量参数,将其添加到数组末尾,修改length,返回数组长度;pop()
移除数组末尾项,修改length,返回移除项。
1 | var colors = new Array(); |
队列方法
ECMAScript为数组提供了shift()
方法。shift()
移除数组第一项,修改length,返回移除项。同时使用push()
和shift()
可以模拟队列。
1 | var colors = new Array(); |
除此之外,ECMAScript还为数组提供了unshift()
方法。unshift()
接收任意数量参数,将其添加到数组前端,修改length,返回数组长度。同时使用unshift()
和pop()
可以以相反的方向模拟队列。
1 | var colors = new Array(); |
重排序方法
- reverse
1 | var values = [1, 2, 3, 4, 5]; |
- sort
sort()
调用数组每项的toString()
方法后再比较得到的字符串,在默认情况下升序排列。
1 | var values = [0, 1, 5, 10, 15]; |
- sort加参数
sort()
可以接收一个比较函数作为参数,以指定排序方式。比较函数接收两个参数,若第一个参数应该位于第二个参数之前则返回一个负数,反之亦然,若参数相等则返回0。
1 | function compare1(value1, value2) { |
reverse()
和sort()
方法的返回值均为排序后的数组。
操作方法
- concat
concat()
方法创建当前数组的一个副本,再将接收到的参数添加到这个副本末尾,返回新构建的数组。原数组不变。
1 | var colors = ["red", "green", "blue"]; |
- slice
slice()
接收一个参数时,返回从该参数指定位置到数组末尾的所有项;接收两个参数时,返回两个参数指定位置间不包括结束位置的所有项。原数组不变。
若参数中有负数则将参数均加上数组length,如length为5的数组上调用slice(-2, -1)
与slice(3, 4)
一致。若结束位置小于起始位置,则返回空数组。
1 | var colors = ["red", "green", "blue", "yellow", "purple"]; |
- splice
splice(<起始位置>,<要删除的项数>[,<要插入的项>]),返回从原始数组中删除的项。splice可用于删除、插入、替换,举例如下:
1 | var colors = ["red", "green", "blue"]; |
位置方法
ECMAScript为数组提供了indexOf()
和lastIndexOf()
,接收查找的项和可选的起点位置索引,分别从前往后和从后往前查找,查找成功返回下标,否则返回-1。在比较时使用全等操作符。
1 | var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; |
迭代方法
五个迭代方法均接收两个参数:一个函数、可选的运行该函数的作用域对象。
该函数接收三个参数:该项的值、该项的下标、数组对象。
- every():若所有都满足则返回true
- filter():返回结果数组
- forEach():无返回值,本质与for循环一样
- map():返回结果数组
- some():若存在一项满足则返回true
缩小方法
reduce()
和reduceRight()
,二者分别从前往后和从后往前迭代数组所有项,构建一个最终返回值。方法接收两个参数:一个函数、可选的作为缩小基础的初始值。该函数接收四个参数:前一个值、当前值、项的索引、数组对象。这个函数返回的任何值都会作为第一个参数自动传给下一项。
1 | var values = [1, 2, 3, 4, 5]; |
Date类型
Date类型使用自UTC(Coordinated Universal Time,国际协调时间)1970年1月1日零时至今的毫秒数保存日期。
创建
创建日期对象使用Date()构造函数即可。
不传递参数构造出的对象表示当前的日期和时间。
1 | var now = new Date(); |
若想创建特定日期和时间的对象,必须传入毫秒数。为了方便,ECMAScript提供了两个方法:Date.parse()
和Date.UTC()
。Date.parse()
接收一个表示日期的字符串参数,然后尝试根据该字符串返回相应日期的毫秒数,若该字符串无法得到解析则会返回NaN。该方法对日期格式没有严格定义,但通常接受以下日期格式:
- 月/日/年
6/13/2004 - 英文月名 日,年
January 12,2004 - 英文星期几 英文月名 日 年 时:分:秒 时区
Tue May 25 2004 00:00:00 GMT-0700 - ISO 8601拓展格式 YYYY-MM-DDTHH:mm:ss.sssZ
2004-05-25T00:00:00
1 | var someDate = new Date(Date.parse("May 25,2004")); |
Date.UTC()
有多个参数:年、月(一月为0,二月为1…)、日(1-31)、小时(0-23)、分组、秒、毫秒,只有前两个参数为必须。未提供天数默认为1,未提供其余参数默认为0。
1 | // GMT时间2000.1.1 00:00:00 |
继承的方法
同样,Date类型也重写了toLocaleString()
、toString()
、valueOf()
。前两者返回日期时间等的字符串表示,具体格式因浏览器而异。valueOf()
则不返回字符串,而是返回日期的毫秒表示,也因此可以使用比较操作符来比较日期。
格式化方法
将日期格式化为字符串,同样因浏览器而异。
- toDateString()
- toTimeString()
- toLocaleDateString()
- toLocaleTimeString()
- toUTCString()
日期/时间组件方法
剩下的和Date类型相关的方法,都是直接缺德日期值中特定部分的方法。其中UTC日期指在没有时区偏差的情况下的日期值,即将日期转为GMT时间后。举例:getTime()
、setTime()
、getUTCFullYear()
等。
RegExp类型
创建
- 字面量
按照以下格式创建正则表达式:
1 | var expression = / pattern / flags; |
pattern为任何正则表达式,可包含字符类、限定类、分组、向前查找以及反向引用。flags为一个或多个标明正则表达式行为的值,有以下几个取值:
- g:global,模式将被用于所有字符串,而非发现第一个匹配项就停止
- i:case-insensitive,模式忽略大小写
- m:multiple,模式在到达行尾时还会查找下一行是否存在模式匹配项
1 | var pattern1 = /at/g; // 匹配字符串中所有"at"实例 |
正则表达式中元字符要转义,转义符为\,元字符包括:( [ { \ ^ $ | ) ? * + . ] }
1 | var pattern2 = /\[bc\]at/i; // 匹配第一个"[bc]at",不分大小写 |
- RegExp构造函数
1 | // 二者等价 |
传递给其构造函数的两个参数均为字符串,所以在某些情况下要对字符进行双重转义。
在ECMAScript3中,正则表达式字面量始终会共享一个RegExp实例,而使用构造函数创建的每一个新RegExp实例都是一个新实例。
1 | var re = null, i; |
第一个例子即使在循环体中指定,实际上只为/cat/创建了一个RegExp实例。由于实例属性不会重置,所以循环中再次调用test()
会失败。因为第一次调用test()
找到了”cat”;第二次调用从上次匹配末尾(即索引为3)的字符开始,直到测试到末尾,就找不到它了;第三次又从头开始。第二个例子每次循环均创建一个新的RegExp实例,因此每次调用test()
都会返回true。
ECMAScript5则明确规定,使用正则表达式字面量必须像直接调用RegExp构造函数一样,每次创建新的RegExp实例。
实例属性
RegExp的每个实例都有以下属性:
- global:布尔值,表示是否设置标志g
- ignoreCase:布尔值,表示是否设置标志i
- lastIndex:表示下一个搜索的字符位置
- multiline:布尔值,表示是否设置标志g
- source:正则表达式的字符串表示,按字面量形式的字符串返回
实例方法
- exec
RegExp对象的主要方法是exec()
,该方法接受要应用模式的字符串参数,若能够匹配则返回包含第一个匹配项信息的数组,否则返回null。返回的数组还包含两个额外属性:index用于表示匹配项在字符串中的位置,input用于表示应用正则表达式的字符串。
1 | var text = "mom and dad and baby"; |
exec()
方法每次只会返回一个匹配项。在未设置g标签情况下,在同一字符串上多次调用exec()
会始终返回第一个匹配项信息;在设置g标签情况下,每次调用exec()
都会在字符串中继续查找新项。
- test
test()
接收一个字符串参数,当模式与其匹配时返回true,否则返回false。
1 | var text = "000-00-0000"; |
- 其他
RegExp实例继承的toLocaleString()
和toString()
方法均返回正则表达式的字面量,valueOf()
方法返回正则表达式本身。
构造函数属性
RegExp构造函数包含一些属性(在其他语言中称为静态属性),它适用于作用域中所有实例,且基于所执行的最近一次正则表达式操作而变化。这些属性分别有长属性名和短属性名(Opera不支持短属性名)。以下举例格式为”长属性名:短属性名”:
- input:$_
- leftContext:$`
- rightContext:$’
- lastMatch:$&
- lastParen:$+
- multiline:$*
1 | var text = "this has been a short summer"; |
除了以上属性,还有九个用于存储捕获组的构造函数属性,为RegExp.$1
、RegExp.$1
…RegExp.$9
。在调用exec()
或test()
方法时,这些属性会被自动填充。
1 | var text = "this has been a short summer"; |
模式的局限性
尽管ECMAScript中正则表达式功能还算比较完备,但仍缺少一些高级正则表达式特性。详情:regular expression
Fuction类型
函数实际上是对象,每个函数都是Fuction类型的实例,都具有与其他引用类型一样具有的属性和方法。由于函数是对象,因此函数名实际是一个指向函数对象的指针。
创建
- 函数声明和函数表达式
1 | // 函数声明 |
- Function构造函数
1 | var sum = new Function("num1", "num2", "return num1 + num2"); |
Function构造函数可以接收任意数量的参数,但最后一个参数始终都被看成函数体,前面的参数则为新函数的参数。但不建议如此方式的定义,因为这种语法会导致解析两次代码:第一次解析常规ECMAScript代码,第二次解析传入构造函数中的字符串。
由于函数名为指向函数对象的指针,因此函数名与包含对象指针的其他变量没什么不同,也就是说,一个函数可以有多个名字。
1 | function sum(num1, num2) { |
没有重载
第三章曾说ECMAScript中没有函数重载的概念,当时的解释是”ECMAScript函数没有签名”,现在再看可以发现,原因在于函数名为指针,当创建相同函数名的第二个函数时,实际上是覆盖了引用第一个函数的变量(即第一个函数的函数名)。
函数声明与函数表达式
解析器在向执行环境中加载数据时,对函数声明来说,它会率先读取函数声明,并使其在执行任何代码之前可用;对函数表达式来说,只有到解析器执行到它所在位置它才会被解析执行。
1 | // 可以运行 |
1 | // 不可运行,会导致unexpected identifier错误 |
也可同时使用函数声明和函数表达式,如var sum = function sum(){}
,但在Safari中会导致错误。
作为值的函数
- 将函数作为参数传递
1 | function callFunc(func, arg) { |
- 将函数作为结果返回
1 | function createCompFunc(propertyName) { |
函数的内部属性
函数内部有三个特殊对象:arguments、this、caller。
- arguments
arguments在第三章讲过,它是一个类数组对象,可以用来保存函数参数,它本身也有一个名为callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。可以通过例子来体会它的一个作用:消除递归函数与其函数名的耦合。
1 | function factorial(num) { |
在重写后的factorial()
函数的函数体内,没有再引用函数名factorial。这样,无论函数使用的是什么名字,都可以保证正常完成递归调用。如果不使用arguments.callee(num - 1)
而是使用factorial(num - 1)
,则均会返回0。
- this
this引用的是函数据以执行的环境对象。
1 | window.color = "red"; |
sayColor()
是在全局环境下定义的,它引用了this对象,但在调用前this的值是不确定的,因此this可能会在代码执行过程中引用不同的对象。全局作用域调用时,this引用全局对象window,this.color为window.color;对象o调用时,this引用对象o,this.color为o.color。
有一点要牢记:函数的名字仅为一个包含指针的变量。因此此处即使在不同的环境中执行,全局的sayColor()
函数和o.sayColor()
指向的仍然时同一个函数。
- caller
caller属性保存着调用当前函数的函数的引用,如果在全局作用域中调用当前函数,它的值为null。
1 | function outer() { |
以上代码会导致警告框显示outer()
函数的源代码。outer()
调用了inner()
,因此inner.caller
就指向outer()
。
* 易混淆
ECMAScript5还定义了arguments.caller属性,该属性始终为undefined。定义该属性是为了区分arguments.caller和函数的caller属性。
* 严格模式的限制
严格模式下不能访问arguments.callee、arguments.caller,不能为函数的caller属性赋值。
函数属性和方法
属性
函数是对象,因此函数也有属性和方法。每个函数都包含两个属性:length、prototype。
- length
length属性表示函数希望接收的命名参数个数。
1 | function func0() {...} |
- prototype
对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。也就是说,诸如toString()
和valueOf()
等方法实际上都保存在prototype名下,只不过是通过各自对象的实例访问罢了。在创建自定义引用类型以及实现继承时,prototype属性的作用是极为重要的。在ECMAScript5中,prototype属性是不可枚举的,因此使用for-in无法实现。
方法
每个函数都包含两个非继承而来的方法:apply()
、call()
。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。除此外ECMAScript5还定义了方法bind()
。
- apply
apply()
方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。第二个参数可以是Array实例也可以是arguments对象。
1 | function sum(num1, num2) { |
callSum()
在执行sum()
函数时传入了this作为this值(因为是在全局作用域中调用的,所以传入的this时window对象)和参数数组。
* 在严格模式下,未指定环境对象而调用函数,则this值不会转型为window。除非明确把函数添加到某个对象或者调用apply()
或call()
,否则this值将是undefined。
- call
call()
方法与apply()
方法作用相同,区别在于:将函数的参数传递给apply()
时,传递的是参数数组;将函数的参数传递给call()
时,传递的是逐个列举的值。
1 | function sum(num1, num2) { |
* apply和call的另一用途
事实上传递参数并非apply()
和call()
真正的用武之地,它们的强大之处在于能够扩充函数赖以运行的作用域。
1 | window.color = "red"; |
第一句中,全局环境调用sayColor()
,this为window,windows.color为red,故为red;第二三句中,显式地在全局作用域中调用函数,故也为red;第四句中,此时函数体内的this对象指向了o,o.color为blue,故为blue。
* 用apply和call扩充作用域的好处
好处在于对象不需要与方法有任何耦合关系。将上述例子与”函数的内部属性”中的this例子进行比较,之前我们先将sayColor()
函数放入对象o中,再通过o来调用它;重写的例子中则无需如此麻烦。
- bind
bind()
方法创建一个函数的实例,其this值会被绑定到传给bind()
函数的值。
1 | window.color = "red"; |
此处sayColor()
调用bind()
并传入对象o,创建了objectSayHello()
函数,该含函数的this值等于o。
- 其他
每个函数的toLocaleString()
和toString()
方法始终返回函数的代码,格式因浏览器而异。有的返回的代码与源代码中的函数代码一致,有的返回函数代码的内部表示(即由解析器删除了注释并对某些代码作了改动)。valueOf()
方法同样也只返回函数代码。
基本包装类型
为了便于操作基本类型值,ECMAScript提供了3中特殊的引用类型:Boolean、Number、String,称为基本包装类型。它们与其他引用类型相似,但同时也具有各自的基本类型相应的特殊行为。
怎么发现是基本包装类型的?
1 | var s1 = "some text"; |
发现盲点:变量s1是基本类型值中的字符串,基本类型值不是对象,从逻辑上讲它们不应该有方法,但是这里它却有方法substring()
。其实,每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。如在以上的例子中,就进行了如下操作:
- 创建String类型的一个实例
- 在实例上调用指定的方法
- 销毁这个实例
相当于:
1 | var s1 = new String("some text"); |
以上三个步骤同样适用于Boolean和Number类型对应的布尔值和数字值。
基本包装类型与引用类型的区别
主要区别在于对象的生存期。使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。自动创建的基本包装类型的对象,只存在于代码执行的瞬间,然后立即被销毁。这意味着我们不能在运行时为基本类型值添加属性和方法。
1 | var s1 = ="some text"; |
第二行创建的String对象被添加了color属性,但是它在执行第三行代码时就被销毁了,第三行代码又创建了自己的String对象,而该对象没有color属性。
typeof和instanceof
对基本包装类型调用typeof返回”object”,且所有基本包装类型的对象都会被转换为布尔值true。Object构造函数会像工厂方法一样,根据传入值的类型返回相应的基本包装类型的实例。
1 | var obj = new Object("some text"); |
把字符串传给Object构造函数会返回String实例;传入数值会返回Number实例;传入布尔值会返回Boolean实例。
基本包装类型的构造函数和转型函数
要注意区分。
1 | var value = "25"; |
Boolean类型
可使用构造函数传入true或false来创建Boolean对象。Boolean类型的实例重写了valueOf()
方法,返回true或false;重写了toString()
方法,返回”true”或”false”。基本类型和基本包装类型容易混淆,包括在真值上的混淆和使用类型判别符的混淆。
1 | var falseObject = new Boolean(false); |
由于经常会造成人们的误解,因此不建议使用Boolean对象。
Number类型
可使用构造函数传入数值来创建Number对象。Number类型重写了valueOf()
方法,返回数值;重写了toString()
和toLocaleString()
方法,返回字符串。其中第三章中写过,可以为toString()
方法传递一个表示基数的参数,来返回指定进制的该数值字符串形式。
除了继承的方法外,Number类型还提供了一些用于将数值格式化为字符串的方法。
- toFixed
toFixed()
方法按照指定的小数位返回数值的字符串表示,参数为指定输出结果中的小数位数。toFixed()
可表示带有0到20个小数位的数值。
1 | var num = 10; |
- toExponential
toExponential()
方法返回以指数表示法表示的数值的字符串形式,参数为指定输出结果中的小数位数。
1 | var num = 10; |
- toPrecision
对一个数值来说,toPrecision()
方法可能会返回固定大小(fixed)格式,也可能返回指数(exponential)格式,具体规则是看哪种格式最合适。此方法接收的参数为表示数值的所有数字的位数(不包括指数部分)。toPrecision()
可表示1到21位小数。
1 | var num = 99; |
和Boolean对象类似,Number对象也以后台方式为数值提供了重要的功能。但同样存在易于造成误解的问题,因此也不建议直接实例化Number类型。
1 | var numberObject = new Number(10); |
String类型
可使用构造函数传入数值来创建String对象。String类型继承的valueOf()
、toString()
和toLocaleString()
方法,都返回字符串。每个String类型的实例都有length属性,表示字符串中的字符个数。String类型提供了许多方法,用于辅助完成对ECMAScript中字符串的解析。
字符方法
charAt()
和charCodeAt()
用于访问字符串中特定字符,二者接收一个表示下标的参数,分别返回字符串该位置的字符和字符编码。ECMAScript5还提供了方括号加数字索引访问字符串的方法。
1 | var stringValue = "hello world"; |
字符串操作方法
- concat
concat()
用于将一个或多个字符串拼接起来,返回拼接得到的新字符串,不影响原字符串。
1 | var stringValue = "hello "; |
- slice
slice()
接收一个或两个参数,第一个参数指定子字符串起始位置,第二个参数指定子字符串最后一个字符后面的位置。若参数中有负数则将负数参数加上字符串length。
1 | var stringValue = "hello world"; |
- substring
substring()
接收一个或两个参数,第一个参数指定子字符串起始位置,第二个参数指定子字符串最后一个字符后面的位置。若参数中有负数则将负数参数转换为0。
1 | var stringValue = "hello world"; |
- substr
substr()
接收一个或两个参数,第一个参数指定子字符串起始位置,第二个参数指定子字符串的字符个数。若第一个参数为负数,则将其加上字符串length;若第二个参数为负数,则将其转换为0。
1 | var stringValue = "hello world"; |
slice()
、substring()
、substr()
三个方法均返回子字符串,不影响原字符串。若没有传递第二个参数,则将字符串的长度作为结束位置。
字符串位置方法
indexOf()
和lastIndexOf()
用于在字符串中查找子字符串,若找到则返回子字符串的位置,否则返回-1。二者分别从前往后和从后往前进行查找。方法可接受一个或两个参数,第一个参数指定查找的子字符串,第二个参数指定开始搜索的位置。
1 | var stringValue = "hello world"; |
字符串修剪方法
trim()
方法创建字符串的副本,删除前置和后缀所有空格,返回结果。还有trimLeft()
和trimRight()
方法用于删除字符串开头或末尾空格。
字符串大小写转换方法
共有四个方法:toLowerCase()
、toUpperCase()
、toLocaleLowerCase()
、toLocaleUpperCase()
。locale的方法是针对特定地区的实现,有些地区中locale方法和通用方法得到的结果相同,而有些语言(如土耳其语)会为Unicode大小写转换应用特殊的规则,这时就必须使用locale方法来保证实现正确的转换。
字符串的模式匹配方法
- match
在字符串上调用match()
本质与调用RegExp的exec()
方法相同。该方法只接收一个参数,参数为正则表达式或RegExp对象。
1 | var text = "cat, bat, sat, fat"; |
- search
search()
方法只接收一个参数,参数为正则表达式或RegExp对象,若查找成功返回第一个匹配项的索引,否则返回-1。search()
方法始终从字符串开头查找。
1 | var text = "cat, bat, sat, fat"; |
- replace
为了简化子字符串替换的操作,ECMAScript提供了replace()
方法。该方法接收两个参数,第一个参数为正则表达式或字符串(这个字符串不会被转换为正则表达式),第二个参数为字符串或者函数。
若第一个参数为字符串,就会只替换第一个子字符串;若想替换所有子字符串,就必须提供一个带g标签的正则表达式。
1 | var text = "cat, bat, sat, fat"; |
若第二个参数是字符串,还可以使用一些特殊的字符序列,将正则表达式操作得到的值插入到结果字符中。
字符序列:
- $$:$
- $&:匹配整个模式的子串
- $’:匹配的子串之前的字符串
- $`:匹配的子串之后的字符串
- $n:匹配第n个捕获组的子串,n为1-9
- $nn:匹配第nn个捕获组的子串,nn为01-99
1 | var text = "cat, bat, sat, fat"; |
若第二个参数是函数,该函数的参数为:模式的匹配项(可能有多个)、模式匹配项在字符串中的位置、原始字符串。该函数的返回值为一个字符串,表示在函数内部一系列处理之后返回的字符串。
1 | function htmlEscape(text) { |
这里的函数htmlEscape()
能够转义四个字符,对这四个字符的匹配使用了正则表达式。
- split
split()
方法基于指定的分隔符将一个字符串分割成多个字符串,并将结果放在一个数组中返回。分隔符可以是RegExp对象或字符串(这个字符串不会被转换为正则表达式)。split()
方法可以接受可选的第二个参数,用于指定数组的大小。
1 | var colorText = "red, blue, green, yellow"; |
对split()
中正则表达式的支持因浏览器而异。
字符串比较方法
localeCompare()
方法比较两个字符串,若在字母表中字符串应排在参数前面,则返回一个负数;若等于返回0;若应排后面返回一个正数。返回的正数或负数取决于实现,大多数情况下为1和-1。
1 | var stringValue = "yellow"; |
字符编码转换函数
String构造函数本身还有一个静态方法fromCharCode()
,该方法接收一个或多个字符编码,再将它们转换为一个字符串。本质上与charCodeAt()
执行的是相反的操作。
1 | alert(String.fromCharCode(104, 101, 108, 108, 111)); // "hello" |
HTML方法
这些方法是为了使用JavaScript动态格式化HTML,不过不建议使用,因为它们创建的标记通常无法表达语义。
- anchor(name):输出结果为
<a name="name">string</a>
- big():输出结果为
<big>string</big>
- …
单体内置对象
内置对象:由ECMAScript实现提供的、不依赖于宿主环境的对象,这些对象在ECMAScript程序执行之前就已经存在了。也就是说开发者不必显示地实例化对象,因为它们已经实例化了。
Global对象
Global对象在某种意义上是作为一个”兜底对象”定义的,即不属于任何其他对象的属性和方法,最终都是它的属性和方法。事实上没有全局变量或全局函数;所有在全局作用域中定义的属性和函数,都是Global对象的属性。如之前提到的isNaN()
、isFinite()
、parseInt()
和parseFloat()
,实际上都是Global对象的方法。除此之外,Global对象还包含一些其他方法。
URI编码方法
encodeURI()
和encodeURIComponent()
方法可以对URI(Uniform Resource Identifiers,通用资源标识符)进行编码,以便发送给浏览器。因为有效的URI中不能包含某些字符(如空格),编码方法的存在就可以对URI进行编码,用特殊的UTF-8编码替换无效字符,从而使浏览器接受和理解。
1 | var uri = "https://www.wrox.com/illeegal value.htm#start"; |
encodeURI()
只替换空格,encodeURIComponent()
替换所有非字母数字字符。因此一般对整个URI用前者,对URI的某一段(如例子URI中的illeegal value.htm)用后者。由于更常见的是对查询字符串参数而不是基础URI进行编码,因此后者用得更多。
相应的解码方法为decodeURI()
、decodeURIComponent()
,前者只对encodeURI()
替换的字符进行解码,后者只对decodeURIComponent()
替换的字符进行解码。
1 | var uri = "https%3A%2F%2Fwww.wrox.com%2Filleegal%20value.htm%23start"; |
eval方法
eval()
方法像一个ECMAScript解析器,接受一个参数:待执行的ECMAScript字符串。在调用eval()
方法时,传入的参数会被当做实际的ECMAScript语句来解析,然后把执行结果插入到原位置。
1 | eval("alert('Hello')"); |
通过eval()
执行的代码被认为是包含该次调用的执行环境的一部分,因为eval行代码最终会被替换为其内部的真正要执行的代码。因此可以进行如下的使用:
1 | var msg = "Hello"; |
严格模式下,在外部访问不到eval()
中创建的任何变量或函数(也就是后两个例子会出错),也不能为eval赋值。
能够解释代码字符串的能力非常强大,但也非常危险。因此在使用eval()
时要谨慎,防止恶意用户通过它来威胁站点或应用程序的安全(即代码注入)。
Global对象的属性
undefined、NaN、Infinity、Object、Array、Function、Boolean、String、Number、Date、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError,这些是Global对象的所有属性。前三个是特殊值,后面的均为原生引用类型的构造函数。
window对象
ECMAScript没有指出如何直接访问Global对象,但Web浏览器都是将全局对象作为window对象的一部分加以实现的。因此在全局作用域中声明的所有变量和函数都成了window对象的属性。
可通过以下代码取得Global对象:
1 | var global = function() { |
在没有给函数明确指定this值的情况下,this值等于Global对象。
Math对象
Math对象的属性
为计算会用到的特殊值:Math.E
、Math.LN10
、Math.PI
…
最值方法
max()
和min()
,可接收任意多的数值参数。
1 | var max = Math.max(1, 2, 3, 4, 5, 6, 7, 8); |
若想找到数组中的最大值可以使用如下技巧:
1 | var values = [1, 2, 3, 4, 5, 6, 7, 8]; |
技巧在于把Math对象作为apply()
的第一个参数,从而正确地设置this值。
舍入方法
ceil()
、floor()
和round()
,分别向上舍入、向下舍入和四舍五入。
random方法
Math.random()
方法返回介于0和1之间的随机数,不包括0和1。
若要从某个整数范围内随机选择一个值,可以使用如下公式:
1 | 值 = Math.floor(Math.random() * 可能值的总数 + 第一个可能的值); |
其他方法
即完成各种运算的方法:Math.abs(num)
、Math.exp(num)
…