JavaScript高级程序设计(第4版)-第5章 基本引用类型

第5章 基本引用类型

引用值/对象:某个特定引用类型的实例

引用类型/对象定义:把数据和功能组织到一起的结构,函数也是一种引用类型

构造函数(constructor):用来创建新对象的函数

JavaScript是一门面向对象的语言,但是缺少传统面向对象编程语言的某些基本结构(类、接口等),因此引用类型和类并不是一个概念。

ECMAScript提供许多原生引用类型

5.1 Date

ECMAScript中Date类型参考的是Java早期版本的java.util.Date

Date类型将日期保存为自协调世界时(UNIX纪元)至今所经过的毫秒数

协调世界时:UTC,Universal Time Coordinated-1970.1.1.00:00

Date精确表示范围:1970.1.1.00:00±285616年

不同的浏览器对Date类型的实现有很多问题

let now = new Date();
console.log(now);   //Tue Jun 28 2022 09:49:59 GMT+0800(中国标准时间)

不给Date构造函数传参时,创建的对象保存当前的日期和时间

要基于其他日期和时间,需要传入自UNIX纪元之后的毫秒数

ECMAScript对此提供两个辅助方法:

  • Date.parse()
    • 接收一个表示日期的字符串参数
      • 月/日/年:6/28/2022
      • 月名 日, 年:Jun 28, 2022
      • 周几 月名 日 年 时:分:秒 时区:Tue Jun 28 2022 09:49:59 GMT+0800
      • ISO 8601扩展格式YYYY-MM-DDTHH:mm:ss:sssZ(只适用于兼容ES5的实现):2022-06-28T09:49:59
    • Date.parse(非日期表示字符串)=NaN
    • new Date(日期表示字符串):Date会在后台对参数隐式调用Date.parse()
let date = new Date("May 30, 2010");
console.log(date);  //Sun May 30 2010 00:00:00 GMT+0800 (中国标准时间)
  • Date.UTC()
    • Date.UTC(年,月0-11,[日1-31],[时0-23],[分0-59],[秒0-59],[毫秒])
    • 只有年和月是必须的
    • 如果不提供日,默认为1日
    • 其他参数不提供默认0
    • 也会被Date构造函数隐式调用
let date = new Date(2008, 2, 19, 10, 3, 34);
console.log(date);  //Wed Mar 19 2008 10:03:34 GMT+0800 (中国标准时间)

ECMAScritp还提供了Date.now()方法,可以返回方法执行时日期和时间的毫秒数,一般使用该方法用于分析代码性能

function test() { 
  for (let i = 0; i < 1000; i++) { 
    for (let j = 0; j < 1000;j++) { }
  }
}
let start = Date.now();
test();
let end = Date.now();
console.log(end - start); //1

5.1.1 继承的方法

Date类型重写的toLocalString()、toString()和valueOf()方法的返回值各不相同

  • toLocalString()
    • 返回与浏览器运行的本地环境一致的日期和时间
    • 有的浏览器会在格式中包含针对时间的AM/PM,但不包含时区信息
  • toString()
    • 通常返回带时区信息的日期时间
    • 时间往往以24小时制表示
    • 现在浏览器的toLocalString和toString输出结果上通常一致
    • 版本较老的浏览器可能会不同
  • valueOf()
    • 返回的是日期的毫秒表示
    • 通常用于比较日期的先后
let date = new Date(2022, 7, 6, 13, 14, 5, 21);
let date2 = new Date("8/7/2022");
console.log(date.toLocaleString()); //2022/8/6 13:14:05
console.log(date.toString()); //Sat Aug 06 2022 13:14:05 GMT+0800 (中国标准时间)
console.log(date.valueOf());  //1659762845021
console.log(date.valueOf() < date2.valueOf());  //true

5.1.2 日期格式化方法

Date类型有几个专门用于格式化日期的方法(都返回字符串,且会因浏览器而异):

  • toDateString():周-月-日-年
  • toTimeString():时-分-秒-时区
  • toLocaleDateString():年月日
  • toLocaleTimeString():时分秒
  • toUTCString():完整UTC日期
  • toGMTString():和toUTCStirng功能一致,目的是为向后兼容
let date = new Date();
console.log(date.toDateString()); //Tue Jun 28 2022
console.log(date.toTimeString()); //10:27:31 GMT+0800 (中国标准时间)
console.log(date.toLocaleDateString()); //2022/6/28
console.log(date.toLocaleTimeString()); //10:28:15
console.log(date.toUTCString());  //Tue, 28 Jun 2022 02:28:35 GMT
console.log(date.toGMTString());  //Tue, 28 Jun 2022 02:29:12 GMT

5.1.3 日期/事件组件方法

