《JavaScript高级程序设计》读书笔记-第五章(引用类型)
引用类型
引用类型的值( 对象 )是引用类型的一个实例。在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起。
引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方式。
Object类型
创建Object实例的两种方式:
1、new操作符后跟着Object构造函数
var person = new Object()
persion.name = 'Nicholas'
person.age = 29
2、对象字面量表示法
var person = {
name: 'Nicholas',
age: 29
}
-
var person = {}
与var person = new Object()
相等 - 有点表示法和方括号表示法,建议使用点表示法
person['first name'] = 'Nicholas' //方括号表示法
person.age = '18' //点表示法
Array类型
虽然ECMAScript数组与其他语言中的数组都是数据的有序列表,但与其他语言不同的是,ECMAScript数组的每一项都可以保存任何类型的数据。而且ECMAScript数组的大小是可以动态调整的,即可以随着数据的添加自动增长以容纳新数据。
- 创建数组的基本方式有两种
1、Array构造函数
var colors = new Array()
var colors = new Array(20) //length值为20的数组
var colors = new Array('red','blue','green') //创建一个包含3个字符串值的数组
可以省略new操作符,结果相同
2、数组字面量表示法
var names = []
var colors = ['red','blue','yellow']
- 利用length属性也可以方便地在数组末尾添加新项
var colors = ['red','blue','green']
colors[colors.length] = 'black'
colors[colors.length] = 'brown'
检测数组
Array.isArray()
方法
if( Array.isArray(value) ){
//对象组执行某些操作
}
数组继承的toLocaleString()、toString()、和valueOf()方法,在默认情况下都会以逗号分隔的字符串的形式返回数组想。而如果使用join()方法
var colors =['red','blue','green']
console.log(colors.join(',')) //red,green,blue
console.log(colors.join('||')) //red||green||blue
栈方法
后进先出
进栈push()
出栈pop()
var colors = ['red','blue']
colors.push('brown')
colors[3] = 'black'
var item = colors.pop() //'black'
队列方法
先进先出
入队shift()
出队push()
ECMAScript还为数组提供了unshift()
方法。
-
unshift()与shift()的用途相反,它能在数组前端添加任意个项并返回新数组的长度。
-
同时使用unshift()和pop()方法,可以从相反的方向来模拟队列
重排序方法
正序sort()
- sort()方法会调用每个数组项的toString()转型方法,然后比较得到的字符串,即使数组中的每一项都是数值,sort()方法比较的也是字符串
逆序reverse()
- 如果只想反转数组原来的顺序,使用reverse()方法要更快一些
操作方法
concat()
- concat()方法可以基于当前数组中的所有项创建一个新数组。
- 具体来说,这个方法会先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。
slice()
- slice()基于当前数组中的一个或多个项创建一个新数组
var colors = ['red','green','blue','yellow','pruple']
var colors2 = colors.slice(1) //green,blue,yellow,purple
var colors3 = colors.slice(1,4) //green,blue,yellow
splice()
- 删除:splice(0,2)会删除数组中的前两项
-
插入:只需要提供三个参数:起始位置、0(要删除的项数)和要插入的项
例如:splice(2,0,'red','green')会从当前数组的位置2开始插入字符串'red','green' -
替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项
splice(2,1,'red','green')会删除当前数组位置2的项,然后再从位置2开始插入字符串'red'和'green'
splice()方法始终都会返回一个数组,该数组中包含从原始数组中删除的项,如果没有删除任何项,则返回一个空数组。
会修改原数组。
位置
indexOf()
从开头找
lastIndexOf()
从结尾往前找
这两个方法会使用全等操作符
迭代方法
-
every()
对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true
var numbers = [1,2,3,4,5,4,3,2,1]
var everyResult = numbers.every(function(item,index,array)){
return (item > 2)
}
console.log(everyResult) //false
-
filter()
对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组
var numbers = [1,2,3,4,5,4,3,2,1]
var filterResult = numbers.filter(function(item,array,index){
return (item > 2)
})
console.log(filterResult) //[3,4,5,4,3]
-
forEach()
这个函数只是对数组的每一项运行传入的函数。这个方法没有返回值,本质上与使用for循环迭代数组一样
var numbers = [1,2,3,4,5,4,3,2,1]
numbers.forEach(function(item,index,array)){
//xxxxxxxxxxxxx
}
-
map()
也返回一个数组,而这个数组的每一项都是在原始数组中的对应项上运行传入函数的结果。例如,可以给数组中的每一项乘以2,然后返回这些乘积组成的数组
var numbers = [1,2,3,4,5,4,3,2,1]
var mapResult = numbers.map(function(item,index,array)){
return item * 2
}
console.log(mapResult) //[2,4,6,8,10,8,6,4,2]
-
some()
对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true
var everyResult = numbers.some(function(item,index,array)){
return (item > 2)
}
console.log(everyResult) //true
缩小方法
reduce()
从数组的第一项开始,逐个遍历到最后
reduceRight()
从数组的最后一项开始,向前遍历到第一项
-
这两个方法接受两个参数
1、在每一项调用的函数
2、作为缩小基础的初始值(可选) -
第一项函数接受4个参数,前一个值,当前值,项的索引和数组对象
这个函数返回的任何值都会作为第一个参数自动传给下一项。
所以使用reduce()方法可以执行求数组中所有值之和的操作
var values = [1,2,3,4,5]
var sum = values.reduce(function(prev,cur,index,array){
return prev + cur
})
console.log(sum) //15
Date类型
ECMAScript中的Date类型是在早期Java中的java.util.Date类基础上构建的。为此,Date类型使用自UTC(Coordinated Universal Time,国际协调时间)1970年1月1日午夜(零时)开始经过的毫秒数来保存日期。
在使用这种数据存储格式的条件下,Date类型保存的日期能够精确到1970年1月1日之前或之后的285616年
要创建一个日期对象,使用new操作符和Date构造函数即可
var now = new Date()
- 如果直接将表示日期的字符串传递给Date构造函数,也会在后台调用Date.parse()
- 还有很多零碎的函数,就不多描述了
RegExp类型
待更新
Function类型
函数声明与函数表达式
- 解析器会率先读取函数声明,并使其在执行任何代码之前可用。例如
console.log(sum(10,10))
function sum(num1,num2){
return num1 + num2
}
代码开始执行之前,解析器就已经通过一个名为函数声明提升(function declaration hoisting)的过程,读取并将函数声明添加到执行环境中。
如果改为函数表达式,就会报错
console.log(sum(10,10))
var sum = function(num1,num2){
return num1 + num2
}
- 访问函数的指针而不执行函数的话,必须去掉函数名后面的那对圆括号
函数内部属性
函数内部有两个特殊的对象:arguments
和this
argument
- arguments的主要用途是保存函数参数
- 有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数
请看下面非常经典的阶乘函数
function factorial(num){
if(num <= 1){
return 1
} else {
return num * factorial(num-1)
}
}
这个函数定义没有问题,问题在与这个函数执行与函数名factorial紧紧耦合在了一起,为了消除这种紧密耦合的现象,可以像下面这样使用arguments.callee
function factorial(num){
if(num <= -1){
return 1
} else {
return num * arguments.callee(num - 1)
}
}
this
其行为和Java和C#中的this大致类似。换句话说,this引用的是函数以执行的环境对象
函数属性和方法
length
- length属性表示函数希望接受的命名参数的个数
prototype
- 在ES5中,prototype属性时不可枚举的,因此使用for-in无法发现
每个函数都包含两个非继承而来的方法 : apply() 和 call()
-
这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
-
这两个函数用来传递参数,apply传递一个数组,call传递的方式是一个一个传递
apply:
function sum(num1 , num2){
return num1 + num2
}
function applySum1(num1 , num2){
return sum.apply(this, arguments)
}
function applySum2(num1 , num2){
return sum.apply(this, arguments)
}
console.log(callSum1(10,10)) //20
console.log(callSum2(10,10)) //20
call:
function sum(num1 , num2){
return num1 + num2
}
function callSum(num1 , num2){
return sum.call(this, null1, num2)
}
- 传递参数并非apply()和call真正的用武之地,他们真正强大的地方是能够扩充函数赖以运行的作用域
window.color = 'red'
var object = {color : 'blue'}
function sayColor(){
console.log(this.color)
}
sayColor() //red
sayColor.call(this) //red
sayColor.call(window) //red
sayColor.call(object) //blue
使用call()和apply()扩充作用域,最大的好处:就是对象不需要与方法有任何耦合关系。自己体会
- ES5还定义了一个方法,bind()
这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值
window.color = 'red'
var object = { color: 'blue' }
function sayColor(){
console.log(this.color)
}
var objectSayColor = sayColor.bind(object)
objectSayColor() //blue
基本包装类型
为了便于操作基本类型值,ECMAScript还提供了了三个特殊的引用类型:Boolean、Number和String
- 每当读取一个基本类型值的时候,后台就会创建一个对应的基本包装类型的对象
例如
var s1 = 'some text'
var s2 = s1.substring(2)
等于
1、创建String类型的一个实例
2、在实例上调用指定的方法
3、销毁这个实例
var s1 = new String('some text')
var s2 = s1.substring(2)
s1 = null
- 引用类型和基本包装类型的主要区别是对象的生存期
例如
var s1 = 'some text'
s1.color = 'red'
console.log(s1.color) //undefined
Object构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例
var obj = new Object('some text')
console.log(obj instanceof String) //true
Boolean类型
Boolean类型的实例重写了valueOf()方法,返回基本类型值true或false,重写了toString()方法
布尔表达式中的所有对象都会被转换为true
var falseObject = new Boolean(false)
var result = falseObject && true
console.log(result) //true
var falseValue = false
result = falseValue && true
console.log(resulet) //false
typeof的表现方式
- 基本类型:
console.log( typeof falseValue ) //boolean
- 对象类型 :
console.log( typeof falseObject ) //object
Number类型
Number同理
String类型
- charAt()
返回给定位置的字符
var string = 'hello world'
console.log(string.charAt(1)) //'e'
- charCodeAt()
返回给定位置的字符编码
var string = 'hello world'
console.log(string.charCodeAt(1)) //'101'
- concat()
原数组不变,返回一个新数组
concat()可以接受任意多个参数
var stringValue = 'hello'
var result = stringValue.concat('world')
console.log(result) //'hello world'
console.log(stringValue) //'hello'
- length
即使字符串中包含双字节字符(不是占一个字节的ASCII字符),每个字符也仍然算一个字符 -
slice()、substr()、substring()
-
slice() 和 substring()的第二个参数指定的是子字符串最后一个字符后面的位置。
substr()的第二个参数指定的是返回的字符个数 - 当这三个方法的参数是负值的情况下,他们的行为就不尽相同了
- slice()方法会将传入的负值与字符串的长度相加,第二个参数转换为7
- substr()方法将负的第一个参数加上字符串的长度,而将负的第二个参数转换为0
- substring()方法会把所有的负值参数都转换为0
-
slice() 和 substring()的第二个参数指定的是子字符串最后一个字符后面的位置。
var string = 'hello world'
console.log(string.slice(-3)) //'rld'
var string = 'hello world'
console.log(string.substr(3)) //'rld'
console.log(string.substr(3,-4)) //'空字符串'
var string = 'hello world'
console.log(string.substring(-3)) //'hello world'
console.log(string.substring(3,-4)) //'hel'
字符串位置方法
- indexOf()
从前往后 - lastIndexOf()
从后往前
var string = 'hello world'
console.log(string.indexOf('o')) //4
console.log(string.lastIndexOf('o')) //7
可以循环调用indexOf()或lastIndexOf()来找到所有匹配的子字符串
var string = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit'
var positions = new Array()
var pos = stringValue.indexOf('e', pos + 1)
while(pos > -1){
positions.push(pos)
pos = string.indexOf('e', pos + 1)
}
console.log(positions) //'3,24,32,35,52'
- trim()
删除前后空格,原始字符串的前后空格保持不变,返回字符串的副本
字符串大小写转换方法
-
toLowerCase()、toLocaleLowerCase()
转换为小写
LocaleLowerCase是针对特定地区的实现,少数语言会为Unicode大小写转换应用特殊的规则 -
toUpperCase()、toLocaleUpperCase()
转换为小写 同上
字符串的模式匹配方法
String类型定义了几个用于在字符串中匹配模式的方法
-
match()
match()方法只接受一个参数,要么是一个正则表达式,要么是RegExp对象
返回了一个数组,数组的第一项是与整个模式匹配的字符串,之后每一项保存着与正则表达式中的捕获组匹配的字符串。
var text = 'cat, bat, sat, fat'
var pattern = /.at/
var matches = text.match(pattern)
//与pattern.exec(text)相同
console.log(matches.index) //0
console.log(matches[0]) //'cat'
console.log(pattern.lastIndex) //0
-
search()
search()方法返回字符串中第一个匹配项的所有;如果没有找到匹配项,则返回-1
从字符串开头向后查找模式
var text = 'cat, bat, sat, fat'
var pos = text.search(/at/)
console.log(pos) //1
-
replace()
替换
第一个参数可以是一个RegExp对象或者一个字符串(这个字符串不会转换成正则表达式)
第二个参数可以是一个字符串或者一个函数
如果想替换所有匹配的字符串,唯一的办法就是提供一个正则表达式
var text = 'cat, bat, sat, fat'
var result = text.replace('at','ond')
console.log(result) //'cond,bat,sat,fat'
result = text.replace(/at/g, 'ond')
console.log(result) //'cond,bond,sond,fond'
如果第二个参数是字符串,还可以用一些特殊的字符序列
- $&匹配整个模式的子字符串
- $'匹配的子字符串之前的子字符串
- $`匹配的子字符串之后的子字符串
- $n匹配第n个捕获组的子字符串
- $nn匹配第nn个捕获组的子字符串
var text = 'cat, bat, sat, fat'
result = text.replace(/(.at)/g,'word($1)')
replace()的第二个参数也可以是一个函数。
在只有一个匹配项的情况下,会向这个函数传递3个参数:模式的匹配项、模式匹配项在字符串中的位置和原始字符串
function htmlEscape(text){
return text.replace(/[<>'&]/g, function(match,pos,originalText){
switch(match){
case '<':
return '<'
case '>':
return '>'
case '&':
return '&'
case '\':
return '"'
}
})
}
- split()
split()返回一个数组
split()方法可以接受可选的第二个参数,用于指定数组的大小
单体内置对象
URI(标识、定位任何资源的字符串)
- encodeURI()
不会对本身属于URI的特殊字符进行编码
var uri = 'http://www.wrox.com/illegal value.htm#start'
console.log(encodeURI(uri))
console.log(encodeURIComponent(uri))
- encodeURIComponent()
则会对它发现的任何非标准字符进行编码
eval()
eval()中创建的任何变量或函数都不会被提升,在解析代码的时候,他们被包含在一个字符串中