JavaScript - 原始值包装类型

Posted 友人A ㅤ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript - 原始值包装类型相关的知识,希望对你有一定的参考价值。

为了方便操作原始值,ECMAScript提供了3种特殊的引用类型:Boolean、Number和String


引用类型与原始值包装类型的主要区别在于对象的生命周期

在通过new实例化引用类型后,得到的实例会在离开作用域时被销毁,而自动创建的原始值包装对象则只存在于访问它的那行代码执行期间。这意味着不能在运行时给原始值添加属性和方法:

let s1 = "aaa";
// 代码运行时会临时创建一个String对象
s1.color = "red";
// 代码执行时,这个对象已经被销毁了。即这里创建了自己的String对象,但这个对象没有color属性
comsole.log(s1.color); // undefined

1. Boolean

Boolean是对应布尔值的引用类型。要创建一个Boolean对象,就使用Boolean构造函数并传入true或false。

let booleanObject = new Boolean(true);

Boolean对象在ECMAScript中用的很少,因为容易引起误会:

// 创建一个值为false的Boolean对象
let falseObject = new Boolean(false);
console.log(falseObject); // [Boolean: false]

// 表达式是对falseObject对象而不是对它表示的值(false)求值。
// 而所有对象在布尔表达式中都会自动转换为true,因此falseObject在这个表达式里实际上表示一个true值
let result = falseObject && true;
console.log(result); // true

let falseValue = false;
result = falseValue && true;
console.log(result); // false

2. Number

要创建一个Number对象,就使用Number构造函数并传入一个数值:

let numberObject = new Number(10);

toString()方法可选地接收一个表示基数的参数,并返回相应基数形式的数值字符串:

let num = 10;
console.log(num.toString()); // "10"
console.log(num.toString(2)); // "1010"
console.log(num.toString(8)); // "12"
console.log(num.toString(10)); // "10"
console.log(num.toString(16)); // "a"

toFixed()方法返回包含指定小数点位数的数值字符串:

let num = 10;
console.log(num.toFixed(2)); // "10.00"

// 如果数值本身的小数位超过了参数指定的位数,则四舍五入到最接近的小数位
let num1 = 10.006;
console.log(num1.toFixed(2)); // "10.01"

toExponential()返回以科学记数法(也称为指数记数法)表示的数值字符串:

let num = 10000;
// 参数表示结果中小数的位数
console.log(num.toExponential(2)); // "1.00e+4"

toPrecision()方法会根据情况返回最合理的输出结果,可能是固定长度,也可能是科学记数法形式:

let num = 99;
// 参数表示结果中数字的总位数

// "1e+2"就是100。因为99不能只用1位数字来精确表示,所以这个方法就将它舍入为100
console.log(num.toPrecision(1)); // "1e+2"
console.log(num.toPrecision(2)); // "99"
console.log(num.toPrecision(3)); // "99.0"

Number.isInteger()方法用于辨别一个数值是否保存为整数。因为小数位的0可能会让人误以为数值是一个浮点值:

console.log(Number.isInteger(1)); // true
console.log(Number.isInteger(1.00)); // true
console.log(Number.isInteger(1.01)); // false

Number.isSafeInteger()方法可以鉴别整数是否在数值范围内:

  • IEEE 754数值格式有一个特殊的数值范围,在这个范围内二进制值可以表示一个整数值
  • 数值范围:Number.MIN_SAFE_INTEGER(-253+ 1)到Number.MAX_SAFE_INTEGER(253-1)
  • 对超出这个范围的数值,即使尝试保存为整数,IEEE 754编码格式也意味着二进制值可能会表示一个完全不同的数值
console.log(Number.isSafeInteger(-1 * (2 ** 53))); // false
console.log(Number.isSafeInteger(-1 * (2 ** 53) + 1)); // true
console.log(Number.isSafeInteger(2 ** 53)); // false
console.log(Number.isSafeInteger((2 ** 53) - 1)); // true

3. String

要创建一个String对象,使用String构造函数并传入一个数值:

let stringObject = new String("aaa");


3.1 javascript字符

JavaScript字符串由16位码元组成。

1个字符 = 16位码元。


每个String对象都有一个length属性,表示字符串中字符的数量:

let stringObject = new String("aaa");
console.log(stringObject.length); // 3

charAt()方法返回给定索引位置的字符,由传给方法的整数参数指定:

let message = "abcde";
console.log(message.charAt(2)); // "c"

charCodeAt()方法可以查看指定码元的字符编码。这个方法返回指定索引位置的码元值,索引以整数指定:

let message = "abcde";
console.log(message.charCodeAt(2)); // 99

// Unicode中c的编码是 U+0063
console.log(99 === 0x63); // true