Date类型剩下方法涉及到取得或设置日期值的特定部分,

UTC日期:没有时区偏移(日期转换为GMT)时的日期

5.2 RegExp

let expression = /pattern/flags;
  • pattern(模式):可以使任何简单或复杂的正则表达式,包括:
    • 字符类
    • 限定符
    • 分组
    • 向前查找
    • 反向引用
  • flag(标记):每个正则表达式可以带0个或多个flags,用于控制正则表达式的行为,匹配模式的标记有如下这些:
    • g:全局模式,查找字符串全部内容
    • i:不区分大小写
    • m:多行模式
    • y:粘附模式:只查找从lastIndex开始及之后的字符串
    • u:Unicode模式
    • s:toAll模式,表示元字符

所有元字符在模式中必须转义:

正则表达式创建方式也分两种:

  • 字面量形式定义:需要注意元字符的转义
let pattern1 = /[bc]at/i; //bat/car
let pattern2 = /\[bc\]at/i  //[bc]at
let pattern3 = /.at/gi; //at结尾
let pattern4 = /\.at/gi;  //.at
  • 构造函数创建
    • RegExp(模式字符串,标记字符串)
    • 两个参数都是字符串
    • 所有元字符都需要二次转义,包括转义字符序列
    • 可能够基于已有的正则表达式实例,并可选择性修改标记
let rel = /.at/gi;
let pattern1 = new RegExp("[bc]at", "i");  //bat/cat
let pattern2 = new RegExp("\\[bc\\]at", "i"); //[bc]at
let pattern3 = new RegExp(rel); // /.at/gi
let pattern4 = new RegExp(rel, "s");  //  /.at/s

5.2.1 RegExp实例属性

每个RegExp都有下列属性:

  • global:
    • boolean类型
    • 表示是否设置了g标记
  • ignoreCase:
    • boolean类型
    • 表示是否设置了i标记
  • unicode:
    • boolean类型
    • 表示是否设置了u标记
  • sticky:
    • boolean类型
    • 表示是否设置了y标记
  • lastIndex:
    • number整数类型
    • 表示在源字符串中下一次搜索开始的位置,始终从0开始
  • multiline:
    • boolean类型
    • 表示是否设置了m标记
  • dotAll:
    • boolean类型
    • 表示是否设置了s标记
  • source:
    • 正则表达式的字面量字符串
    • 没有开头和结尾的斜杠\
  • flags:
    • 正则表达式的标记字符串
    • 始终以字面量而非字符串模式返回
let rel = /[0-9][bc].at/gi;
console.log(rel.global);  //true
console.log(rel.ignoreCase);  //true
console.log(rel.unicode); //false
console.log(rel.sticky);  //false
console.log(rel.lastIndex); //0
console.log(rel.multiline); //false
console.log(rel.dotAll);  //false;
console.log(rel.source);  //[0-9][bc].at
console.log(rel.flags); //gi

5.2.2 RegExp实例方法

5.2.2.1 exec()

matches = pattern.exec(text)
  • 参数(text):要应用模式的字符串
  • 返回值(matches):
    • 找到匹配项:返回第一个匹配信息的数组Array
    • 未找到匹配项:返回null 
    • 额外值
      • index:字符串中匹配模式起始位置
      • input:要查找的字符串
let text =  'dog and cat and bat'
let pattern = /dog( and cat( and bat)?)?/gi
//['dog and cat and bat','and cat and bat','and bat',index:0,input:'dog and cat and bat']
let matches = pattern.exec(text)
  • 设置了全局标记(global = true)的模式:每一次调用exec()方法会返回一个匹配信息
  • global = false的模式:无论对同一个字符串调用多少次exec(),只返回第一个匹配的信息,lastIndex在全局模式下始终不变
let text = 'cat and bat meet at 6 am'
let pattern = /.at/
let reg_g = new RegExp(pattern,'g')
let reg_s = new RegExp(pattern)
console.log(reg_g.exec(text)) //['cat', index:0]
console.log(reg_g.exec(text)) //['bat', index:8]
console.log(reg_g.exec(text)) //[' at', index:16]
console.log(reg_g,exec(text)) //null

console.log(reg_s.exec(text)) //['cat', index:0]
console.log(reg_s,exec(text)) //['cat', index:0]
  • 粘附模式下(sticky = true):每次调用exec()智慧在lastIndex的位置上寻找匹配项,y标记会覆盖g标记
let reg_y = new RegExp(pattern, 'y')
console.log(reg_y.exec(text)) //['cat',index:0]
console.log(reg_y.exec(text)) //null
reg_y.lastIndex = 8
console.log(reg_y.exec(text)) //['bat',index:8]

