十. class 和 ES6转ES5阅读
10.1. class定义方式
按照前面的构造函数形式创建 类,不仅仅和编写普通的函数过于相似,而且代码并不容易理解。
- 在ES6(ECMAScript2015)新的标准中使用了class关键字来直接定义类;
- 但是类本质上依然是前面所讲的构造函数、原型链的语法糖而已;
可以使用两种方式来声明类:类声明和类表达式;
// 类的声明
class Person { }
// 类的表达式
let Student = class { }
10.2. class和构造函数的异同
对比后发现其实和我们的构造函数的特性其实是一致的;
class Person { }
let p=new Person()
console.log(Person); //[Function: Person]
console.log(Person.prototype); // Person {}
console.log(Person.prototype.constructor); // [Function: Person]
console.log(p.__proto__ === Person.prototype); // false
console.log(typeof Person); // function
10.3. class的构造函数
创建对象的时候如何给类传递一些参数?
-
每个类都可以有一个自己的构造函数(方法),这个方法的名称是固定的constructor;
- 每个类只能有一个构造函数,如果包含多个构造函数,那么会抛出异常;
-
当我们通过new操作符,操作一个类的时候会调用这个类的构造函数constructor;
- 构造器会创建出一个对象,并赋给给this
- class创建对象执行的操作和构造函数创建对象的原理一样的
-
直接在类中定义的实例方法,是放到原型上的
-
在类中可以直接添加setter和getter函数
- 函数名和属性名不能一样,比如下面是age和 _age,
-
静态方法通常用于定义直接使用类来执行的方法,不需要有类的实例,使用static关键字来定义:
class Person {
constructor(name, age) {
// constructor中定义的属性是放到对象上的
this.name = name
this._age = age // 私有属性,不希望被在对象方法的外部被访问
}
// class中定义的属性是放到Person.prototype上的
eating=function(){
console.log("这是一个函数");
}
// 类的访问器方法
set age(newAge) {
// 可拦截修改操作
this._age = newAge
}
get age() {
// 可拦截访问操作
return this._age
}
// 类的静态方法(也叫类方法):不用创建对象,直接用 对象.方法 进行访问
static randomCreatePerson(){
let nameIndex= Math.floor(Math.random() * names.length)
let name= names[nameIndex]
let age= Math.floor(Math.random()*100)
return new Person(name,age)
}
}
let p = new Person("why", 12)
p.age = 20
console.log(p.age);
let names= ['张三','李四','王五','赵六']
console.log(Person.randomCreatePerson());
10.4. extends_ super_ static_ 重写
extends:
在ES5中实现继承的方案,过程却是非常繁琐的。ES6中新增了使用extends关键字,可以方便的帮助我们实现继承。
super:
super关键字有不同的使用方式:
- 注意:语法规定:在子(派生)类的构造函数中使用this或者返回默认对象之前,必须先通过super调用父类的构造函数!
- super的使用位置有三个:子类的构造函数、实例方法、静态方法;
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
eating() {
console.log("chi");
}
personMethod() {
console.log("处理逻辑1");
console.log("处理逻辑2");
console.log("处理逻辑3");
}
static staticMethod() {
console.log("父类的静态方法");
}
}
class Student extends Person {
// 如果有实现继承,在子类构造方法中,使用this之前必须调用super()
constructor(name, age, sno) {
// 复用父类构造函数
super(name, age)
this.sno = sno
}
stydying() {
console.log("xuexi");
}
// 子类对父类方法的重写
eating() {
console.log("子类重写了eating");
}
// 重写personMethod方法
personMethod() {
// 复用父类中的personMethod
super.personMethod()
console.log("处理逻辑4");
console.log("处理逻辑5");
console.log("处理逻辑6");
}
// 重写staticMethod静态方法
static staticMethod() {
super.staticMethod()
console.log("子类的静态方法");
}
}
let stu = new Student("张三", 18, 111)
// 子类class内定义的实例方法(如stydying方法),是加在子类的原型上的,反之父类也是
console.log(Object.getOwnPropertyDescriptors(stu.__proto__));
console.log(Object.getOwnPropertyDescriptors(stu.__proto__.__proto__));
stu.eating() // 子类重写了eating
stu.personMethod() // 重写并复用personMethod方法
stu.staticMethod() // 重写并复用staticMethod静态方法
10.5. 继承内置类_ 混入_ 多态
- 继承内置类并拓展一些方法
class hyArray extends Array{
firstItem(){
return this[0]
}
lastItem(){
return this[this.length-1]
}
}
let arr =new hyArray(1,2,3)
console.log(arr.firstItem()); // 1
console.log(arr.lastItem()); // 3
- JavaScript的类只支持单继承:也就是只能有一个父类
- 如果需要在一个类中添加更多相似的功能时,可以使用混入(mixin);
class Person {}
function mixinRunner(BaseClass) {
class NewClass extends BaseClass {
runing() {
console.log("runing");
}
}
return NewClass
}
// 在js中 类只能有一个父类:单继承
class Student extends Person {}
let NewStudent = mixinRunner(Student)
let ns = new NewStudent()
ns.runing()
- 不同的数据类型进行同一个操作,表现出不同的行为,就是多态的体现。
function sum(a, b) {
console.log(a + b);
}
sum(10, 20)
sum("aa", "bb")
10.6. label ES6转ES5源码阅读
v1:没有继承和静态方法
class Person{
constructor(name,age) {
this.name=name
this.age=age
}
eating(){
console.log(`${this.name}eating`);
}
}
下面是转成ie10的es5代码
"use strict";
function _classCallCheck(instance, Constructor) {
// undefind 或者 window是不是Person的实例,是就抛错
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
// target:Person原型,props:数组
function _defineProperties(target, props) {
// 遍历数组
for (var i = 0; i < props.length; i++) {
// descriptor:数组中第一个对象
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
// 用属性描述符给Person添加一个属性,并返回该对象
Object.defineProperty(target, descriptor.key, descriptor);
}
}
// Constructor:Person, protoProps:数组, staticProps:静态方法
function _createClass(Constructor, protoProps, staticProps) {
// 判断第二个参数是否有值,有就执行_defineProperties,并将Person原型和第二个参数传进去
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
Object.defineProperty(Constructor, "prototype", {
writable: false
});
return Constructor;
}
// /*#__PURE__*/: 标记这是个纯函数。
// 作用:webpack压缩时发现Person从头到尾都没有用过,就用tree-shaking优化,将Person代码直接删掉
var Person = /*#__PURE__*/ (function () {
function Person(name, age) {
// 如果是new Person() this就是Person的实例对象.
// 如果是Person(),严格模式下this是undefined; 非严格模式下this是window,
// 这里是为了防止Person()这样调用
_classCallCheck(this, Person);
this.name = name;
this.age = age;
}
// eating(){console.log(`${this.name}eating`);}
封装了_createClass方法往原型上加eating方法
// 把东西抽离出来是为了以后的复用
_createClass(Person, [{
key: "eating",
value: function eating() {
console.log("".concat(this.name, "eating"));
}
}]);
return Person;
})();
v2:有继承
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
runing() {
console.log(this.name + "runing");
}
}
class Student extends Person {
constructor(name, age, sno) {
super(name,age)
this.sno = sno
}
studying() {
console.log(this.name + "studying");
}
}
下面是转成ie10的es5代码
"use strict";
function _typeof(obj) {
"@babel/helpers - typeof";
return (
(_typeof =
"function" == typeof Symbol && "symbol" == typeof Symbol.iterator ?
function (obj) {
return typeof obj;
} :
function (obj) {
return obj &&
"function" == typeof Symbol &&
obj.constructor === Symbol &&
obj !== Symbol.prototype ?
"symbol" :
typeof obj;
}),
_typeof(obj)
);
}
// 目的:实现寄生式继承
function _inherits(subClass, superClass) {
// 只要有一个返回false,结果就为false;目的是要求子类是一个函数,父类不是null
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
// 1.将创建新对象的隐式原型指向父类的显示原型,然后给新建的对象添加一个constructor属性
// 2.再将新对象赋值给子类的显示原型
// 注:这里的实现思路和寄生组合式继承一样,目的:子类.prototype= 新对象, 新的对象.__proto__= 父类.prototype,
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
Object.defineProperty(subClass, "prototype", {
writable: false
});
// 如果父类有值就执行_setPrototypeOf()
// 目的:静态方法的继承
if (superClass) _setPrototypeOf(subClass, superClass);
}
// 目的:实现静态方法的继承, 实现思路:子类的__proto__ = 父类. o:子类, p:父类
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf ||
function _setPrototypeOf(o, p) {
// 为什么子类的__proto__ = 父类,就可以继承静态方法?
// 答: 假如父类有个static staticMethod(){}, 子类调用时,发现自身没有该方法,就会去子类的隐式原型找,也就是Function.prototepe
// Function的显示原型根本就没有这个方法,所以我们让子类的__proto__= 父类,而父类里是有staticMethod方法的,从而实现了静态方法继承
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _createSuper(Derived) {
// 判断当前是否支持Reflect true
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived), // 父类(name,age){}
result; // undefined
if (hasNativeReflectConstruct) {
// 这里的this是_createSuperInternal也就是_super()显示绑定的Student实例对象
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
function _possibleConstructorReturn(self, call) {
if (call && (_typeof(call) === "object" || typeof call === "function")) {
return call;
} else if (call !== void 0) {
throw new TypeError(
"Derived constructors may only return object or undefined"
);
}
return _assertThisInitialized(self);
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError(
"this hasn't been initialised - super() hasn't been called"
);
}
return self;
}
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Boolean.prototype.valueOf.call(
Reflect.construct(Boolean, [], function () {})
);
return true;
} catch (e) {
return false;
}
}
// o:子类
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ?
Object.getPrototypeOf :
function _getPrototypeOf(o) {
// 返回子类的原型对象,_setPrototypeOf已经把子类的__proto__ = 父类,所以这里直接返回父类
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
// 和之前一样
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
// 和之前一样
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
// 和之前一样
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
Object.defineProperty(Constructor, "prototype", {
writable: false
});
return Constructor;
}
var Person = /*#__PURE__*/ (function () {
function Person(name, age) {
debugger
_classCallCheck(this, Person);
this.name = name;
this.age = age;
}
_createClass(Person, [{
key: "runing",
value: function runing() {
console.log(this.name + "runing");
}
}]);
return Person;
})();
// Student是个立即执行函数,参数是Person. /*#__PURE__*/: 标注为纯函数,
var Student = /*#__PURE__*/ (function (_Person) {
// 让Student继承Person(包括静态方法的继承),和寄生式继承核心一样,
_inherits(Student, _Person);
// 父类不能直接被调用,所以这里封装了一个_super函数
debugger;
var _super = _createSuper(Student);
function Student(name, age, sno) {
var _this;
_classCallCheck(this, Student);
debugger;
// 为什么不和手写的寄生式组合继承一样去调用父类的构造方法 Person.call(this, name, age, friends)
// 答: 父类不能当成一个函数直接去调用,否则报错, _classCallCheck()对调用做了限制
_this = _super.call(this, name, age);
_this.sno = sno;
return _this;
}
_createClass(Student, [{
key: "studying",
value: function studying() {
console.log(this.name + "studying");
}
}]);
return Student;
})(Person);
// new Student()