io.js入门(三)—— 所支持的ES6(下)

(接上篇

标准ES6特性

6. 新的String方法/New String methods

7. 符号/Symbols

8. 字符串模板/Template strings

新的String方法/New String methods

codePointAt() 和  String.fromCodePoint()
JS内部是以UTF-16的形式(每个字符长度都固定为16位元、2个字节)来存储字符编码的。就汉字来说,大部分的汉字均以2字节的形式来存储即可,却也有部分汉字需要有4个字节的长度来存储(其codePoint/码点大于0xFFFF),比如这个生僻字“𠮷”(注意不是“吉”),其Unicode码点是0x20BB7(十进制为134071),UTF-16码点为0xD842 0xDFB7(十进制为55362 57271)。
我们如果对这种字符使用常规的 charAt(0) 和 String.fromCharCode() ,将无法获得正确的码点/字符:

var s = "𠮷",
      a = s.charCodeAt(0),  //55362,即前两个字节的码点
   b = String.fromCharCode(0x20BB7);  //乱码字符

为解决此问题,ES6推出了 codePointAt() 和  String.fromCodePoint() 这两个字符串方法,前者可以顺利获取字符的Unicode码点,后者可由已知码点获得对应字符:

var s = "𠮷",
    a = s.codePointAt(0),  //134071
    b = String.fromCodePoint(0x20BB7);  //"𠮷"

该方法的测试比较麻烦,因为常规是命令行界面是GBK编码的,即使使用“chcp 65001”口令切到UTF-8形式也无法正常显示上文提到的生僻字符。不过我们可以将代码转到浏览器(别用IE)上显示:

var s = "𠮷",
    a = s.codePointAt(0),
    aa = s.charCodeAt(0),
    b = s.codePointAt(1),
    bb = s.charCodeAt(1),
    c = String.fromCodePoint(a),
    cc = String.fromCharCode(aa),
    d = String.fromCodePoint(b),
    dd = String.fromCharCode(bb);
console.log("a: "+a+",aa: "+aa+",b: "+b+",bb: "+bb+",c: "+c+",cc: "+cc+",d: "+d+",dd: "+dd);

var http = require('http'),
    fs = require('fs');

http.createServer(function(req,res) {
    res.writeHeader(200,{"Content-Type": "text/html"});
    res.write('<!doctype html><html><head><meta charset="utf-8"><title>测试</title></head><body><div>');
    res.write("a: "+a+",aa: "+aa+",b: "+b+",bb: "+bb+",c: "+c+",cc: "+cc+",d: "+d+",dd: "+dd);
    res.end("</div></body></html>");
}).listen(1337);
View Code

接着我们访问地址 http://127.0.0.1:1337/  可看到如下内容:

startsWith(),endsWith() includes()
常规我们会使用 s.indexOf() 来检测一个字符串里是否含有某段字符串。ES6提供了3个新的字符串检索方法,其中 startsWith() 和 endsWith() 分别用于从头部/尾部开始匹配字符串片段,includes()则没有位置限定,它们都返回boolean值。这三个方法都很好理解,直接上例子吧:

var str = 'To be, or not to be, that is the question.';
console.log(str.startsWith('To be'));   //true
console.log(str.endsWith('question.'));   //true
console.log(str.includes('not to be'));   //true

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

var str = 'To be, or not to be, that is the question.';
console.log(str.startsWith('To be',1));   //false
console.log(str.endsWith('To',2));   //true
console.log(str.includes('not to be',1));   //true

注意endsWith()加上第二个参数后,表示匹配从开始到参数索引位置结束的字符串。

repeat()
repeat()返回一个新字符串,表示将原字符串重复n次:

var str = "x".repeat(3);
console.log(str); //xxx

normalize()
该方法可将所给字符串转化为Unicode标准化字符:

var n1 = '\uD842\uDFB7'.normalize(); //𠮷

这里有个有趣的例子——为了表示语调和重音符号,Unicode提供了两种方法。一种是直接提供带重音符号的字符,比如Ǒ(\u01D1)。另一种是提供合成符号(combining character),即原字符与重音符号的合成,两个字符合成一个字符,比如O(\u004F)和ˇ(\u030C)合成Ǒ(\u004F\u030C)。
这两种表示方法,在视觉和语义上都等价,但是JavaScript不能识别:

console.log('\u01D1'==='\u004F\u030C'); //false
console.log('\u01D1'.length); // 1
console.log('\u004F\u030C'.length); // 2

上面代码表示,JavaScript将合成字符视为两个字符,导致两种表示方法不相等。使用normalize()方法可以解决该问题:

var n1 = '\u01D1'.normalize() ; 
var n2 = '\u004F\u030C'.normalize();
alert(n1===n2); //true 注意这里仅仅是在chrome为true,但在iojs里是false,因为iojs默认没有自带Intl,需要安装的童鞋可以查看这里

normalize方法可以接受四个参数:

   NFC,默认参数,表示“标准等价合成”(Normalization Form Canonical Composition),返回多个简单字符的合成字符。所谓“标准等价”指的是视觉和语义上的等价。
   NFD,表示“标准等价分解”(Normalization Form Canonical Decomposition),即在标准等价的前提下,返回合成字符分解的多个简单字符。
   NFKC,表示“兼容等价合成”(Normalization Form Compatibility Composition),返回合成字符。所谓“兼容等价”指的是语义上存在等价,但视觉上不等价,比如“囍”和“喜喜”(当然这里只是举例子,normalize只适用于欧洲文字,解决法语的重音符号之类的问题)。
   NFKD,表示“兼容等价分解”(Normalization Form Compatibility Decomposition),即在兼容等价的前提下,返回合成字符分解的多个简单字符。

'\u004F\u030C'.normalize(NFC).length // 1
'\u004F\u030C'.normalize(NFD).length // 2

符号/Symbols

codePointAt() 和  String.fromCodePoint()

ES6引入了一种新的原始数据类型Symbol,表示独一无二的ID。它通过Symbol函数生成:

var symbol1 = Symbol();
console.log(typeof symbol);  // "symbol"

Symbol函数可以接受一个字符串作为参数,表示Symbol实例的名称。需要注意的是Symbol实例均是独一无二、不相等的:

var symbolA = Symbol('Test');
var symbolB = Symbol('Test');
console.log(symbolA===symbolB); //false

注意,Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,不是对象。如果你的确想生成一个Symbol包装对象,应使用Object() 方法:

var sym = Symbol("foo");
typeof sym;     // "symbol" 
var symObj = Object(sym);
typeof symObj;  // "object"

常规我们把Symbol实例作为某个对象的属性名字,因为它是独一无二的,可以有效防止对象属性被污染:

var syma = Symbol('test');
var symb = Symbol('test');
var aag = {};
aag[syma] = 123;
aag[symb] = 456;
console.log(aag[syma]);  //123

另一个需要了解的是Symbol实例作为对象属性是不可被枚举的,但我们可以通过 Object.getOwnPropertySymbols() 来获取它们:

var syma = Symbol('a');
var symb = Symbol('b');
var obj = {};
obj[syma] = 123;
obj[symb] = 456;
obj["c"] = 789;
for(var i in obj)
console.log(i+":"+obj[i]);  //只输出"c:789"

var objectSymbols = Object.getOwnPropertySymbols(obj);
console.log(objectSymbols.length); // 2
console.log(objectSymbols[0]);     // Symbol(a)
for(var i in objectSymbols)
console.log( i +":"+ objectSymbols[i].toString()); //注意把Symbol实例转为字符串得使用.toString()方法
//0:Symbol(a)
//1:Symbol(b)

除了常规创建独一无二的Symbol实例,我们也可以通过 Symbol.for() 来创建可共享的Symbol实例。通过Symbol.for()创建的Symbol实例,我们可以使用 Symbol.keyFor() 获取它的键名:

var syma = Symbol('a');
var symaa = Symbol('a');
var symb = Symbol.for('b');
var symbb = Symbol.for('b');
console.log(syma===symaa); //false
console.log(symb===symbb); //true
console.log(Symbol.keyFor(symb)); //b

Symbol除了可作为对象的某个独一无二的、不可枚举的属性,还有一个用处是可用于描述:

var obj = {};
var descript = Symbol.for('这是一个有趣的对象');
obj[descript] = Symbol.keyFor(descript);
console.log(obj[descript]); //'这是一个有趣的对象'

字符串模板/Template strings

模板字符串(template string)是增强版的字符串,用反引号(`)标识(注意不是单引号)。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量和公式等东西:

console.log(`string text line 1  //多行字符串
string text line 2`);
//string text line 1 
//string text line 2

// 字符串中嵌入变量
var name = "Bob", time = "today";
console.log(`Hello ${name}, how are you ${time}?`); //Hello Bob, how are you today?

//花括号内可进行计算
var param_a = 1, param_b = 2;
var obj = {c:3};
console.log(`the result is ${param_a + param_b + obj.c}`); //the result is 6

模板字符串使得字符串与变量的结合,变得容易。下面是一个例子:

if (x > MAX) {
    throw new Error(`Most ${MAX} allowed: ${x}!`);
    // 传统写法为'Most '+MAX+' allowed: '+x+'!'
}

模板字符串可以紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串:

var a = 5;
var b = 10;

function tag(s, v1, v2) {
  console.log(s[0]); 
  console.log(s[1]); 
  console.log(v1);  
  console.log(v2); 
}

tag`Hello ${ a + b } world ${ a * b}`;
// "Hello "
// " world "
// 15
// 50

与常规的函数不同,Templat函数所接收到的参数比较另类,像上方的函数tag依次接受三个参数。第一个参数是一个数组,该数组的成员是模板字符串中那些没有变量替换的部分,也就是说,变量替换只发生在数组的第一个成员与第二个成员之间、第二个成员与第三个成员之间,以此类推。第一个参数之后的参数,都是模板字符串各个变量被替换后的值

也就是说,tag函数实际的参数如下:

tag(['Hello ', ' world '], 15, 50)

下面是一个更复杂的例子,展示了如何将各个参数按照原来的位置拼合回去:

var total = 30;
var msg = passthru`The total is ${total} (${total*1.05} with tax)`;

function passthru(literals) {
  var result = "";
  var 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)"
View Code

 

另外模板字符串还有一个 String.raw() 方法,往往用来充当模板字符串的处理函数,返回字符串被转义前的原始格式:

console.log(String.raw`Hi\n${2+3}!`);
// "Hi\n5!"

console.log(String.raw`Hi\u000A!`);
// 'Hi\u000A!'

 

注:本文大部分内容参考自阮一峰老师的ES6入门,但本文实例已事先对所有代码进行了校正(包括在io.js上的兼容性、代码错误的堪正,以及部分未提及特性方法的补漏)

就此我们便介绍了io.js上所支持的全部ES6特性,共勉~

donate

posted @ 2015-02-11 12:27 vajoy 阅读(...) 评论(...) 编辑 收藏
Copyright © 2014 - 2017 VaJoy Studio