5.2.2.2 test()

isMatch = reg.test(text)
  • 参数:字符串类型参数
  • 返回值:若字符串参数和模式匹配,返回true,反之为false
let textA = '000-00-0000'
let textB = '000-000-0000'
let pattern = \/d{3}-/d{2}-/d{4}\
console.log(pattern.test(textA)) //true
console.log(pattern.test(textB)) //false

5.2.2.3 other

let pattern = /[bc]at/
console.log(pattern.toLocaleString()) //字面量/[bc]at/
console.log(pattern.toString()) //字面量/[bc]at/
console.log(pattern.valueOf())   //返回正则表达式本身

5.2.3 RegExp构造函数属性

RegExp构造函数具有几个静态属性,适用于作用域中所有的正则表达式,并且户根据最后执行的正则表达式操作而变化

  • input
    • 缩写:$_
    • 功能:返回最后搜索的字符串
  • lastMatch
    • 缩写:$&
    • 功能:返回最后匹配的文本
  • lastParen
    • 缩写:$+
    • 功能:返回最后匹配的捕获组
  • leftContext
    • 缩写:$`
    • 功能:input字符串中出现在lastMatch前面的文本
  • rightContext
    • 缩写:$'
    • 功能:input字符串中出现在lastMatch后面的文本
let text = 'cat and bat fight at night'
let pattern = /(.)at/g
pattern.lastIndex = 8
if (pattern.test(text)) { 
  console.log(RegExp.input)  //cat and bat fight at night
  console.log(RegExp.lastMatch)  //bat
  console.log(RegExp.lastParen) //b
  console.log(RegExp.leftContext) //cat and 
  console.log(RegExp.rightContext)  // fight at night
}
let text = 'cat and bat fight at night'
let pattern = /(.)at/g
pattern.lastIndex = 8
if (pattern.test(text)) { 
  console.log(RegExp["$_"]) //input
  console.log(RegExp["$&"]) //lastMatch
  console.log(RegExp["$+"]) //lastParen
  console.log(RegExp["$`"]) //leftContent
  console.log(RegExp["$'"]) //rightContent
}
  • RegExp.$1~RegExp.$9:最多可以存储9个捕获组的匹配项
let text = 'summer is a short song'
let pattern = /(..)mm(..)/g
if (pattern.test(text)) { 
  console.log(RegExp.$1) //su
  console.log(RegExp.$2) //er
}

5.2.4 模式局限

正则表达式参考网站:Regular-Expressions.info

5.3 原始值包装类型

对于ECMAScript提供的特殊引用类型,即Boolean、Number、String

即原始值包装类型,具有如下特点:

  • 具有引用类型的把部分特点
  • 也具有原始类型对应的特殊行为
  • 当使用到原始值的方法或属性时,后台都会创建一个相应原始包装类新型的对象
let s1 = 'milk and bread'
let s2 = s1.substring(2)  //lk and bread

在通过访问s1创建s2时,对s1是以只读模式(要从内存中读取变量保存的值)访问的。

只读模式访问原始值后台执行步骤:

  1. 创建一个原始类型对应包装类的实例
  2. 调用该实例的指定方法
  3. 销毁该实例
let s1 = new String('milk and bread') //1.创建引用类型包装类实例
let s2 = s1.substring(2)    //2.调用该实例中方法
s1 = null    //3.销毁实例

这种方法可以让原始值拥有对象的行为。

那引用类型和原始值包装类型有什么区别?答:生命周期

  • 引用类型:在new实例化之后,实例会在离开作用域时被销毁
  • 原始值包装类型:在访问它的那行代码执行期间存在,即不能在运行时给原始值添加属性和方法
let s1 = 'hello world'
s1.name = 'Jacob'
console.log(s1.name) //undefined

如上,第三行代码实际上是又以s1为原始值创建了一个新的原始值包装类实例,而此新实例已经不是上一行的那个实例了,因此没有name属性。

  • typeof 原始类型包装类实例 = Object
  • new Object(原始类型) instanceof 原始类型包装类名 = true
  • 原始类型名():转型函数
  • new 原始类型名():构造函数

5.3.1 Boolean

let booleanObject = new Boolean(false)
console.log(boolenaObject && true) //true

对象在布尔表达式里都会被强制转换成true,在JavaScript中一般不建议使用Boolean包装类

5.3.2 Number

继承的方法:

  • valueOf:返回Number对象表示的原始数值
  • toString(基数参数):返回数值字符串
let numberObject = new Number(10)
console.log(numberObject.valueOf()) //10
console.log(numberObject.toLocaleString()) //10
console.log(numberObject.toString()) //10
console.log(numberObject.toString(2)) //1010
console.log(numberObject.toString(8)) //12
console.log(numberObject.toString(16)) //a