JavaScript字符串使用了两种Unicode编码混合的策略:UCS-2和UTF-16。对于可以采用16位编码的字符(U+0000~U+FFFF),这两种编码实际上是一样的。


fromCharCode()方法用于根据给定的UTF-16码元创建字符串中的字符。这个方法可以接受任意多个数值,并返回将所有数值对应的字符拼接起来的字符串:

console.log(String.fromCharCode(0x61, 0x62, 0x63, 0x64, 0x65)); // "abcde"
// 等价于
console.log(String.fromCharCode(97, 98, 99, 100, 101)); // "abcde"

注意:对于U+0000~U+FFFF范围内的字符,length、charAt()、charCodeAt()和fromCharCode()返回的结果都跟预期是一样的。


但是,16位只能唯一表示65536个字符,这个字符集在Unicode中称为基本多语言平面(BMP) 。为了表示更多字符,Unicode采用了一个策略,即每个字符使用另外16位去选择一个增补平面。这种每个字符使用两个16位码元的策略称为代理对

// 笑脸表情符号的编码是 U+1F60A === 126522
let message = "ab😊de"
console.log(message.charAt(2)); // �

// 索引2和索引3对应的码元应该被看成一个代理对,只对应一个字符,它实际上是基于提供的二进制表示直接组合成字符串
console.log(message.charCodeAt(2)); // 55357
console.log(message.charCodeAt(3)); // 56842

console.log(String.fromCodePoint(0x1F60A)); // 😊
// 浏览器可以正确解析代理对(由两个码元构成),并正确地将其识别为一个Unicode笑脸字符
console.log(String.fromCodePoint(97, 98, 55357, 56842, 100, 101)); // "ab😊de"


3.2 normalize()方法

可以检查字符串是否已经规范化。


存在问题:
某些Unicode字符可以有多种编码方式,但比较操作符不在乎字符看起来是什么样的:

let a1 = String.fromCharCode(0x00C5),
    a2 = String.fromCharCode(0x212B),
    a3 = String.fromCharCode(0x0041, 0x030A);
console.log(a1, a2, a3); // Å Å Å

// 比较操作符不在乎字符看起来是什么样的,因此这3个字符互不相等
console.log(a1 === a2); // false
console.log(a1 === a3); // false
console.log(a2 === a3); // false

解决:
Unicode提供了4种规范化形式,可以将类似上面的字符规范化为一致的格式,无论底层字符的代码是什么。

  • NFD
  • NFC
  • NFKD
  • NFKC

可以使用normalize()方法对字符串应用上述规范化形式,使用时需要传入表示哪种形式的字符串:“NFD”、“NFC”、“NFKD"或"NFKC”。

let a1 = String.fromCharCode(0x00C5),
    a2 = String.fromCharCode(0x212B),
    a3 = String.fromCharCode(0x0041, 0x030A);
console.log(a1, a2, a3); // Å Å Å

// 通过比较字符串与其调用normalize()的返回值,就可以知道该字符串是否已经规范化
console.log(a1 === a1.normalize("NFD")); // false
console.log(a1 === a1.normalize("NFC")); // true
console.log(a1 === a1.normalize("NFKD")); // false 
console.log(a1 === a1.normalize("NFKC")); // true

console.log(a2 === a2.normalize("NFD")); // false 
console.log(a2 === a2.normalize("NFC")); // false 
console.log(a2 === a2.normalize("NFKD")); // false 
console.log(a2 === a2.normalize("NFKC")); // false 

console.log(a3 === a3.normalize("NFD")); // true
console.log(a3 === a3.normalize("NFC")); // false
console.log(a3 === a3.normalize("NFKD")); // true
console.log(a3 === a3.normalize("NFKC")); // false

// 选择同一种规范化形式可以让比较操作符返回正确的结果
console.log(a1.normalize("NFD") === a2.normalize("NFD")); // true

3.3 字符串操作方法

concat()用于将一个或多个字符串拼接成一个新字符串

let stringValue = 'aaa';
let result = stringValue.concat(' bbb');
console.log(result); // aaa bbb

// 接收任意多个参数,因此可以一次性拼接多个字符串
let result1 = stringValue.concat(' bbb', ' ccc');
console.log(result1); // aaa bbb ccc

提取子字符串的方法:slice()substr()substring()

这3个方法都返回调用它们的字符串的一个子字符串,而且都接收一或两个参数。第一个参数表示子字符串开始的位置,第二个参数表示子字符串结束的位置。

let stringValue = 'abc def ghi';

console.log(stringValue.slice(5)); // ef ghi
console.log(stringValue.substring(5)); // ef ghi
console.log(stringValue.substr(5)); // ef ghi


console.log(stringValue.slice(5, 9)); // ef g
console.log(stringValue.substring(5, 9)); // ef g
// substr()的第二个参数表示返回的字符数
console.log(stringValue.substr(5, 9)); // ef ghi

