JavaScript中String详解

javaScript 中的 String 对象用来表示和操作字符序列。字符串对于保存可以以文本形式表示的数据。并且有一些常用的字符串方法,用来查看字符串的长度,用来寻找字符串的位置等。

创建字符串的方法。

  1. 可以通过使用 String() 构造函数的将字符串创建出来字符串原语和字符串对象。 

const string1 = "This is a string";

typeof string1   // string 

const objStr = new String('This is a obj string');

typeof objStr.  // object 

字符串可以使用单引号和双引号两种,他们的处理方式相同,或者使用反引号字符`,来指定一个模版字符串(可用于插入表达式)。

在大多情况下,字符串原语和字符串对象是可以互相使用的。在使用一个字符串的对象的时候,JavaScript 自动将原始类型转换为 String 对象,因此可以使 String 基本类型调用字符串的方法。JavaScript 将自动包装字符串原始并调用方法或执行属性查找。并且在 String 期望是使用原始字符串时,会自动的转换为对应的原始类型。这个过程是不需要我们手动来进行的,是内部做了处理。

let string1 = 'foo';

let objStr = new String(string1);

objStr.valueOf();  // 调用 valueOf 方法, 可以手动将包装对象转换为对应的基础类型。

我们如何访问一个字符串。

1. 我们可以使用字符串中的方法, charAt() 方法(后文中会详细介绍)。

'cat'.charAt(1);   // ‘a’  就是访问 cat 字符串的第一个字符。

2. 我们可以使用另一种在 ECMAScript 5 中引入,将字符串视为类数组对象,其中单个字符对应于数字索引。

'cat'[1];   // ‘a’  通过下标来访问字符串的第一位字符。

转义序列。

\0 空字符(U+0000 NULL)
\' 单引号 (U+0027 撇号)
\" 双引号(U+0022 引号)
\\ 反斜杠 (U+005C REVERSE SOLIDUS)
\n 换行符(U+000A 换行符;LF)
\r 回车(U+000D 回车;CR)
\v 垂直制表符(U+000B 行制表)
\t 选项卡(U+0009 字符列表)
\b 退格(U+0008 退格)
\f 换页 (U+000C 换页)
\uXXXX...其中XXXX正好是范围内的 4 个十六进制数字0000– FFFF; 例如,\u000A\n(LINE FEED)相同;\u0021是“ ! U+0000和之间的 Unicode 代码点U+FFFF(Unicode 基本多语言平面)
\u{X}… \u{XXXXXX}…其中XXXXXXX是范围内的 1–6 个十六进制数字0– 10FFFF;例如,\u{A}\n(LINE FEED)相同;\u{21}是“ ! U+0000和之间的 Unicode 代码点U+10FFFF(整个 Unicode)
\xXX...其中XX正好是范围内的 2 个十六进制数字00– FF; 例如,\x0A\n(LINE FEED)相同;\x21是“ ! U+0000和之间的 Unicode 代码点U+00FF(Basic Latin 和 Latin-1 Supplement 块;等效于 ISO-8859-1)

长字符串

当我们遇到很长的字符串时,你希望可以转换为多行,并且可以正常运行,还可以随时换行。

1. 我们可以使用 + 运算符来拼接到一起,来展示我们的字符串。

let longString = "This is a very long string which needs " +
                 "to wrap across multiple lines because " +
                 "otherwise my code is unreadable."

2. 我们可以使用 \ 来在每一行最后来指示在下一行继续。

let longString = "This is a very long string which needs \
to wrap across multiple lines because \
otherwise my code is unreadable."

 3. 下文中还会提到一种更加好的一种表示方法。

在讲解属性和方法之前,让我们先抛析一下 String() 构造函数。

  1. 当与 new 声明, 参数可以是任意类型,最终生成一个字符串对象。
  2. 当不语 new 一起连用,参数可以是任意类型,把任意参数转换为一个字符串值返回。
String() 转换为
字符串 返回字符串
数字 返回字符串形式number
null 'null'
undefined 'undefined'
true 'true'
false 'false'

 如果是对象的话,传入 string 上下文,会先调用 toString(),如果得到基本类型,就直接返回,否则就继续调用 valueOf(),如果得到基本类型,就返回,否则就抛出错误。

静态方法。

1. String.fromCharCode

fromCharCode() 方法可以将字符编码转换为字符串。也算是一种定义字符串的形式。参数可以是任意个,UTF-16编码单元的数字序列。范围在 0~65535(是Unicode 的基本平面的码点),十六进制表示是 0x0000 ~ 0xFFFF,也可以用转义序列 \u0000 ~ \uFFFF 来表示。如果数字大于 0xFFFF,就会被剪短,不做有效检查。

String.fromCharCode(65, 66, 67); // 'ABC'
String.fromCharCode(0x2014); // '—'
String.fromCharCode(0x12014);  // '—' // 超出范围,1会被截掉,剩下 0x2014
String.fromCharCode(8212);   // '—'  这就是在 0 ~ 65535之间的十进制,转为16进制0x2014

在UTF-16这篇文章中,我们将结果关于 Unicode 的实现方法之一 UTF-16,我们也知道了对于在UTF-16中,经常用到的字符放到了基本多语言平面 (BMP),仅占可寻址 Unicode 代码点总数的 1/17。65536 ( 0x010000) 到11141110x10FFFF)范围内的其余代码点称为补充字符。

fromCharCode 只使用于 16位值的。所以它只能表示0x0000~0xFFFF之间的字符,剩下的表示不到的字符,将使用代理对才能返回补充字符。1048576个码点正好代表了 2^20个字符(可以自己算一次2^20 === 1048576)。在 0 ~ 65536 之间正好留出了 两个 2^10 个字符,也就是  U+D800 ~ U+DBFF,另一个是 U+DC00 ~ U+DFFF。我们使用代理的方式,将 2^20 分为两个 2^10 填补。就可以将补充字符通过基本字符表示出来。

2^20 = 1048576;  // 补充字符

1114111 - 1048576 = 65535; // 基础平面中的字符。

2^10 = 1024;  

DFFF - D800 = 1024 = 2^10;

DFFF - DC00 = 1024 = 2^10;

然后将补充字符 分成两半,前10位映射在U+D800到U+DBFF, 后10位映射在U+DC00到U+DFFF。

注意:不是说 2^10 + 2^10 = 2^20,而是以位来分。

H = Math.floor((c - 0x10000) / 0x400) + 0xD800

高位H, 我们先计算超出基本字符的部分,然后/0x400(2^10), 保留前面十位,加上U+D800 对应的二进制数为  1101100000000000,结果转为十六进制。

L = (c - 0x10000) % 0x400 + 0xDcC00

低位L, 我们先计算超出基本字符的部分,然后%0x400  (2^10), 保留后十位,加上0xDC00 对应的二进制数为  1101110000000000,结果转为十六进制。

String.fromCharCode(0xD83C, 0xDF03);  // 代表补充字符 U+1F303 '''
String.fromCharCode(55356, 57091);   // "\uD83C\uDF03". 代表补充字符 U+1F303 '''
String.fromCharCode(0x61, 0xD834, 0xDF07); // "a\uD834\uDF07". 'a'


如果给出无效的 Unicode 代码点,使用fromCharCode的话,会返回\x00
String.fromCharCode(NaN);  // '\x00'
String.fromCharCode(Infinity);  // '\x00'
String.fromCharCode('3.9');  // '\x03'
String.fromCharCode(2e2); // 'È'


参数会带有 Number 上下文, 会转为数字。并且小数的话会进行向下取整。

虽然有补充代码点值之间的数学关系,表示它需要进行计算,使用替代值经过计算每次都是需要的。因为它是不能直接识别补充字符的,必须经还映射代理点的方式,重点是每次都要进行额外步骤的查找。有一个更加简便的静态方法 String.fromCodePoint() 可以用实际的代码点来直接表示补充字符。

String.fromCodePoint

fromCodePoint() 方法可以使用指定的代码点创建字符串。它和 String.fromCharCode 相似,但又有所不同。

 参数可以是多个,但是参数类型却不同,可以使用0x0000~0x10FFFF的码点,都可以直接使用,不会发生截取。

String.fromCodePoint(42);       // "*"
String.fromCodePoint(65, 90);   // "AZ"
String.fromCodePoint(0x404);    // "\u0404" == "Є"
String.fromCodePoint(0x2F804);  // "\uD87E\uDC04"
String.fromCodePoint(194564);   // "\uD87E\uDC04"
String.fromCodePoint(0x1D306, 0x61, 0x1D307); // "\uD834\uDF06a\uD834\uDF07"

如果给出无效的Unicode 代码点,会抛出异常。RangeError.

String.fromCodePoint('_');      // RangeError
String.fromCodePoint(Infinity); // RangeError
String.fromCodePoint(-1);       // RangeError
String.fromCodePoint(3.14);     // RangeError
String.fromCodePoint(3e-2);     // RangeError
String.fromCodePoint(NaN);      // RangeError

由于String.fromCodePoint()方法已添加到 ECMAScript 2015 中,所以还有很多版本不支持此方法,所以我们需要填补一下这个方法。

polyfill: 

String.fromCodePoint = (function(stringFromCharCode) {
    var codeUnits = [], codeLen = 0, result = "";
    for (var index=0, len = arguments.length; index !== len; ++index) {
        if (!(codePoint < 0x10FFFF && (codePoint>>>0) === codePoint))
          throw RangeError("Invalid code point: " + codePoint);
        if (codePoint <= 0xFFFF) { // BMP
          codeLen = codeUnits.push(codePoint);
        } else { 
          codePoint -= 0x10000;
          codeLen = codeUnits.push(
            (codePoint >> 10) + 0xD800,  // highSurrogate
            (codePoint % 0x400) + 0xDC00 // lowSurrogate
          );
        }
        if (codeLen >= 0x3fff) {
          result += stringFromCharCode.apply(null, codeUnits);
          codeUnits.length = 0;
        }
      }
      return result + stringFromCharCode.apply(null, codeUnits);
    }
})(String.fromCharCode)

比较二者的区别。

返回值 给出无效代码点 代码点范围 参数的默认值
fromCharCode (字符串) 会经过强制转换,返回\x00 只接受基本平面字符,不然就会进行切割。 一样,都为0,返回 ''
fromCodePoint (字符串) 不会进行强制转换,返回RangeError 可以接受任意 Unicode 码点。 一样,都为0,返回 ''

实例属性

String.prototype.length

length属性,返回字符串的长度,以 UTF-16 代码单位表示。上文中我们已经了解了使用UTF-16编码来表示字符,基础字符我们使用两个字节(16位)来表示,对于补充字符我们使用四个字节(32位)来表示。所以我们应该猜到,length 属性并不会就以个数来返回字符串的长度,按我的理解来说,应该是按照字节来表示,一个长度表示为两个字节,所以像 ASCII 这种编码,英文来说占有一个长度,表示两个字节。中文这种属于补充字符,是两个长度,占有四个字节。(JavaScript 中虽然没有收集长度的字节,但我们完全可以自己写一个,只要是在 0 ~ 65536 只是收集两字节,否则就收集四个字节)。

'a'.length;  // 1
'\u12345'.length; // 补充字符. 2
''.length;  // 2
''.length;   // 0 空字符串

String.length;  // 1 它是String函数的元数,与字符串的长度无关。

因为 length 是表示代码单元。而不是准确的字符个数,所以当我们只是想要知道个数,我们需要自己来实现。

function getCharacterLength (str) {
    return [...str].length;
}

会对字符串进行拆分放入数组中,我们算数组的个数就不会发生length读取的是代码单元的问题。

字符串是不可变的,所以我们不能修改字符串的长度,因为基本数据是存储在栈中的,所以一经定义,就不可以改变大小长度,是只读的字段,如果你想要修改字符串,只能销毁重新赋值。

let string = 'hello';
string.length; // 5

string.length = 3;
string.length;  // 5

因为实例的方法比较多,我们放到下一篇,重点讲解字符串的实例方法(包括实例方法的具体实现)。

你可能感兴趣的:(javaScript,javascript,前端)