Number类型还提供了几个方法:

  • toFixed(保留小数位数):返回包含指定小数点位数(四舍五入原则)的数值字符串
let numA = 10;
let numB = 10.005;
let numC = 10.001;
console.log(numA.toFixed(2)) //10.00
console.log(numB.toFixed(2)) //10.01
console.log(numC.toFixed(2)) //10.00
  • toExponential(位数):科学(指数)计数法,参数表示结果中小数的位数
let num = 12345
console.log(num.toExponential(3)) //1.235e+4
  • toPrecision(数字总位数):根据情况返回最合理的输出结果,参数表示结果中数字的总位数,可用以表示带1-21个小数位的数值,结果根据浏览器而不同
let num = 123
console.log(num.toPrecision(1)) //1e+2
console.log(num.toPrecision(2)) //1.2e+2
console.log(num.toPrecision(3)) //123
console.log(num.toPrecision(4)) //123.0
  • isInteger():ES6新增方法用于辨别一个数值是否保存为整数
console.log(Number.isInteger(10)) //true
console.log(Number.isInteger(10.00))  //true
console.log(Number.isInteger(10.01))  //false
  • isSafeInteger():IEEE754数据格式具有特殊的数据范围,在这个范围内二进制值可以表示一个整数值,对于超出这个范围的数值,IEEE754编码可能将其转换为完全不同的二进制数值,因此可以使用Number.isSafeInteger来确定整数是否在此范围内
    • Number.MIN_SAFE_INTEGER:-2^53+1
    • Number.MAX_SAFE_INTERGER:2^53-1
console.log(Number.MAX_SAFE_INTEGER) //9007199254740991
console.log(Number.MIN_SAFE_INTEGER) //-9007199254740991
console.log(Number.isSafeInteger(10)) //true
console.log(Number.isSafeInteger(Number.MAX_SAFE_INTEGER+1)) //false

5.3.3 String

let stringObject = new String("hello world")

String对象的3个继承方法(都返回对象的原始字符串值):

  • valueOf()
  • toLocaleString()
  • toString()

String的length属性返回字符串包含的字符数量,注意:双字节字符会被当做单字节来计数

console.log(new String('hello world').length)   //11
console.log(new String("你好").length)  //2

5.3.3.1 JavaScript字符

JavaScript字符串有如下组成特点:

  • 由16位码元(code unit)组成,1个字符=16位码元
  • 两种Unicode编码混合策略:
    • UCS-2
    • UTF-16
  • 针对String字符的一些操作方法:
    • charAt(params)
    • charCodeAt(params)
      • params:整数索引
      • return:指定索引位置的字符编码/码元值
    • fromCharCode(char1, char2, ....)
console.log('hello'.charAt(1));  //e
console.log('hello'.charCodeAt(1)) //101
console.log(String.fromCharCode(101,102))  //ef
    • 字符表示范围  
      • U+0000~U+FFFF
      • Unicode
        • 基本多语言平面(BMP):2^16 = 65536个字符
        • 增补字符平面:为了表示更多字符被提出,采用代理对策略,即每个字符使用两个16位码元
        • 码点(code point):Unicode中一个字符的完整标识,16/32位
    • charPointAt(params)
      • params:16位码元索引
      • return:索引上码点
      • 适用字符串:因为增补字符平面的原因,charCodeAt对于包含双码元的字符串返回的码元存在索引错位的情况(实际上浏览器已经可以正确解析代理对了),因此一般使用charPointAt来解析同时包含单双码元/代理对字符的字符串。
    • fromCodePoint(char1, char2, ...):对应charPointAt的通过码元获得字符方法
let message = '0☺2345';
//charCodeAt对代理字符码元的正确索引是由于浏览器的修正
//codePointAt则能够正确的获取单双码元混存字符串索引位置的码元值
console.log(message.charCodeAt(1),message.codePointAt(1)); // 9786 9786
console.log(String.fromCharCode(9876),String.fromCodePoint(9876)) // ⚔ ⚔

5.3.3.2 normalize()方法

Unicode字符编码方式不一,有的字符可以同时通过BMP和代理对的方式实现,

console.log(String.fromCharCode(0x00c5)) //Å
console.log(String.fromCharCode(0x212B)); //Å
console.log(String.fromCharCode(0x0041)); //A
console.log(String.fromCharCode(0x30A));//̊
console.log(String.fromCharCode(0x0041,0x30A)) //Å
console.log('Å'.charCodeAt(0)) //8491

需要注意的是:使用不同的方式实现同样的字符,在使用比较操作符时一般会得到不相等的结果,

由于比较操作符是对字符编码进行比较的

