字符串的扩展

1. 字符的Unicode表示法

JavaScript允许采用 \uxxxx 形式表示一个字符,其中 xxxx 表示字符的 Unicode 码点。

"\u0061" // 表示小写字母"a"

但是这种表示法只限于码点在 \u0000-\uFFFF 之间的字符,有些字符的 Unicode 编码超出了这个范围,那么就必须使用2个双字节的形式表示。

"\uD842\uDFB7" // "?" 注意不是吉祥的"吉"
"\u5409" // "吉" 这个才是吉祥的"吉"

ES5 中如果在 \u 后面超过 oxFFFF 的数值,如 "\u0061我" 输出结果为 a我"\u0061我"JS 引擎看来就是 "\u0061+'我'" 后面的通过字符串拼接拼接上。

ES6 对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。

例如 \u20BB7 表示的是 "?" ,在 ES5"\u20BB7"JS 引擎解析成 "7" ,这是因为 \u20BB 是一个不可打印的字符,所以只会显示一个空格,后面拼接上一个 7 .

ES6"\u{20BB7}" 的解析结果为 "?" .

2. codePointAt()

JavaScript内部,字符以 UTF-16 的格式存储,每个字符固定为2个字节(范围 \u0000-\uFFFF),但是有些字符的码点是大于 0xFFFF 的,JavaScript会认为它是两个字符。

var s1 = "你好";
var s2 = "?";

s1.length // 2
s2.length // 2

这是因为 ? 的码点大于 0xFFFFJS 引擎认为它是两个字符,即占四个字节。

s2.charCodeAt(0) // 55362
s2.charCodeAt(0) // 57271
s2.codePointAt(0) // 134071=0X20BB7
s2.codePointAt(1) // 57271

可以看出 charCodeAt 方法一次只能返回两个字节的值,而 codePointAt(0) 可以返回四个字节的值,codePointAt(1)charCodeAt(1) 返回的值相同。

var s = "?a";
s.codePointAt(0) // "13401" 对应"?"
s.codePointAt(2) // "61" 对应"a"

可以看到在传入 2 时才能得到第二个字符 a , 可以通过 for...of 循环来解决这个问题,因为它会正确识别32位的 UTF-16 字符。

var s = "?a";
for (let ch of s) {
    console.log(ch.codePointAt(0).toString(16));
}
// 20bb7
// 61

codePointAt() 方法还可以用来测试一个字符是2个字节还是4个字节。

function is32Bit(c) {
    return c.codePointAt(0) > 0xFFFF;
}

is32Bit("?"); // true
is32Bit("a"); // false

3. String.fromCodePoint()

ES5 提供了 String.fromCharCode 方法,用于从码点返回对应字符,但是这个方法不能识别32位的 UTF-16 字符(Unicode 编号大于 0xFFFF)。

String.fromCharCode(0x20061); // 'a'

String.fromCharCode 方法不能识别大于 0xFFFF 的码点,所以 0x20BB7 就发生了溢出,最高位2被舍弃,最后返回码点 U+0061 对应的字符 a

ES6 提供了 String.fromCodePoint 方法,可以识别大于 0xFFFF 的字符,作用上与 codePointAt 方法正好相反。

String.fromCodePoint(0x78, 0x1f680, 0x79) ==== 'x\uD83D\UDE80y';

String.fromCharCode 方法如果有多个参数,那么它们就会拼接成一个字符串。

注意: fromCodePoint 方法定义在 String 对象上,而 codePointAt 方法定义在字符串的实例对象上。

4. 字符串的遍历器接口

ES6 为字符串添加了遍历器接口,使得字符串可以由 for...of 循环遍历。

for (let codePoint of 'foo') {
    console.log(codePoint);
}
// 'f'
// 'o'
// 'o'

前面已经提到过,利用 for...of 可以识别大于 0xFFFF 的码点,传统的 for 循环无法识别大于 0xFFFF 的码点。

var text = String.fromCodePoint(0x20BB7);

for (let i = 0, length = text.length; i < length; i++) {
    console.log(text[i]);
}
// ''
// '' 输出两个不可打印的字符

for (let i of text) {
    console.log(i);
}
// '?'

在上面的代码中 0x20BB7 只有一个字符,但是 for 循环认为它包含 2 个字符,而 for...of 循环会正确识别出这个字符。

5. at()(提案)

ES5 中为字符串对象提供了 charAt 方法,返回字符串给定位置的字符,该方法不能识别码点大于 0xFFFF 的字符。

'abc'.charAt(0) // 'a'
'?'.charAt(0) // '\uD842',一个不可打印的字符

charAt 方法只能返回 UTFF-16 编码中的第一个字节,目前有一个提案提出字符串实例的 at 方法,可以识别 Unicode 编号大于 0xFFFF 的字符,返回正确的字符。

'?'.charAt(0) // '?',可以正确返回

