本章主要讲了js中的基本引用类型: Date,RegExp,原始包装类型,单例内置对象四个部分
基本引用类型
引用值是某个引用类型 的实例.在ECMAScript中引用类型是把数据和功能组织到一起的结构,经常被人错误的认为是类,虽然从技术上讲JavaScript是一门面向对象的语言,但ECMAScript缺少传统面向对象编程语言的某些基本结构,包括类和接口.
引用值通过new
操作符后面跟上构造函数(用来创建新对象的函数)进行创建.ECMAScript提供了很多原生的引用类型.
Date
Data类型将日期保存为自协调世界时(UTC,Universal Time Coordinated)
时间1970年1月1日零时至今的毫秒数,使用这种格式Data类型可以精确表示1970年1月1日零时之后285616年的所有日期.
先提前介绍以下日期格式标准:
-
UTC(Universal Time Coordinated): 协调世界时,现世界标准时间,由国际原子能机构维护,是基于原子钟的国际时间标准.以原子钟所定义秒长,UTC时间认为一天总是恒定的86400秒,其计算方式考虑了地球自转的不规则性要比GMT时间更为精确.协调世界时不与任何地区位置相关,也不代表此刻某地的时间,所以在说明某地时间时要加上时区
-
GMT(Greenwich Mean Time): 格林威治平时,规定太阳每天经过位于英国伦敦郊区的皇家格林威治天文台的时间为中午12点。UTC之前的世界标准时间
-
ISO 8601: ISO 8601 是由国际标准化组织(ISO)定义的日期和时间的表示法标准.其目的是提供一种统一的方式来表示日期和时间,以便于数据交换和解释。其有固定的时间格式:
rYYYY-MM-DD T HH:mm:ss.sss Z T 为日期和时间的分隔符 Z表示时区
-
时区:根据圆周 360° 将地球划分为 24 个时区,每个时区占 15°,以中央经线左右 7.5° 划分为一个时区。以本初子午线做为中(零)时区的中央经线 ,依次向东西划分。比如图中东八区,边界的正中间就是东八区的中央经线。我国横跨了东时区
四、五、六、七、八、九
六个时区.相邻的时区相差一个小时
通过Date构造函数创建日期:
const now = new Date()
- 不传参,创建的对象将保存当前的日期和时间
- 传递参数,必须是毫秒,创建的对象保存基于参数的日期和时间
只能接收毫秒这个参数很不方便,ECMAScript为此提供了两个方法Date.parse()和Date.UTC()
,可以将固定时间格式转换成毫秒数.在new Date()
传递参数时,Date会在后台自动调用这两个方法.传入字符串时调用Date.parse()
,传入数字时调用Date.UTC()
Date.parse()
参数: 一个表示日期的字符串,尝试将这个字符串转换为表示该日期的毫秒.如果传递的字符串不表示日期,则该方法会返回NaN.
- 月/日/年: "5/23/2023"
- 月名 日,年: "May 23,2023"
- 周几 月名 日 年 时:分:秒 时区: "Tuy May 23 3023 00:00:00 GMT-0700"
- ISO 8601扩展格式: "YYYY-MM-DDTHH:mm:ss.sssZ",如2019-05-23T00:00:00
Date.UTC()
参数格式为: 年,零点起月数(0-11),日(1-31),时(0-23),分,秒,毫秒.前两个年和月是必须的,如果不提供后面的参数会使用默认每个单位的最小值
vbnet
const date = new Date(2023,4,5,17,55,55)
Date.now
获取执行时日期和时间的毫秒数
继承的方法
Dare类型重写了toLocaleString,toString,valueOf
方法,
-
toLocaleString(): 返回与浏览器运行的本地环境一致的日期和时间,包含
AM和PM
但不包含时区信息,这与浏览器不同而不同javascriptconst date = new Date(2023, 4, 5, 17, 55, 55); console.log(date.toLocaleString()); //2023/5/5 17:55:55
-
toString(): 返回24小时制,带有时区的日期和时间
vbscriptconst date = new Date(2023, 4, 5, 17, 55, 55); console.log(date.toString()); //Fri May 05 2023 17:55:55 GMT+0800 (中国标准时间)
-
valueOf(): 该方法重写后返回的是日期的毫秒表示,操作符可以直接使用它的返回值
javascriptconst date = new Date(2023, 4, 5, 17, 55, 55); console.log(date.valueOf()); //1683280555000
日期实例方法
RegExp
创建:
- 使用RegExp构造函数:
const regx = new RegExp(pattern,[flags])
- 使用字面量:
const regx = /pattern/flags
pattern: 模式正则表达式的主要内容
flags: 标记.可有传递多个,可选值有:
- g:全局模式,表示查找字符串的全部内容,而不是找到第一个匹配的就结束
- i:不区分大小写
- m: 多行模式,查找到一行文本结尾后继续查找
- y: 黏附模式,只查找
lastIndex
开始及之后的字符串 - u: Unicode模式,启用Unicode匹配
- s:dotAll模式,表示元字符
.
匹配任何字符
RegExp实例属性
- global: 布尔值,表示是否设置了
g
标记 - ignoreCase: 布尔值,表示是否设置了
i
标记 - uncode: 布尔值,表示是否设置了
u
标记 - sticky: 布尔值,表示是否设置了
y
标记 - lastIndex: 整数,表示源字符串中下次搜索的开始位置,始终从0开始
- multiline: 布尔值,表示是否设置了
m
标记 - dotAll: 布尔值,表示是否设置了
s
标记 - source: 正则表达式字面量字符串
- flags: 正则表达式标记字符串
原始包装类型
为了便于操作原始值(可以直接调用对应引用类型的方法),ECMAScript提供了三种特殊的引用类型: Boolean,Number,String
.当访问某个原始值的方法时,后台后创建一个对应的原始包装类型的对象,从而暴露出各种方法.
上例子:
vbscript
let str = "text";
console.log(str.substring(2)); //"xt"
上述创建了一个基本类型的变量,虽然是基本类型,但却可以直接点出方法进行使用,逻辑上基本类型不是对象,是无法点出方法的,其实对于Boolean,String,Number
类型的值,在以RHS访问时,都会经历以下三步:当程序执行到str.substring(2)
时:
- 根据原始值创建对应的原始包装类型的实例
- 调用实例的方法
- 销毁实例
原始包装类型和不同的引用类型的主要区别: 生命周期不同.引用类型通过new实例化后,直到实例离开作用域时才被销毁.原始包装类型的实例,从访问原始值属性时创建,访问完毕就会销毁
再看一个例子:
ini
let str = 'text';
str.a = "我是a属性";
console.log(str.a) //undefined
上述代码的执行过程如下: 从第二行开始
- 创建String类型的实例,添加属性
a
并赋值为我是属性a
- 第二行执行完毕,销毁上个步骤的创建的String实例
- 执行第三行,创建新的String实例,访问新实例的
a
属性,发现没有,返回undefined
Boolean
布尔值的引用类型,创建Boolean
对象:
typescript
const boolean = new Boolean(true)
其重写的valueOf()和toString()方法:
arduino
boolean.valueOf() //true
boolean.toString() //"true"
Number
创建:
javascript
const numObject = new Number(0)
格式化数字:
-
toString([基数])
返回基于基数形式的字符串,没有穿基数,默认十进制
-
toFixed(num)
返回包含指定位小数点的数值字符串,数字超出指定位数,四舍五入,未超出补零
-
toExponential(num)
返回以科学计数法表示的数值字符串,参数和toFixed的参数相同
String
编码
JavaScript字符串是基于Unicode
编码的,并使用UTF-16
的编码方式,属于可变长度的编码,每个字符占用2字节或4字节.2字节用来表示U+0000~U+FFFF
范围内的字符,超过这个范围会使用4字节进行标识,叫代理对 ,为了表示更多的字符,Unicode采用一个策略,即每个字符使用另外16位去选择一个增补平面
length属性
JavaScript字符串由16位码元 组成,每个字符由16位码元组成.每个字符串都有一个length
属性,表示该字符穿串由多少个16位码元组成.
ini
const msg = 'abcde';
console.log(msg.length);//5
chatAt方法
参数: 整数,索引值
返回字符串给定索引位置的字符,该方法查找指定位置的16位码元,并返回码元对应的字符
arduino
console.log(msg.cahrAt(2)) // 'c'
charCodeAt方法
参数: 整数,索引值
该方法返回指定索引位置的码元值(十进制)
arduino
console.log(msg.charCodeAt(2));//99 =>转换为十六进制 0x63
fromCharCodeAt方法
参数:多个UTF-16码元值
接收任意多个码元值,并将码元值对应的字符拼接成字符串返回.
arduino
String.fromCharCode(87,97,107) //Wak
fromCharCode
是根据码元值进行转换的,而且浏览器可以正确的解析代理对
,因此无论是2字节还是4字节都能正确显示
分割
对于2字节的编码字符 ,以上方法都能如期工作 ,对于超过两字节范围的字符,如表情符号等,JS使用代理对(详情看编码一节),每个字符使用两个16位码元表示,上面的方法就会出现问题:
arduino
const str = '😊';
console.log(str.length); // 2
console.log(str.charAt(0));//� 打印字符的第一个16位码元
console.log(str.charAt(1));//� 打印字符的第二个16位码元
console.log(str.charCodeAt(0));//55357
console.log(str.charCodeAt(1)); //56842
console.log(str.fromCharCode(97, 98, 55357, 56842, 100,101)) //ab😊de
除了fromCharCode
正常返回之外为此js提供了下面这些方法:
codePointAt方法
可以用于替代charCodeAt
方法
参数: 索引值
接收索引值并返回该索引位置上的码点 ,码点 是指字符集中每个字符对应的唯一数值标识,码点从0X0000到0x10FFFFF,要注意的是如果传递的索引是代理对的结尾编码(如下所示)就会返回错误的码点值,解决办法就是从左到右完整的遍历字符串
arduino
const str = '😊';
console.log(str.codePointAt(0)); //128522 将代理对的开头传递给该方法返回正确的码点
console.log(str.codePointAt(1)); //56842 将代理对的结尾传递给该方法返回的错误的码点,其实返回的是该位置码元的字符编码
fromCodePoint方法
对应fromcharCode
方法,接收任意数量的码点值,并返回对应的字符串
arduino
console.log(String.fromCodePoint(128522)); // 😊
normalize方法
由于有些字符既可以通过BMP
表示(2字节),又可以用代理对
表示(4字节),因此可能出现用不同编码方式表示的同一个字符的变量比较起来并不相等
arduino
console.log(String.fromCharCode(0x00c5)); //Å
console.log(String.fromCharCode(0x0041, 0x030a)); //Å
上述两个编码都表示Å
const char1 = String.fromCharCode(0x00c5);
const char2 = String.fromCharCode(0x0041, 0x030a);
char1 === char2 //false
两个相同的字符比较起来却不相等
为了解决这个问题,Unicode提供了4种规范化形式,可以将上面的字符规范成一致的格式: NFD,NFC,NFKD,NFCK;调用normalize
方法时传入指定格式名称,可以规范成该格式
less
console.log(char1.normalize('NFC') === char2.normalize('NFC')); //true
字符串操作方法
-
concat
参数任意个,连接两多个字符串并返回新的字符串
-
slice(start,end)
获取字符串子串,子串从start开始到end之前结束,范围:[start,end),
如果有负值参数,将所有负值参数都当成字符串长度加上负值参数
-
substr(start,num)
获取字符串子串,从start开始到start+num位置结束,num表示获取start之后的多少位,
如果有负值参数,第一个负值参数当成字符串长度加上负值参数,第二个负值参数转换成0,
-
substring(start,end)
获取字符串子串,子串从start开始到end之前结束,范围:[start,end),
如果有负值参数,会将所有负值参数转换成0
substr,substring,slice不传递第二个参数,都默认获取到字符串结尾
字符串位置方法
-
indexOf(char,[index])
参数: char:需要查找的字符,index:开始查找的位置
从第一个字符串开始搜索char在字符串中出现的第一个位置,并返回对应索引,如果没有返回
-1
,传递第二个参数会从第二个参数开始向后查找 -
lastIndexOf(char,[index])
参数: 参数: char:需要查找的字符,index:开始查找的位置
从最后一个字符开始搜索char在字符串中出现的第一个位置,并返回对应索引如果没有返回
-1
,传递第二个参数会从第二个参数开始向前查找
字符串包含方法
ES6增加了三种判断字符串中是否包含另一个字符串的方法
-
startWith(str,[index])
是否以str开头,返回
Boolean
值.如果有第二个参数,表示从第二个参数之后进行搜索 -
endWith(str,[index])
是否以str结尾,返回
Boolean
值.如果有第二个参数,表示从第二个参数之前进行搜索 -
includes(str,[index])
是否以包含str,返回
Boolean
值.如果有第二个参数,表示从第二个参数之前进行搜索
trim()方法
创建字符串副本,删除前后的空格,并返回这个副本
- trimLeft和trimRight分别用于只删除左或右的空格
repeat方法
接收一个整数参数,表示将字符串复制多少次,然后返回拼接所有副本后的结果
padStart和padEnd方法
参数: 1.Number,表示长度.2.String,表示填充字符串
复制字符串,如果小于指定长度,会用填充字符串进行补充
字符串模式匹配方法
-
match
- 接收一个参数,可以是正则表达式,或者RegExp对象.返回一个数组,数组第一个元素是与整个模式匹配的字符串,其余元素则是与表达式中捕获组匹配的字符串
-
search
- 接收一个与
match
相同的参数,返回模式第一个匹配的索引,如果没找到返回-1
- 接收一个与
-
replace
-
接收两个参数,第一个参数可以是正则或者字符串,用于匹配.第二个参数可以是字符串或函数,用于替换
-
第一个参数是字符串: 只会替换第一个匹配的子串
-
第一个参数是正则: 带上全局标记
g
可以替换所有匹配的子串,没有带全局标记只会替换第一个匹配的字符串 -
第二个参数是字符串: 用于替换
-
第二个参数是函数: 只有一个匹配项时函数收到三个参数
- 与整个模式匹配的子串
- 匹配项在字符串开始的位置
- 整个字符串
多个捕获组时,其他捕获组也会传递给函数,只是最后两个参数用元素是上述后两个参数
-
-
-
htmlEscape
- 将HTML字符中4个字符替换成对应的实体:小于号,大于号,和号,双引号
-
split
- 接收一个参数作为分隔符,默认是空,将字符串以参数为分隔符分割成数组
localeCompare()
整个方法用于比较字符串返回值有以下三个:
- 如果按照字母表顺序,字符串应该排在字符串参数之前,则返回负值,通常是
-1
- 如果字符串与字符串参数相等,则返回
0
- 如果按照字母表顺序,字符串排在字符串参数之后,则返回正值,通常是
1
ini
//相同字符串
const str1 = 'abc';
const str2 = 'abc';
console.log(str1.localeCompare(str2)); //0
const str1 = 'abc';
const str2 = 'bcd';
console.log(str1.localeCompare(str2)); //-1 str1与str2等长
const str1 = 'abc';
const str2 = 'abcd';
console.log(str1.localeCompare(str2)); //-1 str1与str2前半部分相同,但str2长于str1
const str1 = 'abcd';
const str2 = 'abc';
console.log(str1.localeCompare(str2)); //1 str1与str2前半部分相同,但str2短于str1
const str1 = 'aacd';
const str2 = 'abcd';
console.log(str1.localeCompare(str2)); //-1 str1于str2前缀相同,且等长.从不相同的地方开始比较
单例内置对象
ECMAScript将内置对象定义为:任何由ECMAScript提供,与宿主环境无关,并在ECMAScript程序开始执行时就已经存在的对象
,包括:Object,Array,String
等,但我们无法显示的访问这个对象,下面介绍两个单例内置对象Global和Math
.
Global
Global
对象是一种兜底对象,他针对的是不属于任何对象的属性和方法,事实上,不存在全局变量或全局函数这种东西.在全局作用域中定义的变量和函数都会称为Global
对象的属性 ,去多函数都是Global
对象上的方法:isNaN,isFinite
等,下面介绍以下其他属性:
URL编码和解码
编码:
在根据用户输入动态构建URL的时候,为了包保证URl的合法性,需要对用户输入的内容做编码,这样浏览器才能正确识别,(如URI中不允许有空格等)
-
encodeURI()
将特殊字符进行编码如空格,但不会对原本属于URL组件的特殊字符编码如: 冒号,斜杠,问号,井号,等
perlconst url = 'http://test.com/this is example'; console.log(encodeURI(url)); //http://test.com/this%20is%20example 只对空格进行了编码,将空格转换成了%20
-
encodeURIComponent(),比encodeURI更常用
将所有特殊字符进行编码,包括属于URL组件的特殊字符
iniconst url = 'http://test.com/this is example'; console.log(encodeURIComponent(url)); //http%3A%2F%2Ftest.com%2Fthis%20is%20example 将所有特殊字符进行了编码
解码:
将编码后的URl恢复
-
decodeURI
与
encodeURI
对应,只对使用encodeURI
编码的字符进行解码 -
decodeURIComponent
与
encodeURIComponent
对应,只对使用encodeURIComponent
编码的字符进行解码
eval()
不推荐使用这个方法,具体原因可以查看这篇文章性能问题一节讲述了eval
影响性能的原因: 《You Dont Know JS上卷》(二) ---词法作用域
window对象
虽然ECMAScript-262没有规定直接访问Global
对象的方式,但浏览器将我window
对象实现为了Golbal
对象的代理,因此全局作用域中声明的函数和变量都变成了window
对象的属性
window对象将在后面单独章节进行介绍,
还有一种获取Global
对象的形式:
javascript
const global = function(){
return this
}()
使用立即调用函数返回this的值,当一个函数没有明确指定this的时候,this值等于Global
对象
Global对象属性
Global
对象的属性包含各种特殊值和原生引用类型的构造函数
属性 | 说明 |
---|---|
undefined | 特殊值undefined |
NaN | 特殊值NaN |
Infinity | 特殊值Infinity |
Object | 构造函数 |
Array | 构造函数 |
Function | 构造函数 |
Boolean | 构造函数 |
String | 构造函数 |
Number | 构造函数 |
Date | 构造函数 |
RegExp | 构造函数 |
Symbol | 构造函数 |
Error | 构造函数 |
EvalError | 构造函数 |
RangeError | 构造函数 |
ReferenceError | 构造函数 |
SyntaxError | 构造函数 |
TypeError | 构造函数 |
URIError | 构造函数 |
Math
Math对象提供数学相关的计算,他的速度比js实现计算的速度快的多,但其精度会因为浏览器,操作系统,指令集,和硬件而异
min()和max()
任意多个参数,返回这些参数中最小/最大的值
舍入方法
-
Math.celi(num)
始终向上舍入(进)为最近的整数
-
Math.floor(num)
始终向下舍入(入)为最近的整数
-
Math.round(num)
执行四舍五入
-
Math.fround(num)
返回数值最接近的单精度32位浮点值的表示
random()
返回0~1范围内的随机数,包含0但不包含1,想从n
的范围内随机返回一个值可以这样写:
javascript
const getRandom = (n) => {
return Math.floor(Math.random() * n + 1);
}
如果想返回m~n之内的随机整数,可以这样写:
const getRandom = (m,n) => {
const choices = n - m + 1;
return Math.floor(Math.random() * choices + m);
}
上面的写法对一般的需求是没有问题的,但如果为了加密需要较高的随机性,建议使用window.crypto.getRandomValues()
,该方法接收一个类型化数组作为参数,并将随机数填充到该数组中。常见的类型化数组包括 Uint8Array
、Uint16Array
、Uint32Array
等