ES6学习——类语法:super和new.target

73 篇文章 23 订阅

这里为什么把super拿出来单独讲一下呢?可能有人要说,super谁不会用,先看个例子,你就知道super不是那么简单:

class ParentA {
	constructor() { this.id = "a"; }
	foo() { console.log( "ParentA:", this.id ); }
}

class ParentB {
	constructor() { this.id = "b"; }
	foo() { console.log( "ParentB:", this.id ); }
}

class ChildA extends ParentA {
	foo() {
		super.foo();
		console.log( "ChildA:", this.id );
	}
}

class ChildB extends ParentB {
	foo() {
		super.foo();
		console.log( "ChildB:", this.id );
	}
}
var a = new ChildA();
a.foo(); 
// ParentA: a
// ChildA: a
var b = new ChildB(); 
b.foo(); 
// ParentB: b
// ChildB: b


b.foo.call( a ); 
// ParentB: a
// ChildB: a

前两个foo的调用没什么,但最后一个b.foo.call的调用结果比较奇怪是不是?为什么this.id是a,但是super仍然是ParentB呢?

答案就是this可以动态绑定,即使是class,因为class实质上还是原型继承的封装。但super不能动态绑定,super在class声明的时候就定了。以后无论你怎么调用带有super的方法,查找这个方法的原型链已经定了。


我们看一下规范8.1.1.3的Table16,函数环境记录的额外字段:


上面这两个字段就是和这节要讲的super与new.target相关。对[[HomeObject]]的描述可以清楚的看出用super调用的函数是绑定在[[HomeObject]]上的。那么这个所谓的[[HomeObject]]是怎么设置的呢?

我们一点一点在规范中找:

规范的14.5.14 Runtime Semantics: ClassDefinitionEvaluation:

20. Else, let methods be NonConstructorMethodDefinitions of ClassBody.

21. For each ClassElement m in order from methods

    a. If IsStatic of m is false, then
        i. Let status be the result of performing PropertyDefinitionEvaluation for m with arguments proto and false.
    b. Else,
        i. Let status be the result of performing PropertyDefinitionEvaluation for m with arguments F and false.


接下来14.3.9 Runtime Semantics: PropertyDefinitionEvaluation:

MethodDefinition : PropertyName ( StrictFormalParameters ) { FunctionBody }
1. Let methodDef be DefineMethod of MethodDefinition with argument object.


继续14.3.8 Runtime Semantics: DefineMethod:

MethodDefinition : PropertyName ( StrictFormalParameters ) { FunctionBody }

7. Perform MakeMethod(closure, object).


最后9.2.10 MakeMethod ( F, homeObject):

The abstract operation MakeMethod with arguments F and homeObject configures F as a method by performing the following steps:
3. Set the [[HomeObject]] internal slot of F to homeObject.


终于看到设置[[HomeObject]]的地方了。这个F大家有兴趣可以自己看规范往前推,F就是子类的原型,对于ChildB就是ChildB.prototype,对于ChildA就是ChildA.prototype。我们看整个过程都是在ClassDefinitionEvaluation的时候完成,也就是说解析类定义的时候就定了这个[[HomeObject]],运行时不会在有什么改变。即使我们动态绑定了this,但用super去调用父类方法的查找起点已经不再改变了。


那具体怎么查找这个super上的方法呢,很简单,就是循环调用Object.getPrototypeOf([[HomeObject]]),直到找到为止:

var homeObject = ChildB.prototype,superObject;
do{
	superObject = Object.getPrototypeOf(homeObject); 
	var superMethod = superObject.foo;
	if(superMethod){
		var result = superMethod.call(a);
		break;
	}
        else{homeObject = superObject} 
}while(superObject != null);

这下弄明白了super的调用过程,所以尽量不要使用类上定义的方法去call或者apply动态绑定this了。


我们再来看看这个new.target是什么东西?先看个例子:

class Foo {
    constructor() {
        console.log( "Foo: ", new.target.name );
    }
}

class Bar extends Foo {
    constructor() {
        super();
        console.log( "Bar: ", new.target.name );
     }

    baz() {
        console.log( "baz: ", new.target );
    }
}

var a = new Foo();
// Foo: Foo

var b = new Bar();
// Foo: Bar
// Bar: Bar

b.baz();
// baz: undefined

其实new.target就是new一个对象时,对应的构造函数。并且无论在父类还是子类中都是一样的,就是new哪个类,就是那个类的构造函数。只有在new的时候有值,普通方法调用的时候new.target是undefined。

那么构造函数是怎么确定的呢?很简单,即:Foo.prototype.constructor,Bar.prototype.constructor。


*以上全部代码在Chrome 47下通过测试

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值