重学Javascript之引用类型

QQ图片20190827232014.jpg

注意: 本文章为 《重学js之JavaScript高级程序设计》系列第五章【JavaScript引用类型】。

关于《重学js之JavaScript高级程序设计》是重新回顾js基础的学习。

引用类型的值(对象)是引用类型的一个实例。在ES中,引用类型是一种数据结构,用于将数据和功能组织在一起,也被称之为类。但这并不妥当,引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法。

对象是某个特定引用类型的实例。新对象是使用 new 操作符跟一个 构造函数来创建的。构造函数本身就是一个函数,只不过该函数是出于创建新对象的目的而定义的,如下:

var person = new Object();

上面这行代码创建了 Object 引用类型的一个新实例,然后把该实例保存在变量person中。使用的构造函数是 Object ,它只为新对象定义了默认的属性和方法。

1、object类型

创建object实例的方式有两种。第一种使用new操作符后跟object函数,第二种使用对象字面量表示法,对象字面量是对象定义的一种简写形式。

// 第一种方法

var person = new Object()
person.name = 'nice'
person.age = 20

// 第二种方法

var p = {
    name : 'nice',
    age: 29
}

我们可以通过两种方式来访问对象的属性

p.name = nice
p['name'] = nice

2、Array类型

在ES 中数组的每一项可以保存任何类型的数据,也就是说,可以用数组的第一个位置来保存字符串,第二个位置来保存数值,第三个位置保存对象。另外数组的大小是可以动态调整的,可以随着数据的添加自动增长以容纳新的数据。

创建数组的两种方式

  1. 使用Array构造函数
1
2
3
var c = new Array()     // 初始值是  undefined
var b = new Array(20) // 创建一个包含20个元素的数组
var a = new Array('a', 'b') // 包含 a b 两个元素的数组
  1. 使用数组字面量表示法
1
2
3
var a = [1,2,3]  // 包含3个数字的数组
var n = [] // 一个空数组
var v = ['a', 'c'] // 两个字符串的数组
  1. 读取数组的值
1
2
3
a[0]    // 1  方括号中的值就是数组的索引。返回数组对应的值
a[4] // 如果值超过了数组的长度,则会增加到该索引值加1的长度
n[2] // [undefined, undefined]

注意: 数组的length不是制度的,可以通过设置这个属性来往数组中增加删除项。数组的最后一项的索引 永远是 length -1,因此下一项的位置就是length。另外数组最多可以包含 4294967295个项,

2.1 转换方法

在之前的描述中,所有的对象都具有 toLocaleString()、toString() 和 value() 方法,其中调用数组的 toString()和 valueOf() 方法会返回相同的值,即由数组中每个值的字符串形式拼接而成以逗号分隔的字符串。如下:

var a = ['a', 'b', 'c'];
a.toString()    // a,b,c
a.valueOf()     // a,b,c

数组继承的 toLocaleString()、toString()、valueOf 方法 在默认情况下都会以逗号分隔的字符串形式返回数组项。如果使用 ‘join’ 方法,则可以使用指定的分隔符来构建这个字符串。它只接受一个参数,用作分隔符的字符串,返回包含所有数组项的字符串。如下

var c = ['a', 'b', 'c']
c.join(',')     // a,b,c
c.join('/')     // a/b/c

注意: 数组中的某一项的值是null 或者 undefined,那么该值在join()、toLocaleString()、toString()、valueOf()方法返回的字符串中以空表示

2.2 栈方法

在 ES 中 为数组也提供了一个让数组的行为类似于其他数据结构的方法,就是说数组可以表现的像栈一样呢,可以限制插入和删除的数据结构。 栈的特性就是 后进先出 ,最新添加的项最早被删除。栈中项的插入 和 移除只发生在栈的顶部。ES 为数组提供了 pop() 和 push() 方法

push() 是插入项,每次添加在数组的末尾,可以插入任意参数

let a = [1,2]
a.push(3)   // [1,2,3]

pop() 删除数组的最后一项

a.pop() //  [1,2]

2.3 队列方法

栈数据结构是遵循 先进后出 ,而队列数据结构的访问规则 是 先进先出。队列的末端添加项,从列表的前端移除项。由于 push() 是想数组末端添加项的方法,如果要获取数组的首端项, 则 需要 shift() 方法。同时还有 unshift() 方法,在数组的最前端添加。

let a = [1,2]
a.shift()   //  1   a => [2]

a.unshift(3)    // a => [3,2]

2.4 重排序方法

数组中可以用 reverse() 和 sort() 对数组进行排序,其中 reverse() 是对数组反转。sort() 默认是对数组升序排列,但是也可以由条件进行控制。