// slice()方法将所有负值参数都当成字符串长度加上负参数值
console.log(stringValue.slice(-9)); // c def ghi

// substring()方法会将所有负参数值都转换为0
console.log(stringValue.substring(-8)); // abc def ghi

// substr()方法将第一个负参数值当成字符串长度加上该值,将第二个负参数值转换为0
console.log(stringValue.substr(-8)); //  def ghi


console.log(stringValue.slice(5, -5)); // e
console.log(stringValue.substring(5, -5)); // abc d
console.log(stringValue.substr(5, -5)); // ""

3.4 字符串位置方法

在字符串中定位子字符串:indexOf()lastIndexOf()。这两个方法从字符串中搜索传入的字符串,并返回位置(如果没找到,则返回-1):

  • indexOf()方法从字符串开头开始查找子字符串
  • lastIndexOf()方法从字符串末尾开始查找子字符串
let stringValue = "hello world";

console.log(stringValue.indexOf('o')); // 4
console.log(stringValue.lastIndexOf('o')); // 7

console.log(stringValue.indexOf('m')); // -1
console.log(stringValue.lastIndexOf('m')); // -1

// 这两个方法都可以接收可选的第二个参数,表示开始搜索的位置,忽略该位置之前的字符
console.log(stringValue.indexOf('o', 6)); // 7
console.log(stringValue.lastIndexOf('o', 6)); // 4

寻找字符串中所有的目标子字符串:

let stringValue = "bat cat mat lap rap";
let positions = new Array();
let pos = stringValue.indexOf('a');

while (pos > -1) {
    positions.push(pos);
    pos = stringValue.indexOf('a', pos + 1);
}
console.log(positions); // [ 1, 5, 9, 13, 17 ]

3.5 字符串包含方法

ECMAScript 6增加了3个用于判断字符串中是否包含另一个字符串的方法:

  • startsWith()检查开始于索引0的匹配项
  • endsWith()检查开始于索引(string.length -substring.length)的匹配项
  • includes()检查整个字符串
let message = 'foobarbaz';

console.log(message.startsWith('foo')); // true
console.log(message.startsWith('bar')); // false

console.log(message.endsWith('baz')); // true
console.log(message.endsWith('bar')); // false

console.log(message.includes('bar')); // true
console.log(message.includes('aaa')); // false
  • startsWith()和includes() 方法接收可选的第二个参数,表示开始搜索的位置

  • endsWith() 方法接收可选的第二个参数,表示应该当作字符串末尾的位置


3.6 trim()方法

trim()方法会创建字符串的一个副本,删除前、后所有空格符,再返回结果

trimLeft()和trimRight()方法分别用于从字符串开始和末尾清理空格符

let message = '   aaa bbb   ';
let trimString = message.trim();

console.log(message); // "   aaa bbb   "
console.log(trimString); // "aaa bbb"

3.7 repeat()方法

接收一个整数参数,表示要将字符串复制多少次,然后返回拼接所有副本后的结果

let stringValue = 'aaa ';
console.log(stringValue.repeat(6) + 'bbb'); // aaa aaa aaa aaa aaa aaa bbb

3.8 padStart()padEnd()方法

padStart()和padEnd()方法会复制字符串,如果小于指定长度,则在相应一边填充字符,直至满足长度条件。

这两个方法的第一个参数是长度,第二个参数是可选的填充字符串,默认为空格。

let stringValue = 'aaa';

console.log(stringValue.padStart(6)); // "   aaa"
console.log(stringValue.padStart(6, ".")); // "...aaa"

console.log(stringValue.padEnd(6)); // "aaa   "
console.log(stringValue.padEnd(6, ".")); // "aaa..."

可选的第二个参数并不限于一个字符。如果提供了多个字符的字符串,则会将其拼接并截断以匹配指定长度。此外,如果长度小于或等于字符串长度,则会返回原始字符串


3.9 字符串迭代与解构

let message = 'abcde';
console.log([...message]); // [ 'a', 'b', 'c', 'd', 'e' ]

3.10 字符串大小写转换

let message = 'abcde';

console.log(message.toUpperCase()); // "ABCDE"
console.log(message.toLowerCase()); // "abcde"

// 基于特定地区实现,如在少数语言中(如土耳其语), Unicode大小写转换需应用特殊规则,要使用地区特定的方法才能实现正确转换
console.log(message.

以上是关于JavaScript - 原始值包装类型的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 包装类

JS内置类型

每日分享!~ vue JavaScript中为什么可以读取到字符串的长度!(包装对象)

笔记:DateRegExp原始值包装类单例内置对象

你根本不会Javascript——类型值和变量

JS中数据类型原始数据内置对象包装类型对象typeof