转自https://gitee.com/hongjilin/hongs-study-notes/tree/master
一、字符串的拓展
Ⅰ-概括总结
Unicode表示法:
大括号包含
表示Unicode字符(\u{0xXX}
或\u{0XXX}
)字符串遍历: 可通过 [ for-of ] 遍历字符串
字符串模板: 可单行可多行可插入变量的增强版字符串
标签模板: 函数参数的特殊调用
String.raw(): 返回把字符串所有变量替换且对斜杠进行转义的结果
String.fromCodePoint(): 返回码点对应字符
codePointAt(): 返回字符对应码点(
String.fromCodePoint()
的逆操作)normalize(): 把字符的不同表示方法统一为同样形式, 返回
新字符串
(Unicode正规化)repeat(): 把字符串重复n次, 返回
新字符串
matchAll(): 返回正则表达式在字符串的所有匹配
includes(): 是否存在指定字符串
startsWith(): 是否存在字符串头部指定字符串
endsWith(): 是否存在字符串尾部指定字符串
以上扩展方法均可作用于由
4个字节储存
的Unicode字符
上
Ⅱ-模板字符串
模板字符串(template string)是增强版的字符串, 用反引号[ ` ]标识. 它可以当作普通字符串使用, 也可以用来定义多行字符串, 或者在字符串中嵌入变量.
嵌入变量使用[
${变量名}
]:如果大括号中的值不是字符串, 将按照一般的规则转为字符串. 比如, 大括号中是一个对象, 将默认调用对象的toString
方法. 如果大括号内部是一个字符串, 将会原样输出.
① 字符串中可以出现换行符
字符串中可以出现换行符:如果使用模板字符串表示多行字符串, 所有的空格和缩进都会被保留在输出之中.
//代码中, 所有模板字符串的空格和换行, 都是被保留的, 比如`<ul>`标签前面会有一个换行. 如果你不想要这个换行, 可以使用`trim`方法消除它. $('#list').html(` <ul> <li>first</li> <li>second</li> </ul> `.trim());
② 可以使用 ${xxx} 形式输出变量
function authorize(user, action) { if (!user.hasPrivilege(action)) { throw new Error( // 传统写法为 // 'User ' // + user.name // + ' is not authorized to do ' // + action // + '.' `User ${user.name} is not authorized to do ${action}.`); } }
③ 大括号内部可以放入任意的 JavaScript 表达式
括号内部可以放入任意的 JavaScript 表达式, 可以进行运算, 以及引用对象属性.
let x = 1; let y = 2; `${x} + ${y} = ${x + y}`// "1 + 2 = 3" `${x} + ${y * 2} = ${x + y * 2}`// "1 + 4 = 5" let obj = {x: 1, y: 2}; `${obj.x + obj.y}`// "3"
④ 模板字符串之中还能调用函数.
function fn() { return "Hello World";} `foo ${fn()} bar` // foo Hello World bar
⑤ 字符串嵌套
const tmpl = addrs => ` <table> ${addrs.map(addr => ` <tr><td>${addr.first}</td></tr> <tr><td>${addr.last}</td></tr> `).join('')} </table> `;
上面代码中, 模板字符串的变量之中, 又嵌入了另一个模板字符串, 使用方法如下.
const data = [ { first: '<Jane>', last: 'Bond' }, { first: 'Lars', last: '<Croft>' }, ]; console.log(tmpl(data)); /**下面是打印结果 <table> <tr><td><Jane></td></tr> <tr><td>Bond</td></tr> <tr><td>Lars</td></tr> <tr><td><Croft></td></tr> </table> */
如果需要引用模板字符串本身, 在需要时执行, 可以写成函数.
let func = (name) => `Hello ${name}!`; func('Jack') // "Hello Jack!"
上面代码中, 模板字符串写成了一个函数的返回值. 执行这个函数, 就相当于执行这个模板字符串了.
Ⅲ-标签模板
模板字符串的功能, 不仅仅是上面这些. 它可以紧跟在一个函数名后面, 该函数将被调用来处理这个模板字符串. 这被称为“
标签模板
”功能(tagged template`). -->反正我是很少用到,可阅读性较差alert`hello` // 等同于 alert(['hello'])
① 简单实例
标签模板其实不是模板, 而是函数调用的一种特殊形式.
[标签]指的就是函数
, 紧跟在后面的模板字符串就是它的参数.但是, 如果模板字符里面有变量, 就不是简单的调用了, 而是会将模板字符串先处理成多个参数, 再调用函数.
let a = 5; let b = 10; tag`Hello ${ a + b } world ${ a * b }`; // 等同于 tag(['Hello ', ' world ', ' ' ], 15, 50);
上面代码中, 模板字符串前面有一个标识名
tag
, 它是一个函数. 整个表达式的返回值, 就是tag
函数处理模板字符串后的返回值.函数
tag
依次会接收到多个参数.function tag(stringArr, value1, value2){ // ... } // 等同于 function tag(stringArr, ...values){ // ... }
tag
函数的第一个参数是一个数组,该数组的成员是模板字符串中那些没有变量替换的部分
, 也就是说, 变量替换只发生在数组的第一个成员与第二个成员之间、第二个成员与第三个成员之间, 以此类推.
tag
函数的其他参数, 都是模板字符串各个变量被替换后的值. 由于本例中, 模板字符串含有两个变量, 因此tag
会接受到value1
和value2
两个参数.
tag
函数所有参数的实际值如下.
- 第一个参数:
['Hello ', ' world ', '']
- 第二个参数: 15
- 第三个参数: 50
也就是说,
tag
函数实际上以下面的形式调用.tag(['Hello ', ' world ', ''], 15, 50)
我们可以按照需要编写
tag
函数的代码. 下面是tag
函数的一种写法, 以及运行结果.let a = 5; let b = 10; function tag(s, v1, v2) { console.log(s[0]); console.log(s[1]); console.log(s[2]); console.log(v1); console.log(v2); return "OK"; } tag`Hello ${ a + b } world ${ a * b}`; // "Hello " // " world " // "" // 15 // 50 // "OK"
② 稍微复杂的栗子
let total = 30; let msg = passthru`The total is ${total} (${total*1.05} with tax)`; function passthru(literals) { let result = ''; let i = 0; while (i < literals.length) { result += literals[i++]; if (i < arguments.length) { result += arguments[i]; } } return result; } msg // "The total is 30 (31.5 with tax)"
上面这个栗子展示了, 如何将各个参数按照原来的位置拼合回去.
passthru
函数采用 rest 参数的写法如下.function passthru(literals, ...values) { let output = ""; let index; for (index = 0; index < values.length; index++) { output += literals[index] + values[index]; } output += literals[index] return output; }
“标签模板”的一个重要应用, 就是过滤 HTML 字符串, 防止用户输入恶意内容.
let message = SaferHTML`<p>${sender} has sent you a message.</p>`; function SaferHTML(templateData) { let s = templateData[0]; for (let i = 1; i < arguments.length; i++) { let arg = String(arguments[i]); // Escape special characters in the substitution. s += arg.replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">"); // Don't escape special characters in the template. s += templateData[i]; } return s; }
上面代码中,
sender
变量往往是用户提供的, 经过SaferHTML
函数处理, 里面的特殊字符都会被转义.let sender = '<script>alert("abc")</script>'; // 恶意代码 let message = SaferHTML`<p>${sender} has sent you a message.</p>`; message // <p><script>alert("abc")</script> has sent you a message.</p>
③ 用作多语言转换(国际化处理)
标签模板的另一个应用, 就是多语言转换(国际化处理).
i18n`Welcome to ${siteName}, you are visitor number ${visitorNumber}!` // "欢迎访问xxx , 您是第xxxx位访问者!"
模板字符串本身并不能取代 Mustache 之类的模板库, 因为没有条件判断和循环处理功能, 但是通过标签函数, 你可以自己添加这些功能.
// 下面的hashTemplate函数 // 是一个自定义的模板处理函数 let libraryHtml = hashTemplate` <ul> #for book in ${myBooks} <li><i>#{book.title}</i> by #{book.author}</li> #end </ul> `;
除此之外, 你甚至可以使用标签模板, 在 JavaScript 语言之中嵌入其他语言.
jsx` <div> <input ref='input' onChange='${this.handleChange}' defaultValue='${this.state.value}' /> ${this.state.value} </div> `
上面的代码通过
jsx
函数, 将一个 DOM 字符串转为 React 对象.下面则是一个假想的栗子, 通过
java
函数, 在 JavaScript 代码之中运行 Java 代码.java` class HelloWorldApp { public static void main(String[] args) { System.out.println("Hello World!"); // Display the string. } } ` HelloWorldApp.main();
模板处理函数的第一个参数(模板字符串数组), 还有一个
raw
属性.console.log`123` // ["123", raw: Array[1]]
上面代码中,
console.log
接受的参数, 实际上是一个数组. 该数组有一个raw
属性, 保存的是转义后的原字符串.请看下面的栗子.
tag`First line\nSecond line` function tag(strings) { console.log(strings.raw[0]); // strings.raw[0] 为 "First line\\nSecond line" // 打印输出 "First line\nSecond line" }
上面代码中,
tag
函数的第一个参数strings
, 有一个raw
属性, 也指向一个数组. 该数组的成员与strings
数组完全一致. 比如,strings
数组是["First line\nSecond line"]
, 那么strings.raw
数组就是["First line\\nSecond line"]
. 两者唯一的区别, 就是字符串里面的斜杠都被转义了. 比如 , strings.raw 数组会将\n
视为\\
和n
两个字符, 而不是换行符. 这是为了方便取得转义之前的原始模板而设计的.
二、数值的拓展
Ⅰ- 概括总结
二进制表示法:
0b或0B开头
表示二进制(0bXX
或0BXX
)八进制表示法:
0o或0O开头
表示二进制(0oXX
或0OXX
)指数运算符:其实这是
ES2016
新增的 ,指数运算符(**
). -->详见下方Number.EPSILON: 数值最小精度
Number.MIN_SAFE_INTEGER: 最小安全数值(
-2^53
)Number.MAX_SAFE_INTEGER: 最大安全数值(
2^53
)Number.parseInt(): 返回转换值的整数部分
Number.parseFloat(): 返回转换值的浮点数部分
Number.isFinite(): 是否为有限数值
Number.isNaN(): 是否为NaN
Number.isInteger(): 是否为整数
Number.isSafeInteger(): 是否在数值安全范围内
Math.trunc(): 返回数值整数部分
Math.sign(): 返回数值类型(
正数1
、负数-1
、零0
)Math.cbrt(): 返回数值立方根
Math.clz32(): 返回数值的32位无符号整数形式
Math.imul(): 返回两个数值相乘
Math.fround(): 返回数值的32位单精度浮点数形式
Math.hypot(): 返回所有数值平方和的平方根
Math.expm1(): 返回
e^n - 1
Math.log1p(): 返回
1 + n
的自然对数(Math.log(1 + n)
)Math.log10(): 返回以10为底的n的对数
Math.log2(): 返回以2为底的n的对数
Math.sinh(): 返回n的双曲正弦
Math.cosh(): 返回n的双曲余弦
Math.tanh(): 返回n的双曲正切
Math.asinh(): 返回n的反双曲正弦
Math.acosh(): 返回n的反双曲余弦
Math.atanh(): 返回n的反双曲正切
Ⅱ - 指数运算符
ES2016 新增了一个指数运算符(
**
).2 ** 2 // 4 2 ** 3 // 8
这个运算符的一个特点是右结合, 而不是常见的左结合. 多个指数运算符连用时, 是从最右边开始计算的.
// 相当于 2 ** (3 ** 2) 2 ** 3 ** 2 // 512
上面代码中, 首先计算的是第二个指数运算符, 而不是第一个.
指数运算符可以与等号结合, 形成一个新的赋值运算符(
**=
).let a = 1.5; a **= 2; // 等同于 a = a * a; let b = 4; b **= 3; // 等同于 b = b * b * b;