let str1 = String.fromCharCode(0x00c5)         // Å
let str2 = String.fromCharCode(0x212B)         // Å
let str3 = String.fromCharCode(0x0041, 0x30A); // Å
console.log(str1 == str2); // false
console.log(str2 == str3); // false
console.log(str3 == str2); // false

Unicode提出了4种规范化形式,将用于将多种码元表示形式的字符规范化为一致格式(参考UAX15):

  • NFD:Normalization Form D
  • NFC:Normalization Form C
  • NFKD:Normalization Form KD
  • NFKC:Normalization Form KC

normalize(规范化标准):一般情况下是为了方便对不同编码格式的字符串进行编码统一比较的

// U+00c5: NFC/NFKC
console.log(str1 == str1.normalize('NFD'));  // false
console.log(str1 == str1.normalize('NFC'));  // true
console.log(str1 == str1.normalize('NFKD')); // false
console.log(str1 == str1.normalize('NFKC')); // true

// U+212B: 未规范化
console.log(str2 == str2.normalize('NFD'));  // false
console.log(str2 == str2.normalize('NFC'));  // false
console.log(str2 == str2.normalize('NFKD')); // false
console.log(str2 == str2.normalize('NFKC')); // false

// U+0041/U+030A: 对U+212B进行NFD/NFKD进行规范化的结果
console.log(str3 == str3.normalize('NFD'));  // true
console.log(str3 == str3.normalize('NFC'));  // false
console.log(str3 == str3.normalize('NFKD')); // true
console.log(str3 == str3.normalize('NFKC')); // false

console.log(str1.normalize('NFD') == str2.normalize('NFD'));   // true
console.log(str1.normalize('NFC') == str3.normalize('NFC'));   // true
console.log(str2.normalize('NFKC') == str3.normalize('NFKC')); // true

5.3.3.3 字符串操作方法

  • 字符串拼接方法
    • cancat(params1[, params2, params3, ...]) 
      • 功能:单个/多个字符串拼接
    • +加号操作符
let firstString = 'Hello '
let result = firstString.concat('World'); // Hello World
let result2 = firstString.concat('World',' !') // Hello Wolrd !
console.log("Hello " + "World"); //Hello World
  •  提取子字符串方法(返回值:新字符串,第二个参数不传意味着提取到字符串尾部):
    • slice(subStart[, subEnd]):第二个参数表示结尾索引
      • 负值参数:字符串长度+负参数值
    • substr(subStart[, strNumber]):第二个参数表示提取矢量
      • subStart负值:字符串长度+负值参数
      • strNumber:转换为0
    • substring(subStart[, subEnd]):第二个参数表示结尾索引
      • 负值参数:转换为0
let message = 'Hello World';
console.log(message.slice(6)); // World
console.log(message.slice(0, 3)); // Hel
console.log(message.substr(1)); // ello World
console.log(message.substr(1, 4)) //ello
console.log(message.substring(7)); // orld
console.log(message.substring(7,9)) // or

5.3.3.4 字符串位置方法

定位子字符串有2种方式,都可以接受2个参数,并返回为子字符串位置,没找到时返回-1,两种方法的区别如下:

  • indexOf(subStr[ ,startIndex ])
    • 从字符串开头寻找子字符串
    • 搜索范围在0/startIndex → string.length
  • lastIndexOf(subStr[ ,endIndex])
    • 从字符串结尾开始寻找子字符串
    • 搜索范围在0→endIndex
let message = 'abcabcabcbac'
let pos_indexOf = new Array()
let pos_lastIndexOf = new Array()
let pos_index = message.indexOf('a')
let pos_lastIndex = message.lastIndexOf('a')
while(pos_index > -1){
    pos_indexOf.push(pos_index)
    pos_index = message.indexOf('a', pos_index+1)
}
while(pos_lastIndex > -1){
    pos_lastIndexOf.push(pos_lastIndex)
    //注意:lastIndexOf的第二个定位index参数为负值时,会被转换为0
    if(pos_lastIndex === 0){ break; } 
    pos_lastIndex = message.lastIndexOf('a',pos_lastIndex-1)
}
console.log(pos_indexOf) //[0,3,6,10]
console.log(pos_lastIndexOf) //[10,6,3,0]

5.3.3.5 字符串包含方法

ECMAScript 6有3个判断是否包含另一个字符串的方法

  • startsWith(subStr[ ,startIndex])
    • 检查开始于索引0/startIndex的匹配项
  • endsWith(subStr[ ,endIndex])
    • 检查以endIndex/string.length结尾的匹配项
  • includes(subStr[ ,startIndex])
    • 检查从0/startIndex开始的整个字符串
