一、字符串拓展
(1)字符表示
-
UTF-16编码回顾
在JS里面的字符是以utf-16编码方式进行解析
utf-16的编码范围:
- U+0000 ~ U+D800:两个字节表示一个字符
- U+D800 ~ U+FFFF:属于空位,四个字节表示一个字符(超出编码极限的字符会以这种方式解析)
超出编码极限的字符也可用花括号包裹,JS会帮忙解析,例如:\u{2pbb7} == 𠮷
-
字符的表示方式
-
代码补全
如果码点不足4位,则用0补全
console.log('\u0041\u0042\u0043'); // ABC
-
{}解析
可以解析超出编码极限,或者没有补全的码点
console.log('\u{0041}\u{0042}\u{0043}'); // ABC console.log('\u{41}\u{42}\u{43}'); // ABC console.log('\u{2pbb7}'); // 𠮷 console.log('\uD842\uDFB7' === '\u{20BB7}'); //true
-
(2)码点相关方法
-
length
码点长度就是字符长度
var s = "\u{20BB7}"; // == \uD842\uDFB7 console.log(s.length); // 2
这样的处理方式不符合预期,只对应了一个字符,长度应该是1,而不是2
-
charAt(es5)
输出索引所对应的字符
缺点:不能正确处理超出编码极限的字符
console.log(s.charAt(0)); // � console.log(s.charAt(1)); // �
-
charCodeAt (es5)
输出索引所对应的码点(10进制)
缺点:不能正确处理超出编码极限的字符
console.log(s.charCodeAt(0)); // 55362 console.log(s.charCodeAt(0)); // 57271 // 转成16进制 console.log(Number.prototype.toString.call(55362,16)); // D842 console.log(Number.prototype.toString.call(57271,16)); // DFB7
-
codePointAt(es6)
输出索引所对应的码点(10进制)
能正确处理超出编码极限的字符,并能拿到索引对应的码点
var s= "𠮷a" console.log(s.codePointAt(0)); // 134071 console.log(Number.prototype.toString.call(55362,16)); // 20bb7 console.log(s.length); // 3 长度还是3,只是识别被正确的解析了 console.log(s.codePointAt(0));//134071 超出编码极限 => 正确解析 console.log(s.codePointAt(1));//57271 console.log(s.codePointAt(2));//97 console.log(Number.prototype.toString.call(97,16));//61 console.log('\u0061'); //a console.log('\u{61}'); //a
应用:判断一个字符是2个字节还是4个字节组成
function is32Bit(c){ // 1Byte == 8Bit return c.codePointAt(0) > 0xffff; // 转16进制为10进制 } console.log(is32Bit('吉')); //true console.log(0xffff); //655535 比对的时候直接转为10进制比较
-
迭代器
可以正确的输出超出编码极限的字符
var s = "𠮷a"; // 对比一下 for (let i = 0; i < str.length; i++) { console.log(str[i]); // � � a } for (let value of s) { console.log(value); // 𠮷 a }
-
fromCharCode(es5)
传入一个码点返回一个对应的字符
缺点:不能识别超出编码极限的码点
console.log(String.fromCharCode(0x20bb7)); // ஷ // 处理方式:当超出物理极限的时候舍弃最高位,返回相应的字符 console.log(String.fromCharCode(0x20bb7) === String.fromCharCode(0x0bb7)); //true
-
fromCodePoint(es6)
传入一个码点返回一个对应的字符
能识别超出编码极限的码点
String.fromCodePoint(0x20BB7); //吉
(3)字符串新增方法
-
includes / startsWith / endsWith
判读一个字符串是否在另一个字符串当中、开头、结尾
let s = "Hello world!"; console.log(s.includes("o")); // true console.log(s.startsWith('Hello')); // true console.log(s.endsWith('!')); // true
-
repeat
把原本的字符串重复N次
console.log('x'.repeat(3)); //xxx console.log('x'.repeat(2.9)); //xx console.log('x'.repeat(NaN)); // 空 console.log('x'.repeat(0)); // 空 console.log('x'.repeat("3")); //xxx 会有隐式转换
-
padStart / padEnd
开头、结尾填充 指定字符串 至 指定长度
console.log('x'.padStart(5,"ab")); //ababx console.log('x'.padStart(4,"ab")); //abax console.log('x'.padEnd(4,"ab")); //xaba console.log('x'.padEnd(5,"ab")); //xabab
二、模板字符串
(1)基本用法
let name = 'web'
let info = 'developer'
let m = `I am a ${name} ${info}`;
console.log(m); //I am a web developer
(2)进阶用法
模板里面是表达式,可以做很多事情:
-
可以运算
let x = 1; let y = 2; console.log(`${x} + ${y} = ${x+y}`) // 1 + 2 = 3
-
可以调用对象
let obj = {x:1,y:2}; console.log(`${obj.x + obj.y}`) // 3
-
可以调用函数
function fn(){ return [1,2,3,4]; } console.log(`foo ${fn()} bar`) //foo 1,2,3 bar
模板里会隐式转换成字符串
-
可以嵌套字符串
let msg = `Hello, ${'place'}`; console.log(msg); //Hello, place
(3)模板渲染方法
const temp = arr1 => `
<table>
${
arr1.map(addr => `
<tr><td>${addr.first}</td></tr>
<tr><td>${addr.last}</td></tr>
`)
}
</table>
`
const data = [
{first:"zhang",last:"san"},
{first:"li",last:"si"},
]
console.log(temp(data));
输出:
发现有个逗号,这是因为map返回的是一个数组,而模板内部会隐式转换成字符串,所以导致了这个结果
解决方法:
${
arr1.map(addr => `
<tr><td>${addr.first}</td></tr>
<tr><td>${addr.last}</td></tr>
`).join('')
}
用join方法把数组拼接起来就好了
(4)注入问题
一些恶意的模板数据会通过这种方式渲染到页面
const data = [
{first:"zhang",last:"<script>alert('abc')</script>"},
{first:"li",last:"si"},
]
(5)标签模板
专门用于解决恶意注入的问题
通过标签模板 tag`` 的形式调用函数,可以在函数内部做相应的处理
参数依次是:以{}分隔的字符串,往后{}里的值
let a = 5;
let b = 10;
tag `Hello ${a+b} world${a*b}`;
function tag($,$1,$2){
console.log($,$1,$2)
}
//['Hello ',' world',''] 13 50
解决方案:
function SaferHTML(tempData) {
let s = '';
for (let i = 1; i < arguments.length; i++) {
let arg = String(arguments[i]);
s += arg.replace(/</g, "<").replace(/>/g, ">")
}
return s;
}
let sender = '<script>alert("abc")<\/script>'
let message = SaferHTML `<p>${sender} has set you message</p>`
console.log(message);
输出:
注意区分:字符串里的实体符是不会被浏览器解析的,标签里的实体符才会被解析