let a = [1,2,3,4]
a.reverse() // a => [4,3,2,1]

let a = [1,3,5,2,7]
a.sort()    // a => [1,2,3,5,7]

2.5 操作方法

ES 为数组提供了很多方法, concat() 是基于当前数组创建一个新数组。

规则:

  1. 默认创建当前数组的副本,然后将接受到的参数添加到这个副本的末尾,返回新建的数组
  2. 没有传递参数,就复制当前的数组并返回
  3. 传递了一个或多个数组,则将这些数组的每一项添加到结果数组中。如果传递的值不是数组,则会将值添加到数组末尾

slice() 基于当前数组中的一个或多个项创建新数组。接受 一到两个参数,即返回数组的开始和结束位置。如果只有一个参数,则返回的是起始位置到数组结束位置的所有值。它不会影响原数组。另外如果是两个参数,它返回的是第一个值到最后一个值前一个之间的值。

let a = [1,2,3,4,5,6,7,8]

a.slice(2,4)    // [3, 4]

splice() 用来 删除、插入、替换数组的值。

  1. 删除 可以删除任意数量的项,只需要指定2个参数,要删除的第一个位置 和 要删除的数量。 splice(0,2) 删除数组的前两个
  2. 插入 可以向指定位置插入任意数量的项,需要提供3个参数。 起始位置、0、要插入的项。splice(2,0,’a’) 从数组的 第二个项插入 ‘a’
  3. 替换 可以向指定位置插入任意数量的项,同时删除任意数量的项。需要三个参数:起始位置、要删除的项数、要插入的任意数量。插入的数量不必与删除的项数相等。

3. Date 类型

创建一个日期对象 var now = new Date() 可以 通过 Date.parse() 和 Date.UTC() 来获取特定的日期和时间对象

Date.parse() 接受一个表示日期的字符串参数。
Date.UTC() 返回表示日期的毫秒数

3.1 继承的方法

跟其他类型一样,Date类型也重写了 toLocaleString()、toString() 和 valueOf()方法。在不同浏览器,不同时区,toLocalString() 返回值不同。

3.2 日期的格式方式

Date类型为日期格式化为字符串提供了如下方法:

  • toDateString() 以特定于实现的格式显示星期几、月、日和年
  • toTimeString() 以特定于实现的格式显示时、分、秒和时区
  • toLocaleDateString() 以特定于地区的格式显示星期几、月、日和年
  • toLocaleTimeString() 以特定于实现的格式显示时、分、秒
  • toUTCString() 以特定于实现的格式完整的UTC时间

4. RegExp类型

通过RegExp 类型支持正则表达式

g 表示全局模式,即模式被应用于所有字符串,而非发现的第一个匹配项时立即停止

i 表示不区分大小写模式,即在确定匹配项时忽略模式与字符串的大小写

m 表示多行模式,即在到达一行文本末尾时还会继续查找下一行中是否存在与模式匹配的项

5. Function类型

每一个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也时一个指向函数对象的指针,不会与某个函数绑定,函数通常是使用函数声明语法定义的。如下:

function a (b,c){
    return b + c
}

// 函数表达式声明

var s  = function(a,b){
    return a + b
}

5.1 没有重载

如果命名两个相同 function 那么后一个将会覆盖前一个

function a(b) {
    retrun b + 100
}

function a(b){
    return b + 200
}

a(100)  // 300

5.2 函数表达式和函数声明

在执行环境中加载数据的时候,对于函数表达式和函数声明,解析器优先读取函数声明,并使其在执行任何代码之前可用,至于函数表达式,则必须等到解析器执行到它所在的代码行,才会真正的被执行。

5.3 作为值的函数

因为ES 中的函数名本身就是变量,所以函数也可以作为值来使用,也就是说,不仅可以像传递参数一样把一个函数 传递给另外一个函数,而且可以将一个函数作为另外一个函数的结果返回。

5.4 函数内部属性

在函数内部,有两个特殊的对象:arguments 和 this。其中,arguments 是一个类数组对象,包含着传入函数中的所有参数,虽然 arguments 的主要用途是保存函数参数,但是这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments对象的函数。

this 引用的是函数据以执行操作的对象,或者说 this 是函数在执行时所处作用域,如果是全局 this 指向 window

注意: 函数的名字仅仅是一个包含指针的变量而已。

5.4 函数属性和方法

ES 中函数是对象,因此函数也有属性和方法,每个函数都包含两个属性:length 和 prototype。其中length 表示 函数希望接收的命名参数的个数。

prototype 是保存它们所有实例方法的真正所在,换句话说,诸如 toString() 和 valueOf() 等方法实际上保存在prototype名下。只不过通过各自对象的实例访问。