6. normalize()

许多欧洲语言有重音符号和语调符号,为了表示它们, Unicode 提供了两种方法:

  1. 直接提供带重音符号的字符
  2. 利用合成符号(原字符+重音符号)

但是合成符号在 JS 引擎看来实际上是两个字符,合成字符并不等于带重音符号的字符。

normalize 方法解决了这个问题,将字符的不同表示方法统一为同样的形式,这称为 Unicode 正规化。

不过 nomalize 方法目前不能识别三个或三个以上字符的合成,这种情况下还是只能利用正则表达式,通过 Unicode 编号区间判断。

7. includes(),startWith(),endsWith()

ES5:

  • indexOf 确定一个字符串是否包含在另一个字符串中。

ES6:

  • includes 返回布尔值,表示是否找到了传入的参数字符串
  • startWith 返回布尔值,表示参数字符串是否在源字符串的头部
  • endWith 返回布尔值,表示参数字符串是否在源字符串的尾部
var s = "hello JS";

s.includes("ell"); // true
s.startWith("h"); // true
s.endWith("S"); // true

这三个方法都支持第二个参数,表示开始搜索的位置。

var s = "hello JS";

s.includes("JS", 6); // true, 从第个7个(从0开始)字符位置开始
s.startWith("JS", 6); // true, 从第7个(从0开始)字符位置开始
s.endWith("hello", 5); // false, 前5个字符(下标为0,1,2,3,4)

8. repeat()

功能:返回一个新字符串,将原字符串重复传入的参数次。

'x'.repeat(2); // "xx"

如果传入的参数是小数,会被取整。

  1. 大于0时想下取整
'x'.repeat(2.9) // 'xx'
  1. 大于-1小于0时等于0
'x'.repeat(-0.9) // ''
  1. 小于-1时报错
'x'.repeat(-2) // RangeError

如果传入的参数不是数字,会先将其准换为数字。

'x'.repeat('x'); // ''
'x'.repeat('2'); // 'xx'

9. padStart(),padEnd()(ES2017)

ES2017 引入了字符串补全长度的功能,如果某个字符串长度不够指定长度,会在头部或尾部补全。

  • padStart() 用于头部补全
  • padEnd() 用于尾部补全

上面两个方法都接收两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的字符串。

'x'.padStart(5, 'ab'); // 'ababx'
'x'.padEnd(5, 'ab'); // 'xabab'

如果原字符串的长度等于或大于指定的最小长度,则返回原字符串。

'xxx'.padStart(2, 'ab'); // 'xxx'
'xxx'.padEnd(2, 'ab'); // 'xxx'

如果补全的字符串与原字符串的长度之和大于指定的最小长度,则会截去超出位数的补全字符串。

'xxx'.padStart(5, '01234'); // '01xxx'

如果省略第二个参数,则会用空格来补全。

'x'.padStart(4); // '    x'

padStart() 的两种常用用途:

  1. 为数值补全位数
'1'.padStart(10,'0'); // '0000000001'
  1. 提示字符串格式
'12'.padStart(10, 'YYYY-MM-DD'); // 'YY-MM-12'

10. 模板字符串

模板字符串是增强版的字符串,用反引号 `` ` 来标识,主要有以下三种用法。

  1. 当作普通字符串
`hello ES6`
  1. 定义多行字符串

    所有的空格和缩进都会保留在输出中。

`

hello
ES6

`
// 所有的空格和换行都会被保留,输出结果:
"

hello
ES6

"

trim() 方法可以消除模板字符串反引号 `` ` 和模板字符串内容之间的空格和换行。

​ 注意:模板字符串中的字符串之间的空格和换行是不受影响的

`
hello
ES6
`.trim();
//输出结果:
"
hello
ES6
"

​ 用单引号或双引号定义的字符串是不能有换行符的,否则会报错。

"
hello
ES6
"
// 输出结果:
//Uncaught SyntaxError: Invalid or unexpected token
  1. 在字符串中嵌入变量

    在模板字符串中嵌入变量需要将变量名放在 ${} 中, {} 中实际上可以是任意的 JavaScript 表达式,可以进行运算,引用对象属性等,如果大括号中的值不是字符串,则会按照一定的规则将其转换为字符串。

var name = 'zhangsan', age = 18;
`hello, my name is ${name}, I am ${age} years old`
// 输出结果:
hello, my name is zhangsan, I am 18 years old

11. 标签模板

模板字符串可以紧跟在一个函数名后面,这个函数将会被调用来处理这个模板字符串,这被称为“标签模板”功能。

alert `123`
// 等同于
alert (123)

标签模板实际上不是模板,而是函数调用的一种特殊形式,“标签”指的就是函数,紧跟在它后面的模板字符串就是它的参数。

如果模板字符串中有变量,就不再是简单的调用了,而是将模板字符串先处理成多个参数,再调用函数。