let message = 'foobarbaz'
console.log(message.startsWith("foo"))  //true
console.log(message.startsWith('baz'))  //false
console.log(message.startsWith('foo',1)) // false
console.log("************************")

console.log(message.endsWith('baz'));  //true
console.log(message.endsWith('bar')); //false
console.log(message.endsWith('baz',5)) //false
console.log("************************")

console.log(message.includes('foo')); // true
console.log(message.includes('bar'))  // true
console.log(message.includes('baz',1)) // true

5.3.3.6 trim()方法

以下3中方法都不会改变原始字符串

  • trim() 删除两侧空格
  • trimLeft() 删除左侧空格
  • trimRight() 删除右侧空格
let stringValue = ' hello world '
console.log(stringValue.trim()) //hello world
console.log(stringValue.trimLeft()) //hello world
console.log(stringValue.trimRight()) // hello world
console.log(stringValue); // hello world 

5.3.3.7 repeat()方法

repeat(repeatNumber)

repeatNumber:Number整数,表示字符串复制次数

return:字符串拼接结果

let stringValue = 'na ';
//na na na na na na na na na na na na na na na na batman
console.log(stringValue.repeat(16) + "batman")

5.3.3.8 padStart()和padEnd()方法

  • padStart( length, padString)
  • padEnd ( length, padString)
    • length:指定长度 Number
      • 当length<=padString.length时,返回原始字符串
    • padString:填充字符 String
      • 可以为单字符,也可以为字符串
let stringValue = 'foo'
console.log(stringValue.padStart(6)) //   foo
console.log(stringValue.padStart(9,'*'))    //******foo
console.log(stringValue.padStart(8,'bar')) //barbafoo
console.log(stringValue.padStart(2)); //foo
console.log(stringValue.padEnd(6)); //foo
console.log(stringValue.padEnd(9,'*'))  //foo******
console.log(stringValue.padEnd(8,'bar')) //foobarba
console.log(stringValue.padEnd(2)) //foo

5.3.3.9 字符串迭代与解构

字符串原型上暴露了一个@@iterator方法,用于迭代字符串中的每个字符

let message = 'abc'
let stringIterator = message[Symbol.iterator]();
console.log(stringIterator.next());  //{value:'a',done:false}
console.log(stringIterator.next());  //{value:'b',done:false}
console.log(stringIterator.next());  //{value:'c',done:false}
console.log(stringIterator.next());  //{value:undefined,done:true}
for(const c of 'abc'){
    console.log(c); //a b c
}
console.log([...'abc']) //['a','b','c ']

5.3.3.10 字符串大小写转换

  • toLowerCase()
  • toUpperCase()
    • 对字符进行大/小写转换
  • toLocaleLowerCase()
  • toLocaleUpperCase()
    • 功能和toLower/UpperCase一样,旨在基于应用特殊规则的特定地区实现正确转换
let stringValue = 'Hello World'
console.log(stringValue.toUpperCase()) //HELLO WORLD
console.log(stringValue.toLocaleUpperCase()) //HELLO WORLD
console.log(stringValue.toLowerCase()) //hello world
console.log(stringValue.toLocaleLowerCase()) //hello world

5.3.3.11 字符串模式匹配方法

  • match(param)
    • 和RegExp对象的exec()方法相同
    • param:正则表达式字符串/RegExp对象
    • return:Array,和RegExp对象的exec()方法返回数组一样
      • Array[0]:与整个模式匹配的字符串
      • 其余元素:表达式中的捕获组匹配的字符串
let text = "cat, bat, sat, fat"
let pattern = /.at/
let matches = text.match(pattern);
console.log(matches.index) //0
console.log(matches[0]) //"cat"
console.log(pattern.lastIndex) //0
  • search(param)
    • param:正则表达式字符串/RegExp对象
    • return:模式第一个匹配的位置索引/没找到返回-1
    • 从字符串开头向后匹配
let text = 'cat, bat, sat, fat'
let pos = text.search(/at/);
console.log(pos); //1
  • replace( oldString, newString)
    • oldString:RegExp对象/字符串(字符串不会被转换为正则表达式)
      • 当oldString为字符串时,只会替换第一个子字符串
    • newString:字符串/函数
      • 当newString为字符串时,可以插入几个特殊的字符序列
      • 当newString为函数时,该函数有如下特性
        • param1:与整个模式匹配的字符串
        • param2:匹配项在字符串中的开始位置
        • param3:整个字符串
        • return:String,表示匹配项的替换格式

