《JavaScript高级程序设计》(第4版)阅读笔记(十二)

这篇文章继续分享第三章的内容,目前看到了系统内置的一些Symbol,也就是对象的一些固有属性。

12. Symbol.species

据ECMAScript规范,这个符号作为一个属性表示“一个函数值,该函数作为创建派生对象的构造函数”。这个属性在内置类型中最常用,用于对内置类型实例方法的返回值暴露实例化派生对象的方法。用 Symbol.species 定义静态的获取器(getter)方法,可以覆盖新创建实例的原型定义

(下面的代码举了两个例子,Bar和Baz两个类都是内置类型Array的派生类。

其中Bar没有做什么设置,只是一个安安静静、普普通通的派生类,所以Bar和Array的prototype都在它实例bar的原型链上,它可以使用Array.prototype上的concat方法,使用后没有什么改变。

而Baz用Symbol.species方法定义了静态的get。一开始,它也是普普通通的,它的实例baz的原型链上确实有Baz和Array的prototype。但是后面它要调用concat方法,它本身并没有这个方法,所以到原型里去获取。它一去获取类型,就调用了Symbo.species定义的get方法,显然返回的是Array。它的原型就变成了Array的prototype。因此,Array.prototype仍然在它的原型链上,但是跳过了Baz,所以最后的结果为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

根据ECMAScript规范,这个符号作为一个属性表示“一个正则表达式方法,该方法在匹配正则表达式的索引位置拆分字符串。由String.prototype.split() 方法使用”。String.prototype.split() 方法会使用以Symbol.split 为键的函数来对正则表达式求值。正则表达式的原型上默认有这个函数的定义,因此所有正则表达式实例默认是这个 String 方法的有效参数。

console.log(RegExp.prototype[Symbol.split]);
// f [Symbol.split]() { [native code] }
console.log('foobarbaz'.split(/bar/));
// ['foo', 'baz']

给这个方法传入非正则表达式值会导致该值被转换为 RegExp 对象。如果想改变这种行为,让方法直接使用参数,可以重新定义Symbol.split 函数以取代默认对正则表达式求值的行为,从而让 split() 方法使用非正则表达式实例。 Symbol.split函数接收一个参数,就是调用 split() 方法的字符串实例。返回的值没有限制。

class FooSplitter {
    static [Symbol.split](target) {
        return target.split('foo');
    }
} 
console.log('barfoobaz'.split(FooSplitter));// ["bar", "baz"]


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


根据ECMAScript规范,这个符号作为一个属性表示“一个方法,该方法将对象转换为相应的原始值由 ToPrimitive 抽象操作使用”。很多内置操作都会尝试强制将对象转换为原始值,包括字符串、数值和未指定的原始类型。对于一个自定义对象实例,通过在这个实例的 Symbol.toPrimitive 属性上定义一个函数可以改变默认行为

(Symbol.toPrimitive函数被调用时,会被传递一个字符串参数 hint ,表示要转换到的原始值的预期类型。 hint 参数的取值是 "number""string" 和 "default" 中的任意一个)

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"

对象遇到“-”,肯定是转成number,遇到String,肯定是转成string类型,遇到“+”按照“default”来转换。

15. Symbol.toStringTag

根据ECMAScript规范,这个符号作为一个属性表示“一个字符串,该字符串用于创建对象的默认字符串描述。由内置方法Object.prototype.toString() 使用”通过 toString() 方法获取对象标识时,会检索由Symbol.toStringTag 指定的实例标识符,默认为 "Object" 。内置类型已经指定了这个值,但自定义类实例还需要明确定义

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

(上面的代码分别展示了3种情况,内置类型,未定义[Symbol.toStringTag]的自定义类型,和定义了[Symbol.toStringTag]的自定义类型。

如果调用对象的toString方法,而且这个对象并没有重写Object.prototype.toString,那么就会输出“[object xxx]”,xxx就是这个对象的[Symbol.toStringTag]。如果对象没有设置[Symbol.toStringTag],则输出“[object Object]”。)

16. Symbol.unscopables

根据ECMAScript规范,这个符号作为一个属性表示“一个对象,该对象所有的以及继承的属性,都会从关联对象的 with 环境绑定中排除”。设置这个符号并让其映射对应属性的键值为
true ,就可以阻止该属性出现在 with 环境绑定中。

(本来foo属性可以在with环境中使用的,但在把[Symbol.unscopables]里面设置foo为true后,就不能在with环境里使用了)

let o = { foo: 'bar' };
with (o) {
    console.log(foo); // bar
} 
o[Symbol.unscopables] = {
    foo: true
};
with (o) {
    console.log(foo); // ReferenceError
}

注意 不推荐使用 with ,因此也不推荐使用Symbol.unscopables 。

3.4.8 Object 类型

ECMAScript中的对象其实就是一组数据和功能的集合对象通过 new 操作符后跟对象类型的名称来创建。开发者可以通过创建Object 类型的实例来创建自己的对象,然后再给对象添加属性和方法。

这个语法类似Java,但ECMAScript只要求在给构造函数提供参数时使用括号。如果没有参数,如上面的例子所示,那么完全可以省略括号(不推荐)
Object 的实例本身并不是很有用,但理解与它相关的概念非常重要。类似Java中的 java.lang.Object ,ECMAScript中的Object 也是派生其他对象的基类。 Object 类型的所有属性和方
法在派生的对象上同样存在。

每个 Object 实例都有如下属性和方法。

constructor :用于创建当前对象的函数
hasOwnProperty(propertyName) :用于判断当前对象实例(不是原型)上是否存在给定的属性。要检查的属性名必须是字符串(如 o.hasOwnProperty("name") )。
isPrototypeof(object) :用于判断当前对象是否为另一个对象的原型
propertyIsEnumerable(propertyName) :用于判断给定的属性是否可以使用 for-in 语句枚举。与 hasOwnProperty() 一样,属性名必须是字符串。
toLocaleString() :返回对象的字符串表示,该字符串反映对象所在的本地化执行环境
toString() :返回对象的字符串表示
valueOf() :返回对象对应的字符串、数值或布尔值表示。通常与 toString() 的返回值相同

任何对象都有这些属性和方法。

注意 严格来讲,ECMA-262中对象的行为不一定适合JavaScript中的其他对象。比如浏览器环境中的BOM和DOM对象,都是由宿主环境定义和提供的宿主对象。而宿主对象不受ECMA-262约束,所以它们可能会也可能不会继承 Object

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值