var a = 5;
var b = 10;

tag `hello ${ a + b } world ${ a * b }`;
// 等同于
tag(["hello ", " world ", ""], 15, 50);

tag 函数的第一个参数是一个数组,该数组的成员是模板字符串中那些没有变量替换的部分。

tag 函数所有参数的实际值如下:

  • 第一个参数: ["hello ", " world ", ""]
  • 第二个参数: 15
  • 第三个参数: 50

标签模板的两个重要应用

  1. 过滤HTML字符串,防止用户输入恶意内容

    function saferHTML(templateData) {
        var s = templateData[0]; // templateData=["<p>"," has sent you a message.</p>"]
        for (var i = 1; i < arguments.length; i++) {
            var arg = String(arguments[i]); // argument[1]="<script>alert('abc')</script>"
    
            s += arg.replace(/&/g, "&amp;")
                .replace(/</g, "&lt;")
                .replace(/>/g, "&gt");
    
            s += templateData[i];
        }
        return s;
    }
    
    var sender = "<script>alert('abc')</script>"; // 恶意代码
    var message = saferHTML`<p>${sender} has sent you a message.</p>`;
    
    输出结果:
    // <p>&lt;script&gtalert('abc')&lt;/script&gt has sent you a message.</p>
    

    我们一般要保证用户输入的内容中不能含有可执行的 JS 代码,这是为了防止那些黑客将这些代码植入到我们的程序中对我们的程序进行攻击。

    因此,我们要将 script 标签等一切可引入 JS 代码的方式都过滤掉,上面的程序中只考虑 script 标签可引入 JS 代码这一种方式。

  2. 多语言转换

    这里的语言不仅指中文,英文这种语言之间的转换,还指在 JS 中还可以运行其它的计算机语言。

    当然模板字符串本身并不具有这样的功能,这种功能的完成是依靠一些标签模板来完成的。

i18n`Welcome to ${siteName}, you are visitor number ${visitorNumber}!`
// 输出结果:
// "欢迎访问xxx, 您是第xxx位访问者!"

i18n 函数可以将英文转换为中文。

jsx`
	<div>
		<input
			ref = 'input'
			onChange = '${this.handleChange}'
			defaultValue = '${this.state.value}'
		/>
		${this.state.value}
	</div>
`

上面的代码是通过 jsx 函数将一个 DOM 字符串转换为 React 对象。

模板处理函数的第一个参数(模板字符串数组)还有一个raw属性,raw属性中保存的是转义后的原字符串。

tag`First line\nSecond line`

function tag (string) {
    console.log(string.raw[0]);
}
// 输出结果:
// "First line \\nSecond line" 保存的是转义后的字符串
// 而string = "First line\nSecond line"

stringstring.raw 唯一的区别就在于 string.raw 里面保存的是转义后的字符串。这是为了方便取得转义之前的原始模板而设计的。

12. String.raw()

ES6String 对象提供了一个 raw 方法。

String.raw() 方法往往用来充当模板字符串的处理函数,返回一个连反斜线都会转义的字符串。

String.raw`hello\n{2+3}`;
// "hello\\n5"

String.raw() 方法也可以当作正常的函数使用,但是第一个参数必须是一个就有raw 属性的对象,并且 raw 属性的值必须是一个数组。

13. 模板字符串的限制

一句话总结就是模板字符串会将字符串进行转义,比如

function latex {
    ...
}

let document = latex`
\newcommand{\unicode}{\textbf{Unicode!}}` // 报错

这是因为 \uLaTex 中具有特殊的含义,但是 JS 将它们进行了转义。

tring.raw()

ES6String 对象提供了一个 raw 方法。

String.raw() 方法往往用来充当模板字符串的处理函数,返回一个连反斜线都会转义的字符串。

String.raw`hello\n{2+3}`;
// "hello\\n5"

String.raw() 方法也可以当作正常的函数使用,但是第一个参数必须是一个就有raw 属性的对象,并且 raw 属性的值必须是一个数组。

13. 模板字符串的限制

一句话总结就是模板字符串会将字符串进行转义,比如

function latex {
    ...
}

let document = latex`
\newcommand{\unicode}{\textbf{Unicode!}}` // 报错

这是因为 \uLaTex 中具有特殊的含义,但是 JS 将它们进行了转义。

为了解决这个问题,有个提案提出放松对标签模板里字符串转义的限制,如遇到不合法的字符串转义,就返回 undefined ,而不是报错。