let text = 'cat, bat, sat, fat';
let 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
result = text.replace(/(.at)/g,"word($1)");
console.log(result); //word(cat), word(bat), word(sat), word(fat)
function htmlEscape(text){
    return text.replace(/[<>"&]/g, function(match, pos, originalText){
        switch(match){
            case '<': return "&lt";
            case '>': return "&gt";
            case '&': return '&amp;';
            case '\"':return '&quot';
        }
    })
}
//&ltp class=&quotgreeting&quot&gtHello world!&lt/p&gt
console.log(htmlEscape('<p class="greeting">Hello world!</p>'))
  • split(splitString[ ,arrayLength])
    • splitString:String/RegExp对象,分隔符
    • arrayLength:Number,数组大小,确保返回的数组不会超过指定大小
let colorText = "red,blue,green,yellow"
console.log(colorText.split(',')) //['red','blue','green','yellow']
console.log(colorText.split(',',2)) //['red','blue']
console.log(colorText.split(/[^,]+/)) //["",",",",",",",""]

5.3.3.12 localeCompare()方法

localeCompare(param)

  • param:String,参数字符串,使用字母表顺序进行比较
  • return
    • 负值:比较字符串 前于 参数字符串,通常是-1
    • 0:比较字符串 = 参数字符串
    • 正值:比较字符串 后于 参数字符串,通常是1
let stringValue = 'yellow'
console.log(stringValue.localeCompare("brick")); //1
console.log(stringValue.localeCompare('yellow')) //0
console.log(stringValue.localeCompare('zoo'))    //-1
  • 注意:
    • 该方法返回的具体值可能因具体实现而异,因此在实际用于判断字符串顺序时,使用函数包装的形式
    • function determineOrder(compareValue, paramValue){
          let result = compareValue.localeCompare(paramValue)
          if(result<0){
              console.log(`The string '${compareValue}' comes before the string '${paramValue}'.`);
          }else if(result>0){
              console.log(`The string '${compareValue}' comes after the string '${paramValue}'.`)
          }else{
              console.log(`The string '${compareValue}' is euqal to the string '${paramValue}'.`)
          }
      }
      //The string 'yellow' comes after the string 'brick'.
      determineOrder('yellow','brick');
      //The string 'yellow' is euqal to the string 'yellow'.
      determineOrder('yellow','yellow')
      //The string 'yellow' comes before the string 'zoo'.
      determineOrder('yellow','zoo')
    • 之所以方法名带locale(地方)标记,是由于该方法比较字符串的规则会受所在地区的影响

5.3.3.13 HTML方法 

早期浏览器增加了部分辅助生成HTML标签的方法,不过这些方法因为生成结果并非语义化标记,已经没有人使用了

5.4 单例内置对象

内置对象定义:任何由ECMAScript实现提供、与宿主环境无关,并在ECMAScript程序开始执行时就存在的对象。

5.4.1 Gloabl

Global对象具有如下特性:

  • 不会被代码显式访问
  • 是一种兜底对象,针对不属于任何对象的属性和方法
  • 在全局作用域中定义的变量和函数都会变成Global对象的属性(即不存在真正意义上的全局作用域)

5.4.1.1 URI(Uniform Resource Identifier 统一资源标识符)编码方法

  • URI编码方法
    • encodeURI
      • 不会编码属于URL组件的特殊字符
    • encodeURIComponent
      • 会编码所有非标准字符
    • 编码方式:对于URI中无效的字符,使用UTF-8编码进行替换
    • 实际使用:编码查询字符串参数(encodeURIComponent)比编码基准URI(encodeURI)次数更多
  • URI解码方法
    • decodeURI
      • 相对encodeURI()编码过的字符解码
    • decodeURIComponent
      • 相对encodeURIComponent()编码过的字符解码
  • 已经废弃的方法:escape()/unescape()
    • 废弃原因:这两种方法只能正确编码ASCII字符,而URI方法可以对所有Unicde字符进行编码
let uri = 'http://www.baidu.com/illegal value.js#start'
let encodeURI_res = encodeURI(uri)
console.log(encodeURI_res);//http://www.baidu.com/illegal%20value.js#start
console.log(decodeURI(encodeURI_res))   //http://www.baidu.com/illegal value.js#start
console.log(decodeURIComponent(encodeURI_res)) //http://www.baidu.com/illegal value.js#start
let encodeURIComponent_res = encodeURIComponent(uri)
console.log(encodeURIComponent_res);//http%3A%2F%2Fwww.baidu.com%2Fillegal%20value.js%23start
console.log(decodeURIComponent(encodeURIComponent_res))//http://www.baidu.com/illegal value.js#start
console.log(decodeURI(encodeURIComponent_res)) //http%3A%2F%2Fwww.baidu.com%2Fillegal value.js%23start

5.4.1.2 eval()方法

eval(param)方法的功能十分强大,其本身就相当于一个完整的ECMAScript解释器