每个函数都包含两个非继承而来的方法: apply() 和 call()。这两个方法的用途都是特定的作用域中调用函数,实际上等于设置函数内 this 对象的值。

apply()

接受两个参数:一个是其运行函数的作用域,另一个是参数数组。
其中第二个参数可以是 Array 的实例也可以是 arguments对象。 

call()

和 apply() 的作用相同,只是接受参数的方式不同,第一个参数同
apply(), 第二个参数是一个参数列表而不是数组。

6. 基本包装类型

为了便于操作基本类型值,ES 还提供了3个特殊的引用类型: Boolean、Number 和 String。

6.1 Boolean 类型

Boolean 类型是布尔值对应的引用类型。要创建Boolean对象,可以像下面这样调用Boolean构造函数并传入 true 或 false 值。

let a = new Boolean(true)

注意: 基本类型与引用类型的布尔值还有两个区别,首先,typeof操作符对基本类型返回“boolean”,而对引用类型返回“object”。其次,由于Boolean对象是Boolean类型的实例,所以使用 instanceof操作符 测试 Boolean 对象会返回 true,而测试基本类型的布尔值则返回 false。

6.2 Number 类型

Number 是数字值对应的引用类型。要创建Number对象,可以在调用Number构造函数是向其中传递相应的数值。如下:

let n = new Number(10)

Number 提供了 toFixed() 方法,来格式化为字符串

在使用 typeof操作符测试基本类型数值时,始终返回 number ,而在测试 Number 对象时,则返回 object。Number 对象是 Number 类型的实例。而基本类型的数值则不是。

6.3 String 类型

String 类型是字符串的对象包装类型,如下:

let s = new String('zifuchuan')
  1. 字符方法
1
2
3
4
5
6
7
8
9
charAt() // 接收一个参数,基于0的字符位置。以单字符串的形式返回给定位置的哪个字符。

let s = 'abc'

a.charAt(1) // a

charCodeAt() // 也是接受一个参数,但是返回的是字符串的编码

a.charCodeAt() // 97
  1. 字符串操作方法
1
2
3
4
concat()    // 接受任意字符串,将字符串进行拼接
slice() // 截取字符串 从第一个参数 到第二个参数结束,如没有第二个参数则到最后一位
substring() // 同上
substr() // 同上
  1. 字符串的位置方法
1
2
indexOf()   // 都是从第一个字符串中搜索给定的子字符串
lastIndexOf()
  1. 字符串大小写转换
1
2
3
4
toLocaleUpperCase()     // 转为大写
toUpperCase() // 同上
toLocaleLowerCase() // 转为小写
toLowerCase() // 同上
  1. 字符串的模式匹配方法
1
match() // 接受一个参数,要么为正则表达式,要么为RegExp 对象。
  1. localeCompare() 方法
1
2
3
4
5
比较两个字符串,返回如下:

- 如果字符串在字母表中应该排在字符串参数之前,则返回负数
- 如果字符串等于字符串参数,返回0,
- 如果字符串在字母表中排在字符串参数之后,返回一个正数。

7. 内置对象

不依赖于宿主环境的对象,这些对象在ES程序执行之前就已经存在了。即不必显式地实例化内置对象。

7.1 Global对象

不属于任何其他对象的属性和方法,最终都是它的属性和方法。没有全局变量或全局函数,所有在全局作用域中定义的属性和函数,都是Global对象的属性。

7.2 Math对象

ES中为了保存数学公式和信息提供了一个公共位置,即Math对象。

8. 总结

  • 引用类型与传统面向对象程序设计中的类相似,但实现不同
  • Object 是一个基础类型,其他所有类型都从Object继承了基本的行为。
  • Array 类型是一组值的有序列表,同时还提供了操作和转换这些值的功能
  • Date 类型提供了有关日期和时间信息,包括当前日期和时间以及相关的计算功能
  • RegExp 类型是ES 支持正则表达式的一个接口,提供了基本的和一些高级的正则表达式功能。
  • 函数实际上是 Function 类型的实例,因此函数也是对象,而这一点正是 Javascript 最有特色的地方。由于函数也是对象,所有函数也拥有方法。
  • 每个包装类型都映射到同名的基本类型
  • 在读取模式下访问基本类型值时,就会创建对应的基本包装类型的一个对象,从而方便了数据操作。
  • 操作基本类型值的语句一经执行完毕,就会立即销毁新创建的包装类型
  • 在所有代码执行之前,作用域中就已经存在两个内置对象 Global 和 Math.

欢迎关注 公众号【小夭同学】