# 引用类型
ECMAScript 提供了很多原生引用类型(例如Object),以便于开发人员用以实现常见的计算任务。
# 5.1 Object 类型
- new Object()
- var a = {}
# 5.2 Array 类型
var arr = [1, 2, 3]
arr.length = 2 // length 不是只读的,会将3移除
2
# 5.2.1 检测数组
// 1. instanceof
value instanceof Array
// 2 Array.isArray(value)
2
3
4
# 5.2.2 转换方法
- toString()
- toLocaleString()
# 5.2.3 栈方法
// push()
// pop()
2
# 5.2.4 队列方法
// shift() // 删除第一项
// unshift // 向第一项添加
2
# 5.2.5 重排序方法
// reverse() 反转
// sort() 默认从小到大,也可以接受一个 function 作为参数,这个函数接收两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等则返回0,如果第一个参数应该位于第二个之后则返回一个正数。
function compare(value1, value2) {
if (value1 < value2) {
return -1
} else if (value1 > value2) {
return 1
} else {
return 0
}
}
var values = [0, 1, 5, 10, 15]
values.sort(compare) // [0, 1, 5, 10, 15]
// 可以通过修改比较函数产生降序结果,只需要交换比较函数返回值
function compare(value1, value2) {
if (value1 < value2) {
return 1
} else if (value1 > value2) {
return -1
} else {
return 0
}
}
// 简写
function compare(value1, value2) {
return value2 - value1
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 5.2.6 操作方法
- concat
- slice
- splice
# 5.2.7 位置方法
- indexOf
- lastIndexOf
# 5.2.8 迭代方法
- every
- filter
- forEach
- map
- some
# 5.2.9 缩小方法
- reduce
- reduceRight
# 5.3 Date 类型
var start = Date.now()
dosomething()
var end = Date.now()
var res = end -start
// 在不支持 Date.now() 的浏览器可以通过 + 号实现
var start = +new Date()
2
3
4
5
6
7
# 5.3.1 继承的方法
- toString()
- toLocaleString()
- valueOf() 返回毫秒数
# 5.3.2 日期格式化方法
- toDateString()
- toTimeString()
- toLocaleDateString()
- toLocaleTimeString()
- toUTCString()
# 5.3.3 日期/时间组件方法
- getTime
- setTime
- getFullYear
- getUTCFullYear
- setFullYear
- setUTCFullYear
- getMonth
- getUTCMonth
- setMonth
- setUTCMonth
- getDate
- getUTCDate
- setDate
- setUTCDate
- getDay
- getUTCDay
- getHours
- getUTCHours
- setHours
- setUTCHours
...
# 5.4 RegExp 类型
var expression = /pattern/flags
模式中使用的所有元字符都必须转义。元字符包括:( [ { \ ^ $ | } ? * + . ] )
# 5.4.1 RegExp 实例属性
- global
- ignoreCase
- lastIndex: 整数,表示开始搜索下一个匹配项的字符位置,从0算起
- multiline
- source
var reg = /a/g
alert(reg.global)
2
3
# RegExp 实例方法
exec() 方法
var text = 'mom and dad and baby'
var reg = /mom( and dad( and baby)?)?/gi
var matches = reg.exec(text)
console.log(mactches.index) // 0
console.log(mactches.input) // 'mom and dad and baby'
console.log(mactches[0]) // 'mom and dad and baby'
console.log(mactches[1]) // ' and dad and baby'
console.log(mactches[2]) // ' and baby'
2
3
4
5
6
7
8
9
对于 exec() 方法而言,即使在模式中设置了全局标志 (g),它每次也只会返回一个匹配项。在不设置全局标志的情况下,在同一个字符串上多次调用 exec() 将始终返回第一个匹配项的信息。而在设置全局标志的情况下,每次调用 exec() 则都会在字符串中继续查找新匹配项,如下面的例子:
var text = 'cat, bat, sat, fat'
var reg = /.at/
var matches = reg.exex(text)
console.log(matches.index) // 0
console.log(matches[0]) // cat
console.log(matches.lastIndex) // 0
matches = reg.exex(text)
console.log(matches.index) // 0
console.log(matches[0]) // cat
console.log(matches.lastIndex) // 0
var reg = /.at/g
var matches = reg.exex(text)
console.log(matches.index) // 0
console.log(matches[0]) // cat
console.log(matches.lastIndex) // 0
matches = reg.exex(text)
console.log(matches.index) // 5
console.log(matches[0]) // bat
console.log(matches.lastIndex) // 8
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
在全局匹配模式下, lastIndex 的值在每次调用 exec() 后都会增加,而在非全局模式下则始终保持不变。
test()方法
# 5.4.3 RegExp 构造函数属性
- input
- lastMatch
- lastParen
- leftContext
- multiline
- rightContext
- $1,$2...$9
# 5.4.4 模式的局限性
# 5.5 Function 类型
# 5.5.1 没有重载
# 5.5.2 函数声明与函数表达式
解析器会率先读取函数声明,并使其在执行任何代码之前可用。函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行。
# 5.5.3 作为值的函数
function createComparisonFunction (propertyName) {
return function (object1, object2) {
var value1 = object1[propertyName]
var value2 = object2[propertyName]
if (value1 < value2) {
return -1
} else if (value1 > value2) {
return 1
} else {
return 0
}
}
}
// 使用
var data = [{name: 'Zac', age: 24}, {name: 'Na', age: 28}]
data.sort(createComparisonFunction('name'))
console.log(data[0].name) // Na
data.sort(createComparisonFunction('age'))
console.log(data[0].name) // Zac
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 5.5.4 函数内部属性
- arguments
- this
- arguments.callee
// 递归实现阶乘
function factorial (num) {
if (num <= 1) {
return 1
} else {
return num * factorial(num-1)
}
}
// 使用 arguments.callee
function factorial (num) {
if (num <= 1) {
return 1
} else {
return num * arguments.callee(num-1)
}
}
// 在这个重写的 factorial 函数的函数体内,没有在引用函数名 factorial。这样,无论引用函数时使用的是什么名字,都能保证正常完成递归。例如:
var trueFactorial = factorial
factorial = function () {
return 0
}
console.log(trueFactorial(5)) // 120
console.log(factorial(5))
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 函数属性和方法
属性:
- length: 表示函数希望接收到的命名参数的个数
- prototype: 所有实例方法的真正所在
方法:
- apply()
- call()
- bind(): 会创建一个函数实例
- toString(): 返回函数的代码
- toLocaleString(): 返回函数的代码
window.color = 'red'
var o = {red: 'blue'}
function sayColor () {
console.log(this.color)
}
sayColor.call(this) // red
sayColor.call(window) // red
sayColor.call(this) // blue
var objectSayColor = sayCOlor.bind(o)
objectSayColor() // blue
2
3
4
5
6
7
8
9
10
11
12
13
# 5.6 基本包装类型
为了便于操作基本数据类型,ECMAScript 还提供了3个特殊的引用类型: Booolean, Number 和 String。
var s = 'text'
s1 = s.substring(2)
2
第二步中后台会自动完成下列处理
- 创建 String 类型的一个实例
- 在实例上调用指定的方法
- 销毁这个实例
// 等同于
var s = new String('text')
var s1 = s.substring(2)
s = null
2
3
4
经过此番处理,基本的字符串值就变得跟对象一样了。而且,上面这三个步骤也分别适用于 Boolean 和 Number 类型对应的布尔值和数字值。
考点啊:引用类型与基本包装类型的主要区别就是对象的生存期。使用 new 操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即被销毁。这意味着不能在运行时为基本类型值添加属性和方法。如下:
var s1 = 'text'
s1.color = 'red'
alert(s1.color) // undefined
2
3
对基本包装类型的实例调用 typeof
会返回 object
,而且所有的基本包装类型的对象都会被转换为布尔值 true。如下:
var a = new String('')
typeof a // object
if (a) {
console.log('i am true') // 会执行
}
2
3
4
5
Object 构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。例如:
var obj = new Object('text')
alert(obj instanceof String) // true
2
3
注意,使用 new 调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的。例如:
var value = '25'
var number = Number(value) // 转型函数
typeof number // number
var obj = new Number(value)
typeof obj // object
2
3
4
5
6
不建议显式地创建基本包装类型的对象。
# 5.6.1 Boolean 类型
Boolean 对象在 ECMAScript 中的用处不大。
理解基本类型的布尔值与 Boolean 对象之间的区别非常重要,建议是永远不要使用 Boolean 对象
# 5.6.2 Number 类型
toPrecision() 方法可能返回固定大小(fixed)格式,也可能返回指数(exponential)格式,具体规则是看哪种格式最合适。接收一个参数,即表示数值的所有数字的位数(不包括指数部分)
var num = 99
console.log(num.toPrecision(1)) // 1e+2
console.log(num.toPrecision(2)) // 99
console.log(num.toPrecision(3)) // 99.0
2
3
4
# 5.6.3 String 类型
字符串操作方法
- slice: 第一个参数指定子字符串的开始位置,第二个参数指定的是子字符串最后一个字符后面的位置
- substr: 第一个参数指定子字符串的开始位置,第二个参数指定的是返回的字符个数。
- substring: 第一个参数指定子字符串的开始位置,第二个参数指定的是子字符串最后一个字符后面的位置
var str = 'hello world'
str.slice(3) // 'lo world'
str.substring(3) // 'lo world'
str.substr(3) // 'lo world'
str.slice(3, 7) // 'lo w'
str.substring(3, 7) // 'lo w'
str.substr(3, 7) // 'lo worl'
2
3
4
5
6
7
8
当这三个方法的参数是负数的时候:
- slice: 会将传入的负值与字符串的长度相加
- substr: 将负的第一个参数加上字符串的长度,而将负的第二个参数转换为0
- substring: 会把所有负值参数都转换为0
var str = 'hello world'
str.slice(-3) // 'rld'
str.substring(-3) // 'hello world'
str.substr(-3) // 'rld'
str.slice(3, -4) // 'lo w' 相当于 (3, 7)
str.substring(3, -4) // 'hel' 相当于 (3, 0) -> (0, 3)
str.substr(3, -4) // '' 相当于 (3, 0)
2
3
4
5
6
7
8
字符串位置方法
- indexOf
- lastIndexOf
通过循环调用 indexof 或者 lastIndexof 来找到所有匹配的子字符串:
var str = 'Lorem ipsum dolor, sit amet consectetur adipisicing elit. Voluptates ex perferendis id expedita repellat in ad aliquid, libero a nulla provident cumque recusandae quas quia aperiam, modi illo vitae molestias!'
var positions = []
var pos = str.indexOf('e')
while (pos > -1) {
positions.push(pos)
pos = str.indexOf('e', pos + 1) // 跳过多少位
}
console.log(positions)
2
3
4
5
6
7
8
9
10
11
字符串的模式匹配方法
- match
- search
- split
- replace
replace 的第二个参数如果是字符串,可以使用一些特殊的字符序列,将正则表达式操作得到的值插入到结果字符串中:
字符序列 | 替换文本 |
---|---|
$$ | $ |
$& | 插入匹配的子串 |
$` | 插入当前匹配的子串左边的内容 |
$' | 插入当前匹配的子串右边的内容 |
$n | 假如第一个参数是 RegExp对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始 |
# 5.7 单体内置对象
ECMA-262 对内置对象的定义是:“由ECMAScript实现提供的、不依赖于宿主环境的对象,这些对象在ECMAScript程序执行之前就已经存在了”。意思就是说,开发人员不必显示地实例化内置对象,因为它们已经实例化了。例如 Object、Array 和 String。另外还有 Global 和 Math
# 5.7.1 Global 对象
Global(全局)对象可以说是 ECMAScript 中最特别的一个对象,因为不管从什么角度去看,这个对象都是不存在的。ECMAScript 中的 Global 对象在某种意义上是作为一个终极的“兜底对象”来定义的。换句话说,不属于任何其他对象的属性和方法,最终都是它的属性和方法。事实上,没有全局变量或全剧函数,所有在全局作用域中定义的属性和函数,都是 Global 对象的属性。例如 isNaN()、isFinite()、parseInt()等,除此之外,Global对象还包含其他一些方法。
1. URI编码方法
- encodeURI(): 主要用于整个URI,不会对本身属于URI的特殊字符进行编码,例如 冒号(😃、正斜杆(/)、问号(?)和井字号(#)
- encodeURIComponent(): 主要对URI中的某一段,会对它发现的任何非标准字符进行编码
var url = 'https://www.baidu.com/path?q=1&s=8?哈 呵'
encodeURI(url)
// "https://www.baidu.com/path?q=1&s=8?%E5%93%88%20%E5%91%B5"
encodeURIComponent(url)
// "https%3A%2F%2Fwww.baidu.com%2Fpath%3Fq%3D1%26s%3D8%3F%E5%93%88%20%E5%91%B5"
2
3
4
5
6
7
- decodeURI(): 只能对使用 encodeURI() 替换的字符进行解码。如 %20 替换为空格,但是对 %23 就不作处理,因为 %23 代表 #,而 encodeURI 不会替换 #
- decodeURIComponent(): 能够解码使用 encodeURIComponent() 编码的所有字符
2. eval()方法
ECMAScript 语言中最强大的一个方法。eval方法就像是一个完整的 ECMAScript 解释器,接受一个参数,即要执行的 ECMAScript(或JavaScript) 字符串。
eval() 执行的代码被认为是包含该次调用的执行环境的一部分,因此被执行的代码具有与该执行环境相同的作用域链。这意味着通过 eval() 执行的代码可以引用在包含环境中定义的变量:
var msg = 'hello'
eval('alert(msg)') // 'hello'
function test () {
var x = 'world'
eval('alert(x)') // 'world'
}
2
3
4
5
6
7
在 eval() 中调用定义一个函数或者变量,在外部中可以引用:
eval('function sayHi () {alert("Hi");}')
sayHi() // 'Hi'
eval('var a = 100;')
alert(a) // 100
2
3
4
5
注意,在eval中创建的任何变量和函数都不会被提升,因为在解析代码的时候,它们被包含在一个字符串中,只在 eval 执行的时候创建
严格模式下,外部访问不到 eval 中创建的任何变量或函数,即上边两个例子将导致错误。同时在严格模式下,为 eval 赋值将导致错误
"use strict";
eval = 'hi' // Uncaught SyntaxError: Unexpected eval or arguments in strict mode
2
3. Global对象的属性
- undefined
- NaN
- Infinity
- Object
- Array
- Function
- Boolean
- String
- Number
- Date
- RegExp
- Error
- EvalError
- RangeError
- ReferenceError
- SyntaxError
- TyprError
- URIError
ES5明确禁止给 undefined、NaN 和 Infinity 赋值,即使是非严格模式下也会报错
4. window对象
ECMAScript 虽然没有指出如何直接访问 Global 对象,但 web 浏览器都是将这个全局对象作为 window 对象的一部分加以实现的。因此,在全局作用域中声明的所有变量和函数,就都成为了 window 对象的属性。