马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
上接语言根本:JavaScript高级步伐计划根本(二)
2.3.6 String范例
ECMAScript 中的字符串是不可变的( immutable ),一旦创建,它们的值就不能变了。要修改 某个变量中的字符串值,必须先烧毁原始的字符串,然后将包罗新值的另一个字符串生存到该变量
2.转换为字符串
- 险些全部值都有toString()方法(字符串值也有 toString()方法,该方法只是简单地返回自身的一个副本),null 和 undefined 值没有 toString()方法
- toString()通过传入参数,得到对应参数的进制字符串
- 如果不确定一个值是不是 null 或 undefined ,可以使用 String() 转型函数,该机制如下
- 如果值有 toString()方法,则调用该方法(不传参数)并返回效果
- 如果值是 null ,返回 "null"
- 如果值是 undefined ,返回 "undefined"
3.模板字面量
模板字面量可以生存换行字符,跨行界说字符串 - let myMultiLineString = 'first line\nsecond line';
- let myMultiLineTemplateLiteral = `first line
- second line`;
- console.log(myMultiLineString);
- // first line
- // second line"
- console.log(myMultiLineTemplateLiteral);
- // first line
- // second line
- console.log(myMultiLineString === myMultiLinetemplateLiteral); // true
复制代码 模板字面量会保持反引号内部的空格 - // 这个模板字面量在换行符之后有 25 个空格符
- let myTemplateLiteral = `first line
- second line`;
- console.log(myTemplateLiteral.length); // 47
复制代码 4.字符串插值
技能上讲,模板字面量不是字符串,而是一种特别的 JavaScript 句法表达式,只不外求值后得到的是字符串。 模板字面量在界说时立刻求值并转换为字符串实例,任何插入的变量也会从它们最靠近的作 用域中取值 - console.log(`Hello, ${ `World` }!`); // Hello, World!
复制代码 全部插入的值都会使用 toString()欺凌转型为字符串,而且任何 JavaScript 表达式都可以用于插值 5.模板字面量标签函数 让我们举例子来明确标签函数 起首我们先来界说一个函数 - function simpleTag(strings, aValExpression, bValExpression, sumExpression) {
- console.log(strings);
- console.log(aValExpression);
- console.log(bValExpression);
- console.log(sumExpression);
- return 'foobar';
- }
复制代码 接着我们调用并给它传入模板字符串
- let a = 6;
- let b = 9;
- let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;
- // ["", " + ", " = ", ""]
- // 6
- // 9
- // 15
- console.log(taggedResult); // "foobar"
复制代码 可以看出来,如果我们直接传入一个模板字符串,函数吸取到的参数,分别为原始字符串数组以及各个变量
我们再让函数简化(究竟每个函数的参数个数并不都为3),使用剩余操纵符(rest operator)将它们网络到一个数组中
- let a = 6;
- let b = 9;
- function simpleTag(strings, ...expressions) {
- console.log(strings);
- for(const expression of expressions) {
- console.log(expression);
- }
- return 'foobar';
- }
- let taggedResult = simpleTag`${ a } + ${ b } = ${ a + b }`;
- // ["", " + ", " = ", ""]
- // 6
- // 9
- // 15
- console.log(taggedResult); // "foobar"
复制代码 对于有 n 个插值的模板字面量,传给标签函数的表达式参数的个数始终是 n(即expressions),而传给标签函数的第一个参数(strings)所包罗的字符串个数则始终是 n+1。
如果你想还原字符串可以这么做
- function zipTag(strings, ...expressions) {
- return strings[0] +
- expressions.map((e, i) => `${e}${strings[i + 1]}`)
- .join('');
- }
复制代码 6.原始字符串
可以使用默认的 String.raw 标签函数直接获取原始的模板字面量内容(如换行符或 Unicode 字符)
- // Unicode 示例
- // \u00A9 是版权符号
- console.log(`\u00A9`); // ©
- console.log(String.raw`\u00A9`); // \u00A9
复制代码 也可以使用过标签函数的第一个参数,即字符串数组的.raw 属性取得每个字符串的原始内容
- printRaw`\u00A9${ 'and' }\n`;
- function printRaw(strings) {
- for (const rawString of strings.raw) {
- console.log(rawString);
- }
- }
- // \u00A9
- // \n
复制代码 2.3.7 Symbol范例
是确保对象属性使用唯一标识符,不会发生属性辩论的伤害 1.符号的根本使用 符号必要使用 Symbol() 初始化。由于符号本身是原始范例,以是 typeof 操纵符对符号返回 symbol 。 也可以传入一个字符串参数作为对符号的形貌(description ),未来可以通过这个字符串来调试代码。但是,这个字符串参数与符号界说或标识完全无关 - let fooSymbol = Symbol('foo');
- let otherFooSymbol = Symbol('foo');
- console.log(fooSymbol == otherFooSymbol); //false
复制代码 最紧张的是,Symbol()函数不能与 new 关键字一起作为构造函数使用,如许做是为了制止创建符号包装对象
- let myNumber = new Number();
- console.log(typeof myNumber); // "object"
- let mySymbol = new Symbol(); // TypeError: Symbol is not a constructor
复制代码 如果你确实想使用符号包装对象,可以借用 Object() 函数 - let mySymbol = Symbol();
- let myWrappedSymbol = Object(mySymbol);
- console.log(typeof myWrappedSymbol); // "object"
复制代码 2.使用全局符号注册表
如果运行时的差别部分必要共享和重用符号实例,必要使用 Symbol.for()方法
- let fooGlobalSymbol = Symbol.for('foo'); // 创建新符号
- let otherFooGlobalSymbol = Symbol.for('foo'); // 重用已有符号
- console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true
复制代码 第一次使用某个字符串调用时,它会查抄全局运行时注册表,发现不存在对应的符号,于是就会天生一个新符号实例并添加到注册表中。后续使用雷同字符串的调用同样会查抄注册表,发现存在与该字符串对应的符号,然后就会返回该符号实例
必须使用字符串键创建,作为参数传给Symbol.for的任何参数都会被转换为字符串
- let emptyGlobalSymbol = Symbol.for();
- console.log(emptyGlobalSymbol); // Symbol(undefined)
复制代码 还可以使用 Symbol.keyFor()来查询全局注册表
- let foo=Symbol.for('m')
- console.log(Symbol.keyFor(foo)) //'m'
复制代码 3.使用符号作为属性
凡是可以使用字符串或数值作为属性的地方,都可以使用符号,这就意味着符号同样可以作为对象的属性
- const S1=Symbol('foo'),
- S2 = Symbol('bar'),
- S3 = Symbol('baz'),
- S4 = Symbol('qux');
- let obj={
- [S1]:'11'
- }
- console.log(obj);
- // {Symbol(foo): 11}
- Object.defineProperty(obj, S2, {value: 'bar val'});
- console.log(obj);
- // {Symbol(foo): 11, Symbol(bar): bar val}
- Object.defineProperties(obj, {
- [s3]: {value: 'baz val'},
- [s4]: {value: 'qux val'}
- });
- console.log(obj);
- // {Symbol(foo): 11, Symbol(bar): bar val,
- // Symbol(baz): baz val, Symbol(qux): qux val}
复制代码 雷同于 Object.getOwnPropertyNames() 返回对象实例的通例属性数组 Object.getOwnPropertySymbols()返回对象实例的符号属性数组。 Object.getOwnPropertyDescriptors()会返回同时包罗通例和符号属性形貌符的对象。 Reflect.ownKeys()会返回两种范例的键
由于符号属性是对内存中符号的一个引用,以是直接创建并用作属性的符号不会丢失。但是,如果没有显式地生存对这些属性的引用,那么必须遍历对象的全部符号属性才气找到相应的属性键 - let o = {
- [Symbol('foo')]: 'foo val',
- [Symbol('bar')]: 'bar val'
- };
- let barSymbol = Object.getOwnPropertySymbols(o)
- .find((symbol) => symbol.toString().match(/bar/));
- console.log(barSymbol);
- // Symbol(bar)
复制代码 4.常用内置符号
用于袒露语言内部举动,开发者可以直接访问、重写或模拟这些举动。这些内置符号都以 Symbol 工厂函数字符串属性的情势存在 留意:经常会引用符号在规范中的名称,前缀为@@。比如,@@iterator 指的就是 Symbol.iterator 5.Symbol.asyncIterator
for-await-of 循环会使用这个函数实行异步迭代操纵
- class Emitter {
- constructor(max) {
- this.max = max;
- this.asyncIdx = 0;
- }
- // Symbol.asyncIterator为键的函数
- async *[Symbol.asyncIterator]() {
- while(this.asyncIdx < this.max) {
- yield new Promise((resolve) => resolve(this.asyncIdx++)); //返回一个实现迭代器 API 的对象
- }
- }
- }
- async function asyncCount() {
- let emitter = new Emitter(5);
- for await(const x of emitter) {
- console.log(x);
- }
- }
- asyncCount();
- // 0
- // 1
- // 2
- // 3
- // 4
复制代码 留意:Symbol.asyncIterator 是 ES2018 规范界说的,因此只有版本非常新的欣赏器支持它。
6.Symbol.hasInstance
该方法决定一个构造器对象是否认可一个对象是它的实例,由 instanceof 操纵符使用
- function Foo() {}
- let f = new Foo();
- console.log(f instanceof Foo); // true
- console.log(Foo[Symbol.hasInstance](f)); // true
复制代码 可以在继承的类上通过静态方法重新界说这个函数
- class Bar {}
- class Baz extends Bar {
- static [Symbol.hasInstance]() {
- return false;
- }
- }
- let b = new Baz();
- console.log(Bar[Symbol.hasInstance](b)); // true
- console.log(b instanceof Bar); // true
- console.log(Baz[Symbol.hasInstance](b)); // false
- console.log(b instanceof Baz); // false
复制代码 7.Symbol.isConcatSpreadable
这个符号作为一个属性体现一个布尔值,如果是 true,则意味着对象应该用 Array.prototype.concat()打平其数组元素。false 或假值会导致整个对象被追加到数组末端
- let initial = ['foo'];
- let array = ['bar'];
- console.log(array[Symbol.isConcatSpreadable]); // undefined
- console.log(initial.concat(array)); // ['foo', 'bar']
- array[Symbol.isConcatSpreadable] = false;
- console.log(initial.concat(array)); // ['foo', Array(1)]
复制代码 8.Symbol.iterator
返回对象默认的迭代器,由 for-of 语句使用,许多时间,返回的对象是实现该 API的 Generator
技能上,这个由 Symbol.iterator 函数天生的对象应该通过其 next()方法连续返回值,也可以隐式地通过天生器函数返回
- class Emitter {
- constructor(max) {
- this.max = max;
- this.idx = 0;
- }
- *[Symbol.iterator]() {
- while(this.idx < this.max) {
- yield this.idx++;
- }
- }
- }
- function count() {
- let emitter = new Emitter(5);
- for (const x of emitter) {
- console.log(x);
- }
- }
- count();
- // 0
- // 1
- // 2
- // 3
- // 4
复制代码 9.Symbol.match
一个正则表达式方法,该方法用正则表达式去匹配字符串。这个方法不作过多先容 10.Symbol.replace 一个正则表达式方法,该方法更换一个字符串中匹配的子串 - console.log('foobarbaz'.replace(/bar/, 'qux'));
- // 'fooquxbaz'
复制代码 给这个方法传入非正则表达式值会导致该值被转换为 RegExp 对象。如果想改变这种举动,让方法直接使用参数,可以重新界说 Symbol.replace 函数以代替默认对正则表达式求值的举动 - class StringReplacer {
- constructor(str) {
- this.str = str;
- }
- [Symbol.replace](target, replacement) {
- return target.split(this.str).join(replacement);
- }
- }
- console.log('barfoobaz'.replace(new StringReplacer('foo'), 'qux'));
- // "barquxbaz"
复制代码 11.symbol.search
一个正则表达式方法,该方法返回字符串中匹配正则表达式的索引 - console.log('foobar'.search(/bar/));
- // 3
复制代码 给这个方法传入非正则表达式值会导致该值被转换为 RegExp 对象,如果想改变这种举动,让方法直接使用参数,可以重新界说 Symbol.search 函数以代替默认对正则表达式求值的举动
- class StringSearcher {
- constructor(str) {
- this.str = str;
- }
- [Symbol.search](target) {
- return target.indexOf(this.str);
- }
- }
- console.log('foobar'.search(new StringSearcher('foo'))); // 0
复制代码 12.Symbol.species
一个函数值,该函数作为创建派生对象的构造函数 现在明确,简单来说派生对象就是父类,基类就是子类。在第一个例子中Bar继承了Array,在Symbol.species没有改变的情况下,Bar的实例对象bar既是Bar的实例也是Array的实例;在第二个例子中,Symbol.species将返回Array,而Array肯定不是Bar的实例,以是会返回false - class Bar extends Array {}
- class Baz extends Array {
- static get [Symbol.species]() {
- return Array;
- }
- }
- let bar = new Bar();
- console.log(bar instanceof Array); // true
- console.log(bar instanceof Bar); // true
- bar = bar.concat('bar');
- console.log(bar instanceof Array); // true
- console.log(bar instanceof Bar); // true
- let baz = new Baz();
- console.log(baz instanceof Array); // true
- console.log(baz instanceof Baz); // true
- baz = baz.concat('baz');
- console.log(baz instanceof Array); // true
- console.log(baz instanceof Baz); // false
复制代码 13.Symbol.split
一个正则表达式方法,该方法在匹配正则表达式的索引位置拆分字符串.给这个方法传入非正则表达式值会导致该值被转换为 RegExp 对象。如果想改变这种举动,让方法直接使用参数,可以重新界说 Symbol.split 函数以代替默认对正则表达式求值的举动 - class StringSplitter {
- constructor(str) {
- this.str = str;
- }
- [Symbol.split](target) {
- return target.split(this.str);
- }
- }
- console.log('barfoobaz'.split(new StringSplitter('foo')));
- // ["bar", "baz"]
复制代码 14.Symbol.toPrimitive
一个方法,该方法将对象转换为相应的原始值。许多内置操纵都会实验欺凌将对象转换为原始值,包罗字符串、数值和未指定的原始范例 - class Foo {}
- let foo = new Foo();
- console.log(3 + foo); // "3[object Object]"
- console.log(3 - foo); // NaN
- console.log(String(foo)); // "[object Object]"
- class Bar {
- constructor() {
- this[Symbol.toPrimitive] = function(hint) {
- switch (hint) {
- case 'number':
- return 3;
- case 'string':
- return 'string bar';
- case 'default':
- default:
- return 'default bar';
- }
- }
- }
- }
- let bar = new Bar();
- console.log(3 + bar); // "3default bar"
- console.log(3 - bar); // 0
- console.log(String(bar)); // "string bar"
复制代码 15. Symbol.toStringTag
一个字符串,该字符串用于创建对象的默认字符串形貌。由内置方法 Object.prototype.toString()使用
- let s = new Set();
- console.log(s); // Set(0) {}
- console.log(s.toString()); // [object Set]
- console.log(s[Symbol.toStringTag]); // Set
- class Foo {}
- let foo = new Foo();
- console.log(foo); // Foo {}
- console.log(foo.toString()); // [object Object]
- console.log(foo[Symbol.toStringTag]); // undefined
- class Bar {
- constructor() {
- this[Symbol.toStringTag] = 'Bar';
- }
- }
- let bar = new Bar();
- console.log(bar); // Bar {}
- console.log(bar.toString()); // [object Bar]
- console.log(bar[Symbol.toStringTag]); // Bar
复制代码 |