这节讲的这四个没有Symbol.iterator常见,但可能也会用到,由于浏览器对这些well-known symbols不是都支持,所以我们需要用Kinoma来测试这些特性。
先看比较简单的Symbol.toStringTag:
function Obj(){
}
Obj.prototype[Symbol.toStringTag] = "-Obj-";
let o1 = new Obj(),o2 = new Obj();;
trace(o1+"\n");//[object -Obj-]
o2[Symbol.toStringTag] = "-o2-Obj-";
trace(o2+"\n");//[object -o2-Obj-]
trace({} + "\n");//[object Object]
trace(Object.prototype.toString.call(o1)+"\n");//[object -Obj-]
trace(Object.prototype.toString.call(o2)+"\n");//[object -o2-Obj-]
trace(Object.prototype.toString.call({})+"\n");//[object Object]
通过上面的代码,可以看到ES6中对检测对象类型提供更灵活的特性,不在所有对象都是[object Object]了,你自己可以自定义。
接着Symbol.hasInstance,这个symbol是用来控制instanceof操作符的,关于instanceof的详细讲解,请参考这里
看个例子:
function Circle(r){
this.r = r;
}
Circle[Symbol.hasInstance] = function(inst){
return inst.r >= 0 && inst.r <= 10;//半径在0-10之内的我们才认为是个圆
}
var c1 = new Circle(5),c2 = new Circle(10),c3 = new Circle(15);
trace((c1 instanceof Circle) + "\n");//true
trace((c2 instanceof Circle) + "\n");//true
trace((c3 instanceof Circle) + "\n");//false
Object.defineProperty( Circle, Symbol.hasInstance, {
value: function(inst) {
return inst.r >= 0 && inst.r <= 10;
}
} );
在来看Symbol.species,这个接口比较有意思,不太好解释。是这样的,我们在使用Array的一些方法时,比如map,slice会返回一个新的数组,这个过程大家可能认为是理所当然的,但实际上返回的结果是可以控制的,通过的就是Symbol.species,实际写代码中,估计用到这个symbol的机会可能真不多。
下面看个例子:
class MyArray1 extends Array {
static get [Symbol.species]() {
return Array;
}
}
class MyArray2 extends Array {
}
let result1 = new MyArray1().map(x => x);
trace(result1 instanceof Array); // true
let result2 = new MyArray2().map(x => x);
trace(result2 instanceof MyArray2); // true
在ES6中,内置类都是可以继承的,在MyArray1中覆盖了Symbol.species实现,返回了Array,所以map出来的新数组是Array类型而不是MyArray类型。下面简单的看一下规范中map的描述,在22.1.3.15章节:
7. Let A be ArraySpeciesCreate(O, len).
方法ArraySpeciesCreate在9.4.2.3,章节:
6. If isArray is true, then
d. If Type(C) is Object, then
i. Let C be Get(C, @@species).
看到这个大概的过程了吧,数组的map,slice,filter,splice,concat方法都是这样的过程。当然还有其它对象的一些API也可能有用到species的时候。
大概知道有这个东西就行了,真正用的地方我感觉不是很多。
最后一个看Symbol.toPrimitive,这个symbol与引用类型和值类型做运算有关系,我们知道运算的时候,引用类型要有一个强制转换的过程,通过这个接口可以控制它。
var arr = [1,2,3,4,5];
trace(arr + 10 + "\n"); // 1,2,3,4,510
arr[Symbol.toPrimitive] = function(hint) {
if (hint == "default" || hint == "number") {
// sum all numbers
return this.reduce( function(acc,curr){
return acc + curr;
}, 0 );
}
};
trace(arr + 10); // 25
function Circle(r){
this.r = r;
}
Circle.prototype[Symbol.toPrimitive] = function(hint){
if(hint == "default" || hint == "number"){
return this.r;
}
else{
return this.r * this.r;
}
}
var c1 = new Circle(5),c2 = new Circle(10);
var c3 = new Circle(c1 + c2),c4 = new Circle(c2 - c1),c5 = new Circle(c1 * c2);
trace(String(c1) + "\n");//25
trace(String(c2) + "\n");//100
trace(String(c3) + "\n");//225
trace(String(c4) + "\n");//25
trace(String(c5) + "\n");//2500
*以上全部代码在Kinoma Studio中测试通过