- es6类功能测试
- es5和es6比较
- 关于继承
- 关于类的构造函数的指向
- 使用es5实现es6的类
- 源码
es6类功能测试
es5和es6比较
关键词:静态方法
、static
注意事项:
- 我们仍然可以使用es5
类.prototype
的方式绑定公用方法和属性 - es6虽然不支持静态属性,但可以通过
类.x
的方式达到静态属性的效果。 - 我们无法像es5构造函数一样将
类
当做函数执行
// 1)
class Child{
constructor(){
}
//静态方法
static echo(){
console.log('这是一个静态方法');
}
}
Child.a = 1; //相当于静态属性
console.log(Child.a); //1
Child.echo(); //这是一个静态方法
Child.prototype.b = 2;
let c1 = new Child();
console.log(c1.b); //2
Child(); //TypeError: Class constructor Child cannot be invoked without 'new'
复制代码
关于继承
- 当不填
constructor
时,使用了继承的子类可以顺利得到父类给实例的私有属性和方法
。 - 子类可以继承到父类原型上的方法以及父类的静态方法
// 1)
class Parent{
constructor(){
this.a = 1; //私有属性
}
echo(){
console.log('我是Parent中的方法');
}
}
class Child extends Parent{
//没有填写constructor
}
let c1 = new Child();
console.log(c1) //{a:1}
console.log(c1.constructor) // [Function:Child]
c1.echo(); //我是Parent中的方法
复制代码
- 当填了
constructor
,必须调用super
方法,否则会报以下错误。
// 2)
class Parent{
constructor(){}
}
class Child extends Parent{
constructor(){}
}
let c1 = new Child();
<<<
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
复制代码
- 当父类在构造函数中返回一个对象时,子类的实例将会是这个对象。
- 这个对象不会包含父类以及子类原型上的方法(但子类仍然可以继承到父类的静态方法)
- 这个对象的
constructor
指向的是Object构造函数
,而不是子类或父类的构造函数
// 3)
class Parent{
constructor(){
this.a = 2;
return {
a:1
};
}
echo(){
console.log('我是Parent中的方法');
}
static echo(){
console.log('我是Parent中的静态方法');
}
}
class Child extends Parent{
constructor(){
super();
this.b = 2;
}
notice(){
console.log('我是子类中的方法')
}
}
let c1 = new Child();
console.log(c1); //{a:1,b:2}
console.log(c1.constructor); //Function Object
console.log(c1 instanceof Child); //false
console.log(c1 instanceof Parent); //false
console.log(c1 instanceof Object); //true
Child.echo(); //我是Parent中的静态方法
c1.notice(); //c1.notice is not a function
c1.echo(); //TypeError: c1.echo is not a function
复制代码
关于类的构造函数的指向
构造函数的指向(__proto__
)是指向 Function
构造函数的原型的
console.log(Object.getPrototypeOf(a)===Function.prototype); //true
复制代码
注意:
这里说的是构造函数的指向,而不是构造函数原型的指向
使用es5实现es6的类
首先我们先定义两个构造函数
function Parent(){};
function Child(){};
复制代码
如果是es6,我们要实现继承的话只需要Child extends Parent
即可,但es5显然是不存在这种语法糖的,我们需要通过把构造函数包装进一个函数中(这个函数其实就是所谓的类),通过函数自执行时传入的参数来决定这个类继承自谁。
这也是通过class创建的类,去掉语法糖的皮,编译过后,真正的运行时的样子。
var Child = function(Parent){
_inherits(Child,Parent);
function Child(){}
return Child;
}(Parent)
var Parent = function(){
function Parent(){};
return Parent;
}()
复制代码
注意我们在匿名函数自执行时使用了一个方法 _inherits
来具体实现类的继承。
function _inherits(subCon,superCon){
// 继承父类prototype上的方法(公有方法)
let subCon.prototype = Object.create(superCon.prototype,{constructor:{value:subCon}});
// 继承父类的static方法(静态方法)
subCon.__proto__ = superCon;
}
复制代码
除此之外子类还需要继承父类的私有属性和方法
var Child = function(Parent){
...
function Child(){
Object.getPrototypeOf(Child).call(this); //在上面的_inheris方法中我们已经将Child__proto__ = Parent,故这里的getPrototypeOf 即为 Parent
}
return Child;
}(Parent)
复制代码
并且当父类返回的是一个对象时,我们子类实例化时返回的对象也要变成这个父类返回的对象。
var Child = function(Parent){
...
function Child(){
let ret = this;
let o = Object.getPrototype(Child).call(this);
if(typeof o === 'object'){
ret = o;
// 还可以在这里进行一些子类的私有属性和方法的挂载
}
return ret;
}
return Child;
}(Parent)
复制代码
除此之外,我们需要确保类只能用 new
来实例化,而不能单独执行。(我们不能像es5一样让构造函数像普通函数一样执行)
So我们在构造函数调用时候使用了一个 __classCallCheck
方法来检查类
这个方法之所以有效的原因在于,如果是像调用普通函数一样调用类,那么此时的 this
应该指向的是 window
or undefined
,这两货显然不是Child的实例。
function _classCallCheck(instance,constructor){ //检查当前类 有没有使用new
if(!(instance instanceof constructor)) throw Error('Without new');
}
...
function Child(){
_classCallCheck(this,Child);
...
}
...
复制代码
另外当我们在 class
中声明一个公共方法或则静态方法时,内部其实调用的是 defineProperty
来给构造函数的原型和构造函数本身上添加属性来实现的。
...
function Parent(){
...
_createClass(Parent,[
//公共方法
{key:'publicFn',value:function(){
console.log(1);
}}
...
],[
//静态方法
{key:'staticFn',value:function(){
console.log(2);
}}
])
}
...
function _createClass(target,protoProperties,staticProperties){
if(protoProperties){
defineProperties(target.prototype,protoProperties);
}
if(staticProperties){
defineProperties(target,staticProperties);
}
}
function defineProperties(target,properties){
var conf = {configurable:true,writable:true,enumerable:true}
for(var i=0;i<properties.length;++i){
conf.value = properties[i].value;
Object.defineProperty(target,properties[i].key,conf);
}
}
复制代码
源码
点击获取源码 github