ES6学习——新的语法:Symbols——Symbol.toPrimitive,Symbol.hasInstance,Symbol.toStringTag,Symbol.species

这节讲的这四个没有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

这里需要仔细看,这个symbol需要定义在Circle上,而不是Circle的prototype上。在ES5以后也可以用Object.defineProperty来搞定:

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

hint值是字符串,有三个:default,number和string。一般来讲+链接运算符传入的是default,乘法等算数运算符传入的是number,String(str)传入的是string,在看个对圆形简单操作的例子:

		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

觉得上面的过程是不是有点类似C++或者Swift中的操作符重载?其实这个toPrimitive过程在JS内部一直都有,只不过在ES6中把它暴露出来的,让程序员可以自定义这个过程。JS越来越强大了,但也越来越复杂了。


*以上全部代码在Kinoma Studio中测试通过


你可能感兴趣的:(ES6,ES6)