1. 第五章 基本引用类型
- 引用值(或者对象)是某个特定引用类型的实例。引用类型是把数据和功能组织到一起的结构。
- 对象被认为是某个特定引用类型的实例。新对象通过使用new操作符后跟一个
构造函数来创建。构造函数就是用来创建新对象的函数,
const date = new Date()
// 创建引用类型Date的一个新实例,并将它保存在变量date中,Date()在这里就是构造函数,它
// 负责创建一个只有默认属性和方法的简单对象
1.1 Date
- Date类型将日期保存为自协调世界时(UTC, Universal Time Coordinated)时间1970年1月1日午夜至今所经过的毫秒数。
- 创建日期对象,使用new操作符来调用Date的构造函数,在不给Date构造函数传参数的情况下,创建的对象保存当前日期和时间。要基于其他日期和时间创建日期对象,必须传入其他毫秒表示。在ECMAScript为此提供了两个辅助方法: Date.parse(), Date.UTC()
- Date.parse()方法接收一个表示日期的字符串参数,尝试将这个字符串转换为表示该日期的毫秒数。所有实现都必须支持下列日期格式。
- "月/日/年",如 "5/29/2022"
- "月名 日 年", 如 "May 29 2022"
- "周几 月名 日 年 时:分:秒 时区", 如 "Tue May 22 2022 18:00:00 GMT-0700"
- ISO 8601扩展格式 "YYYY-MM-DDTHH:mm:ss:sssZ",如 2022-06-22T18:00:00
- 比如,要创建一个表示 "2019年5月23日"的日期对象
let someDate = new Date(Date.parse("May 23 2019"))
// 如果传给Date.parse()的字符串并不表示日期,则该方法会返回NaN,如果直接把表示日期的字符串传递给Date
// 构造函数,那么Date会在后台调用Date.parse(),所以等价于下面代码
let someDate = new Date("May 23 2019")
- Date.UTC()方法也返回日期的毫秒表示,但使用的是跟Date.parse()不同的信息来生成这个值。
- 传递给Date.UTC()的参数是 年、月(0起点的月)、日(0-31)、时(0-23)、分、秒和毫秒。在这些参数中,只有前两个(年和月)是必须的。如果不提供日,那么默认为1日。其他参数的默认值都是0
// GMT时间 2022年1月1日零点
let someDate = new Date(Date.UTC(2022,0))
// GMT时间 2022年1月5日17点55分55秒
let allFives = new Date(Date.UTC(2022,0,5,17,55,55))
2. 与Date.parse()一样,Date.UTC()也会被Date构造函数隐式调用,但有一个区别,这种情况下创建的是本地日期,是不GMT日期,不过Date构造函数跟Date.UTC()接收的参数是一样的。
// 创建的是本地时间 2022年1月1日零点
let y2k = new Date(2022,0)
// 创建的是本地时间 2022年1月1日17时17分17秒
let allFives = new Date(2022,0,1,17,17,17)
- ECMAScript 还提供了Date.now()方法,返回表示方法执行时日期和时间的毫秒数
// 起始时间
let start = Date.now()
// 调用函数
doSomeThing()
// 结束时间
let end = Date.now()
// 函数执行的时间
result = end - start
1.1.1 继承的方法
- 与其他类型一样,Date类型重写了toLocaleString()、toString()和valueOf()方法。但与其他类型不同,重写后这些方法的返回值不一样。
- Date类型的 toLocaleString()方法返回与浏览器运行的本地环境一个的时间和日期
- Date类型的 toString()方法通常返回带时区信息的日期和时间
- Date类型的 valueOf()方法根本就不会返回字符串,这个方法重写后返回的是日期的毫秒表示
1.1.2 日期格式化方法
- Date类型有几个专门用于格式化日期的方法,它们都会返回字符串
- toDateString(): 显示日期中的周几、月、日、年
- toTimeString(): 显示日期中的时、分、秒和时区
- toLocaleDateString(): 显示日期中的周几、月、日、年
- toLocalTimeString(): 显示日期中的时、分、秒
- toUTCString(): 显示完整的UTC日期
1.1.3 日期/时间组件方法
1.2 RegExp
- ECMAScript通过RegExp类型支持正则表达式。
let expression = /pattern/flags;
// 这个正则表达式的pattern可以是简单或复杂的正则表达式,包括字符类、限定符、分组、向前查找和反向引用
// 每个正则表达式可以带0个或多个flags标记,用于控制正则表达式的行为。
1. g: 全局模式,表示在查找字符串的全部内容
2. i: 不区分大小写
3. m: 多行模式
4. y: 粘附模式,表示只查找从lastIndex开始及之后的字符串
5. u: Unicode模式,启用Unicode匹配
6. s: dotAll模式,表示元字符,匹配任何字符
// 匹配字符串中所有的 "at"
let pattern = /at/g
// 匹配第一个"bat"或"aat",忽略大小写
let pattern2 = /[b|a]at/i
// 匹配所有以"at"结尾,忽略大小写
let pattern3 = /.at/gi
1. 所有元字符在模式中也必须转义,包括 ( [ { \ ^ $ | ) ] } ? * + .
2. 元字符在正则表达式中都有一种或多种特殊的功能,所以要匹配上面的字符本身,就需要使用反斜杠来转义。
// 匹配第一个 "bat"或"cat",忽略大小写
let pattern = /[bc]at/i
// 匹配第一个"[bc]at" 忽略大小写
let pattern1 = /\[bc\]at/i
// 匹配所有以 "at"结尾的三字符组合,忽略大小写
let pattern2 = /.at/gi
// 匹配所有的".at" 忽略大小写
let pattern3 = /\.at/gi
2. 以上的例子中的正则表达式都是使用字面量形式定义的。正则表达式也可以使用RegExp构造函数来创建,它接收两个参数:模式字符串和可选的标记字符串。
// 匹配第一个"bat"或"cat" 忽略大小写
let pattern1 = /[bc]at/i
// 和pattern1一样,通过构造函数创建
let pattern2 = new RegExp("[bc]at","i")
// 因为RegExp的模式参数是字符串,所以在某些情况下需要二次转义。 在正则表达式字符串中要使用\\
// eg:
let pattern3 = new RegExp("\\[bc\\]at","i")
1.2.1 RegExp实例属性
- 每个RegExp实例都有下列属性,提供有关模式的各方面信息
- global: 布尔值,表示是否设置了g标记
- ignoreCase: 布尔值,表示是否设置了i标记
- unicode: 布尔值,表示是否设置了u标记
- sticky: 布尔值,表示是否设置了y标记
- lastIndex: 整数,表示源字符串中下一次搜索的开始位置,始终从0开始。
- multiline: 布尔值, 表示是否设置了m标记
- dotAll: 布尔值,表示是否设置了s标记
- source:正则表达式的字面量字符串,没有开头和结尾的斜杠
- flags: 正则表达式的标记字符串。始终以字面量而非传入构造函数的字符串模式形式返回。
let pattern = /\[bc\]at/i
console.log(pattern.global) // false
console.log(pattern.flags) // i
1.2.2 RegExp实例方法
- RegExp实例的主要方法是exec(),主要用于配合捕获组使用。这个方法只接收一个参数,即要应用模式的字符串。如果找到了匹配项,则返回包含第一个匹配信息的数组,如果没找到匹配项,则返回null
- 返回的数组虽然是Array的实例,但包含两个额外的属性:index和input。index是字符串中匹配模式起始位置,input是要查找的字符串。
let text = "mom and dad and baby"
let pattern = /mom(and dad (and baby)?)?/gi
let matchs = pattern.exec(text)
console.log(matchs.index) // 0
console.log(matchs.input) // "mom and dad and baby"
console.log(matchs[0]) // "mom and dad and baby"
console.log(matchs[1]) // "and dad and baby"
console.log(matchs[2]) // "and baby"
// 例子中,模式包含了两个捕获组:最内部的匹配项"and baby",以及最外层的匹配项"and dad"
// 调用exex()后找到了一个匹配项,所以matchs数组的index属性就是0
// 数组的第一个元素是匹配的整个字符串,第二个元素是匹配第一个捕获组字符串,第三个元素是匹配第二个捕获组的字符串
- 如果模式设置了全局标记,则每次调用exec()方法会返回一个匹配的信息。如果没有设置全局标记,则无论对同一个字符串调用多少次exec(),也只返回第一个匹配的信息。
- 无论正则表达式是怎么创建的,继承的方法 toLocaleString()和toString()都返回正则表达式的字面量表示
let pattern = new RegExp("\\[bc\\]at","gi")\
console.log(pattern.toLocaleString()) // /\[bc\]at/gi
console.log(pattern.toString()) // /\[bc\]at/gi
- 正则表达式的另一个方法是test(), 接收一个字符串参数。如果输入的文本与模式匹配,则返回true,否则返回false
let text = "000-00-0000"
let pattern = /\d{3}-\d{2}-\d{4}/
if(pattern.test(text)){
console.log("The pattern was matched...")
}
1.2.3 RegExp构造函数属性
- RegExp构造函数本身也有几个属性。这些属性适用于作用域中的所有正则表达式,而且会根据最后执行的正则表达式操作而变化。
- input 最后搜索的字符串
- lastMatch 最后匹配的文本
- lastParen 最后匹配的捕获组
- leftContext input字符串中出现在lastMatch前面的文本
- rightContext input字符串中出现在lastMatch后面的文本
let text = "this has been a short summer"
let pattern = /(.)hort/g
if(pattern.test(text)){
console.log(RegExp.input) // this has been a short summer
console.log(RegExp.leftContext) // this has been a
console.log(RegExp.rightContext) // summer
console.log(RegExp.lastMatch) // short
console.log(RegExp.lastParen) // s
}
1.3 原始值包装类型
- 为了方便操作原始值,ECMAScript提供了3种特殊的引用类型:Boolean Number String。
每当用到某个原始值的方法或属性时,后台都会创建一个相应原始包装类型的对象,从而暴露出操作原始值的各种方法。
let s1 = "some text"
let s2 = s1.subString(2)
// 解析,我们知道原始值本身不是对象,因此逻辑上不应该有方法。而实际上代码时按照预期执行了
// 代码会按照预期执行,是因为后台进行了很多处理,从而实现了正常的操作
// 具体来说,当第二行访问s1时,是以只读模式访问的,也就是要从内存中读取变量保存的值。
// 在已读模式访问字符串值的任何时候,后台都会执行3步操作 (1) 创建一个String类型的实例
// (2) 调用实例上的特定方法 (3) 销毁实例
// 这3步操作就可以想象成执行了如下3行ECMAScript代码
let s1 = new String("some text")
let s2 = s1.subString(2)
s1 = null
// 这种行为可以让原始值拥有对象的行为,对布尔值和数值而言,以上3步也会在后台发生,只不过使用的是Boolean和Number包装类型。
- 引用类型与原始值包装类型的主要区别在于对象的生命周期。在通过new实例化引用类型后,得到的实例会在离开作用域的时候被销毁,而自动创建的原始值包装对象只存在于访问它的那行代码执行期间。这也意味着不能在运行时给原始值添加属性和方法。
let s1 = "some text"
s1.color = "red"
console.log(s1.color) // undefined
- 在原始值包装类型的实例上调用typeof会返回"object",所有原始值包装对象都会转换为布尔值true。另外,Object构造函数作为一个工厂方法,能够根据传入值的类型返回相应原始值包装类型的实例。
let s1 = "some text"
let obj = new Object(s1)
console.log(obj instanceof String) // true
- 使用new调用原始包装类型的构造函数,与调用同名的转型函数并不一样。
let value = "25"
let number = Number(value)
console.log(typeof number) // "number"
lete obj = new Number(value) // 构造函数
console.log(typeof obj) // "object"
1.3.1 Boolean
- Boolean是对应布尔值的引用类型。Boolean的实例会重写valueOf()方法,返回一个原始值true或false。toString()方法被调用时会被覆盖,返回字符串的"true"和"false".
let falseObject = new Boolean(false)
let result = falseObject && true
console.log(result) // true
let resultValue = false
result2 = resultValue && true
console.log(result2) // false
// 解析: 在布尔算术中,false && true 返回的是false,可是,这个表达式 falseObject && true
// 是对falseValue对象而不是它表示的值false求值。所以表达式的结果会返回false
// 所有对象在布尔表达式中都会自动转换为true
- 除此之外,原始值和引用值(Boolean对象)还有几个区别。首先用typeof操作符对原始值返回"boolean",但对引用值返回"object"。同样Boolean对象是Boolean类型的实例。
console.log(typeof resultValue) // "boolean"
console.log(typeof falseObject) // "object"
console.log(falseObject instanceof Boolean) // true
console.log(resultValue instanceof Boolean) // false
1.3.2 Number
- Number是对应数值的引用类型。与Boolean类型一样,Number类型重写了valueOf()、toLocaleString()和toString()方法。
- valueOf()方法返回Number对象表示的原始数值,
- toString()和toLocaleString()方法可选地接收一个表示基数的参数,并返回相应基数形式的字符串
- 除了继承的方法,Number类型还提供了几个用于将数值格式化为字符串的方法
- toFixed() 表示返回的数值本身的小数位超过了参数指定的位数,则四舍五入到最接近的小数位
- toExponential() 返回以科学计数法表示数值的字符串
- toPrecision()方法
- isInteger()方法与安全整数
- ES6新增了Number.isInteger()方法,用于辨别一个数值是否保存为整数。
- 最小安全整数 Number.MIN_SAFE_INTEGER() -2的53次方加1
- 最大安全整数 Number.MAX_SAGE_INTEGER() 2的53次方减1
1.3.3 String
- String是对应字符串的引用类型。要创建一个String对象,使用String构造函数传入一个数值
let stringObject = new String("hello world")
- String对象的方法可以在所有字符串原始值上调用。3个继承方法toString() toLocaleString() valueOf()都返回对象的原始字符串值。
- Javascript字符串由16位码元组成,对于多数字符来说,每16位码元对应一个字符,也就是说字符串的length属性表示字符串包含多少个16位码元。
- 字符串的方法
- charAt() 返回给定索引位置的字符。
- concat() 用于将一个或多个字符串拼接成一个新字符串
- ECMAScript提供了3个从字符串中提取子字符串的方法,这3个方法都返回调用它们字符串的一个子字符串,而且都接收一或二个参数.
- slice()
- 第一个参数表示子字符串开始的位置,第二个参数表示字符串结束的位置()
- 没有写入第二个参数,默认从第一个参数位置开始截取到最后
- 当参数是负值时,将所有负值参数都当成字符串长度加上负参数值
- subString()
- 第一个参数表示子字符串开始的位置,第二个参数表示字符串结束的位置
- 没有写入第二个参数,默认从第一个参数位置开始截取到最后
- 当参数是负值时,会将所有负参数都转换为0,转换后如果第二个参数大于第一个参数会自动调用位置
- substr()
- 第一个参数表示子字符串开始的位置,第二个参数表示字符串截取的长度
- 没有写入第二个参数,默认从第一个参数位置开始截取到最后
- 当参数是负参数时,第一个负参数当成字符串的长度加上该负值,将第二个参数的负值转换为0
- slice()
- indexOf()和lastIndexOf()
- 用于在字符串中定位子字符串,这两个方法从字符串中搜索传入的字符串,并返回位置(如果没有找到返回-1)indexOf() 从字符串开头开始查找子字符串,而lastIndexOf()从字符串末尾开始查找子字符串。
- 这两个方法都可以接收第二个参数,表示开始搜索的位置。
// 在字符串中找到所有的目标子字符串
let stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"
let positions = stringValue.indexOf("e")
let pos = new Array()
while(positions > -1){
pos.push(positions)
positions = stringValue.indexOf("e")
}
console.log(pos)
- 字符串包含方法
- ES6增加了3个用于判断字符串中是否包含另一个字符串的方法:startsWith()、endsWith()、includes(),这些方法都会从字符串中搜索传入的字符串,并返回一个布尔值。
- startsWith()从索引0开始匹配,endsWith()检查开始于索引(stirng.length - substirng.length)的匹配项
- startsWith()和includes()方法接收可选的第二个参数,表示开始搜索的位置。
- trim() tirmLeft() trimRight() 删除字符串左/右空格。
- repeat()方法,表示要将字符串复制多少次。
- padStart()和padEnd()方法,方法会复制值字符串,如果小于指定的长度,则在相应一边填充字符
- 字符串大小写转换
- toLowerCase()
- toUpperCase()
- toLocaleLowerCase()
- toLocaleUpperCase()
- 字符串模式匹配方法
- match()方法,这个方法本质上跟RegExp对象的exec()方法相同。该方法接收一个参数,可以时一个正则表达式字符串,也可以是一个RegExp对象。
- match()方法返回的数组与RegExp对象的exec()方法返回的数组是一样的,第一个元素是与整个模式匹配的字符串,其余元素则是与表达式中捕获组匹配的字符串。
- search()方法,这个方法的唯一参数与match()方法一样:正则表达式字符串或RegExp对象。方法返回模式第一个匹配的位置索引,如果没有找到则返回-1.
- replace()方法,该方法接收2个参数,第一个参数可以是一个RegExp对象或一个参数字符串,第二个参数可以是一个字符串或是一个函数。
let text = "cat, bat, sat, lat"
let pattern = /.at/g
let matches = text.match(pattern)
console.log(matches.index) // 0
let result = text.replace(/at/g,"ond")
console.log(result) // cond, bond, sond, lond
- localeCompare()方法,这个方法比较两个字符串,返回如下3个值中的一个
- 按照字母表顺序,字符串应该排在字符串参数前头,则返回负值
- 如果字符串与字符串相等,则返回0
- 如果按照字母表顺序字符串应该排在字符串参数后头,则返回正值。
let stringValue = "yellow"
function determineOrder(value){
let result = stringValue.localeCompare(value)
if(result < 0 ){
console.log("字符串排在字符串参数的前面",value)
}else if(result > 0 ){
console.log("字符串排在字符串参数的后面",value)
}else{
console.log("字符串和字符串参数相同",value)
}
}
determineOrder("brick")
determineOrder("yellow")
determineOrder("zoo")
1.4 单例内置对象
- 对内置对象的定义是:"任何由ECMAScript实现提供、与宿主环境无关,并在ECMAScript程序开始执行时就存在的对象"。
- Global 对象是ECMAScript中最特别的对象,因为代码不会显示地访问它。ECMASCript-262规定Global对象为一种兜底对象,它所针对的是不属于任何对象的属性和方法。实际上,不存在全局变量或全局函数这种东西。在全局作用域中定义的变量和方法都会成为Global对象的属性。
- URL编码方式
- encodeURI()和encodeURIComponnent()方法,用于编码统一资源标识符(URI)
- decodeURI()和decodeURIComponnent()方法,将编码后的字符解码
- eval()方法
- eval()方法就是一个完整的ECMASCript解释器,它接收一个参数,即一个要执行的ECMAScript字符串
- 当解释器发现eval()调用时,会将参数解释为实际的ECMAScript语句,然后将其插入到该位置。
- Global对象属性
- undefined NaN Infinity Object Array Function Boolean String Number Date RegExp Symbol Error...
- window对象
- 虽然ECMA-262没有规定直接访问Global对象的方式,但浏览器将window对象实现为Global对象的代理。因此,在全局作用域中声明的变量和函数都变成了window的属性。
// 获取Global对象
let global = function(){
return this
}()
// 通过一个立即调用的函数表达式,返回了this的值。当一个函数在没有明确指定this值的情况下执行时,this值等于Global对象。因此,调用一个简单返回this的函数是在任何执行上下文中获取Global对象的通用方式。
- Math
- Math对象作为保存数学公式、信息和计算的地方
- Math的方法 Math.min() Math.max() Math.random() Math.abs() Math.pow() ...