字符串的扩展(ES6)的更多相关文章

  1. ES6的新特性(4)——字符串的扩展

    字符串的扩展 ES6 加强了对 Unicode 的支持,并且扩展了字符串对象. 字符的 Unicode 表示法 JavaScript 允许采用\uxxxx形式表示一个字在\u0000~\uFFFF之间 ...

  2. ES6学习笔记(3)----字符串的扩展

    参考书<ECMAScript 6入门>http://es6.ruanyifeng.com/ 字符串的扩展ES6之前只能识别\u0000 - \uFFFF 之间的字符,超过此范围,识别会出错 ...

  3. es6 字符串的扩展和数值的扩展

    es6字符串的扩展 1. es6新增的一些方法 1.1 includes 判断是否包括在内,返回一个 true or false 1.2 statsWith 判断是否以什么开头,返回一个 true o ...

  4. ES6学习历程(字符串的扩展)

    字符串的扩展 在看这一节的时候前半部分写的都是关于unicode的内容,我个人感觉这部分在实际的开发中用的很少,所以不打算在做记录,等届时用到再有针对性的看,所以就将在ES6里面关于字符串操作的一些新 ...

  5. ES6模板字符串及字符串的扩展方法

    一.ES6模板字符串 传统定义字符串的方式是: const str='hello es2015,this is a string' ES6新增了一种定义字符串的方式用反引号进行标识 const str ...

  6. ES6 字符串的扩展

    字符的Unicode表示法 JavaScript允许采用\uXXXX形式表示一公分字符,其中XXXX表示字符的码点. "\u0061" //"a" 但是,这种表 ...

  7. ES6字符串相关扩展

    变量的解构赋值 // 数组的解构赋值 let [a,b,c] = [1,2,3]; //1,2,3 let [a,b,c] = [,123,]; //undefined 123 undefined l ...

  8. ES6新特性4:字符串的扩展

    本文摘自ECMAScript6入门,转载请注明出处. 一.ES5字符串函数 concat: 将两个或多个字符的文本组合起来,返回一个新的字符串. indexOf: 返回字符串中一个子串第一处出现的索引 ...

  9. es6字符串的扩展学习笔记

    1. 传统上,JavaScript只有indexOf方法,可以用来确定一个字符串是否包含在另一个字符串中.ES6又提供了三种新方法. includes():返回布尔值,表示是否找到了参数字符串. st ...

随机推荐

  1. Hadoop运行模式

    Hadoop运行模式 (1)本地模式(默认模式): 不需要启用单独进程,直接可以运行,测试和开发时使用. 即在一台机器上进行操作,仅为单机版. 本地运行Hadoop官方MapReduce案例 操作命令 ...

  2. 继上篇-jquery ajax提交 本篇用ajax提交的数据去数据库查询

    上篇讲到如何用jquery ajax提交数据至后台,后台接收并返回给ajax.https://www.cnblogs.com/tiezhuxiong/p/11943328.html 今天我们把数据传到 ...

  3. 【NHOI2018】衰减

    [解题思路] 显然这题并不难,由于数据范围较小,完全可以用DFS解决. 从原数开始每次变异的图谱,每次记录住当前的路径. 当找到1时就可以输出并回溯了. 小技巧:printf和scanf可以提高输出输 ...

  4. 设计模式之工厂模式(Factory)

    转载请标明出处:http://blog.csdn.net/shensky711/article/details/53348412 本文出自: [HansChen的博客] 设计模式系列文章: 设计模式之 ...

  5. UiPath之获取邮件相关信息

    大家好,小U又来给大家分享UiPath文章,争取每一篇文章都给大家带来满满的干货. 本次案例是告诉大家如何使用GetOutLookMailMessage这个Activity, 案例的目的是将某个特定人 ...

  6. 【10分钟学Spring】:(一)初识Spring框架

    简介 Spring是一个轻量级的企业级的Java开发框架.主要是用来替代原来更加重量级的企业级Java技术,比如EJB(Enterprise JavaBean).Java数据对象(Java Data ...

  7. python字符串的特性及相关应用

    一.字符串定义 字符串是 Python 中最常用的数据类型.用单引号(' '),双引号(" ")或者三引号(''' ''')括起来的数据称为字符串(其中,使用三引号的字符串可以横跨 ...

  8. 这货到底还是不是垃圾?【垃圾回收GC算法JVM篇四】

    目录 1.判断对象是否存活的JVM两种计数算法 2.垃圾收集算法 3.垃圾回收算法小结 垃圾收集 Garbage Collection 通常被称为"GC", 在jvm 中,程序计数 ...

  9. 【并发技术16】线程同步工具Exchanger的使用

    如果两个线程在运行过程中需要交换彼此的信息,比如一个数据或者使用的空间,就需要用到 Exchanger 这个类,Exchanger 为线程交换信息提供了非常方便的途径,它可以作为两个线程交换对象的同步 ...

  10. 【玩转MLS系列】基础教程

    1.申请华为机器学习服务MLS标准版服务: 1.如果还未注册华为云,请先进入华为云官网https://www.huaweicloud.com/进行注册:如果已注册,请登录,转第2步. 2.在华为云官网 ...