🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## es6类功能测试 ### es5和es6比较 关键词:`静态方法`、`static` **注意事项**: 1. 我们仍然可以使用es5 `类.prototype` 的方式绑定公用方法和属性 2. es6虽然不支持静态属性,但可以通过 `类.x` 的方式达到静态属性的效果。 3. 我们无法像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](https://github.com/fancierpj0/es6-class)