param:要执行的ECMAScript字符串

eval("console.log('hello')")  //hello

eval方法具有如下特性:

  • eval()的参数会被解释成实际的ECMAScript语句并插入到调用位置
  • eval()执行的代码的作用域在调用时所在的上下文,具有相同的作用域链,因此在eval中可以访问到上下文定义的函数和变量
let msg = 'hello world'
eval('console.log(msg)') //hello world
  • 可以在eval()内部定义一个函数或变量,在外部代码调用
eval('function sayHello(){console.log("hello")}')
sayHello(); //hello
  • 通过eval()定义的变量和函数都不会被提升,因为它们只有在eval()执行的时候才会被创建
eval('let msg = "hello wolrd"');
console.log(msg); //referenceError
  • 在严格模式下,eval内部创建的变量和函数无法被外部访问,且赋值给eval也会导致错误
'use strict'
eval('function sayHello(){console.log("Hello")}');
sayHello(); //ReferenceError: sayHello is not defined
  • 使用eval()必须慎重,特别是在解释用户输入的内容时,极有可能收到XSS(跨站脚本攻击)

5.4.1.3 Global对象属性

所有原生引用类型和一些特殊值都是Global对象的属性,下面列出了所有这些属性:

5.4.1.4 window对象

有两种方式可以获取到Global对象

  • window对象:即Global对象的代理
  • 创建一个立即调用的函数表达式,返回this值
let global = function(){
    return this;
}()

5.4.2 Math

使用Math对象提供的计算的优劣:

  • 速度更快,因为Math对象的计算使用了JavaScript引擎中更高效的实现和处理器命令
  • 精度差异,精度会因浏览器、操作系统、指令集、硬件而异

5.4.2.1 Math对象属性

Math对象中保存有数学中的一些特殊值

console.log(Math.E) //2.718281828459045
console.log(Math.LN10) //2.302585092994046
console.log(Math.LN2) //0.6931471805599453
console.log(Math.LOG2E) //1.4426950408889634
console.log(Math.LOG10E) //0.4342944819032518
console.log(Math.PI) //3.141592653589793
console.log(Math.SQRT1_2) //0.7071067811865476
console.log(Math.SQRT2) //1.4142135623730951

5.4.2.2 min()和max()方法

  • min( num1, num2, num3, ...) 确定一组数中的最小值
  • max( num1, num2, num3, ...) 确定一组数中的最大值
let numArr = [ 3, 54, 32, 16]
let max = Math.max(...numArr);
console.log(max) //54
let min = Math.min(...numArr)
console.log(min) //3

5.4.2.3 舍入方法

  • Math.ceil()
    • 始终向上舍入为最接近的整数
console.log(Math.ceil(25.9)) //26
console.log(Math.ceil(25.5)) //26
console.log(Math.ceil(25.1)) //26
  • Math.floor()
    • 始终向下舍入为最接近的整数
console.log(Math.floor(25.9)) //25
console.log(Math.floor(25.5)) //25
console.log(Math.floor(25.1)) //25
  • Math.round()
    • 执行四舍五入
console.log(Math.round(25.9)) //26
console.log(Math.round(25.5)) //26
console.log(Math.round(25.1)) //25
  • Math.fround()
    • 返回数值最接近的单精度(32位)浮点值表示
console.log(Math.fround(25.9)) //25.899999618530273
console.log(Math.fround(25.5)) //25.5
console.log(Math.fround(25.1)) //25.100000381469727

5.4.2.4 random()方法

返回值:[0,1)的随机数

可以使用如下方式来生成任意范围内的连续整数随机数

total_number_of_choices:可生成随机数的个数

first_possible_value:能生成的最小随机数

number = Math.floor(Math.random() * total_number_of_choices + first_possible_value)
function selectFrom(lowerValue, upperValue){
    let choice = upperValue - lowerValue+1 ;
    return Math.floor(Math.random() * choice +lowerValue)
}
let num = selectFrom(2,10)
console.log(num) //4
let colors = ['red' ,'green' ,'blur', 'yellow', 'black']
console.log(colors[selectFrom(0,colors.length)]) //black

注意:要获取随机数据也可以使用window.crypto.getRandomValues(),具有更高的不确定性

var array = new Uint8Array(10)
window.crypto.getRandomValues(array);
console.log("Your lucky numbers:")
for(var i = 0;i<array.length;i++){
    console.log(array[i]) //227 251 35 47 4 234 246 142 227 209
}

5.4.2.5 其他方法

注意:Math中方法计算精度还是会因浏览器的实现而异

posted @ 2022-09-02 09:09  Electric-Duck  阅读(63)  评论(0)    收藏  举报