1. 前言
ES6新增的Class(类)给我们编程带来了极大方便,有人说这实际上是函数的语法糖,我是赞同的,但词法作用域和原型链是Javascript的核心思想,Class是基于原型链实现的,函数也是基于原型链实现的,这两者必然有一些共性,甚至使用 typeof 类标识符考察类到底是一个什么东西?控制台会告诉你这是一个function。然而,Class作为一个新特性,它有函数该有的东西,它也有函数没有的东西,这是它强大的地方。
2. Class本质上是一个特别的函数
class Polygon {
constructor(height, width) {
this.name = 'Polygon';
this.height = height;
this.width = width;
}
sayName() {
console.log('Hi, I am a ', this.name + '.');
}
}
class Square extends Polygon {
constructor(length) {
super(length, length);
this.name = 'Square';
}
get area() {
return this.height * this.width;
}
set area(value) {
this.area = value;
}
}
var mySquare = new Square(4,4);
typeof Polygon //function 控制台输入typeof Polygon, 它会告诉你这是一个函数
console.dir(Polygon) //反射查看Polygon类的属性
3. Class比函数多一根原型链
我们说函数supF是函数subF的父类,是因为subF.__protol__ = sup.prototype,即子函数的原型对象sub.prototype被链接到父函数supF的原型对象supF.prototype。但是两个父子函数supF和subF本身是独立的,没有任何关系,它们是靠原型对象关联到了一起。Class恰恰把这根链接上了。即子类自己的__protol__指向了父类。简单理解:extend关键字将类和类原型链接到了两根不同的链。
4. super伪引用
我们知道this绑定是动态绑定,super也是动态绑定的吗?super一定指向对象原型链的上级吗?类有两根原型链,那到底是哪根的上级呢?对于Javascript而言,没有类也是可以创建对象的,我们可以使用Object.setPrototypeOf(),随时将一个对象链接到另一个对象,这让super的指向更加复杂?其实也没那么复杂,如前言所讲,Javascript的核心思想:词法作用域链和原型链,函数被定义的位置和被调用的形式,这些能给你答案。
函数是一个独立的特殊对象,它可以作为对象的方法和普通对象(object)关联,这种关联仅仅是一种动态关系,谁也不依赖谁,谁也可以没有谁。但是函数被创建时关联的对象很重要,它是函数一出生就看见的对象,函数长大了,会飞了,换了很多环境生活,但是它的故乡只有一个,称为[[HomeObject]],它出生时就会用[[HomeObject]]记录这个故乡(对象)。
简单点说: super指向封闭函数定义的对象在原型链中的上级。
class P {
foo() {console.log("P.foo");}
}
class C extends P {
//静态方法的[[HomeObject]]指向Class
//实例方法的[[HomeObject]]指向实例
foo() {
/*
super指向主调函数foo.[[HomeObject]].[[prototype]]
即定义函数的对象的原型链上级
*/
super.foo();
}
}
var c1 = new C();
c1.foo();
var D = {
foo: function() {
console.log("D.foo");
}
};
var E = {
foo: C.prototype.foo
};
Object.setPrototypeOf(E,D);
E.foo();