1.认识class定义类
我们会发现,按照前面的构造函数形式创建类,不仅仅和编写普通的函数过于相似,而且代码并不容易理解。
- 在ES6(ECMAScript2015)新的标准中使用了class关键字来直接定义类
- 但是类本质上依然是前面所讲的构造函数、原型链的语法糖而已
- 所以学号了前面的构造函数、原型链更有利于我们理解类的概念和继承关系
1.1 类的定义和类的特性
类的定义的两种方式:
- 类的声明
- 类的表达式
// * 类的声明
class Person{
}
// * 类的表达式
var Animal=class{
}
类的特性:
- 类也有prototype和[[prototype]]
- 类的prototype中也有constructor属性,也是不可枚举的
- 创建的实例对象的[[prototype]]指向的是类的prototype (跟构造函数创造出来对象一样)
- 使用typeof 类名 会发现 打印出来的是function
- 其实类的实质还是一个构造函数
// * 研究类的特点
console.log(Person.prototype);//{}
console.log(Person.prototype.__proto__);//[Object: null prototype] {}
console.log(Person.prototype.constructor); //[class Person]
console.log(typeof Person);//function
// typeof只会打印固定的几个
var p=new Person();
console.log(p.__proto__);//{}
console.log(p.__proto__==Person.prototype); //true
// * class是之前es5实现的语法糖形式而已
// function Person2(){}
// console.log(Person2.prototype.constructor);//[Function: Person2]
// // * 声明一个函数
// function fpp(){
// }
// // * 函数表达式
// var foo=function(){
// }
1.2 类的构造函数
如果我们希望在创建对象的时候给类传递一些参数,这个时候应该如何做呢?
- 每个类都可以由一个自己的构造函数(方法),这个方法的名称是constructor(类的原型对象上的constructor是指向的这个类的)
- 当我们通过new操作符操作一个类的时候,会自动调用这个类的构造函数constructor
- 每个类只能有一个构造函数,如果包含多个构造函数,那么就会抛出异常。
当我们通过new关键字操作类的时候,会调用这个constructor函数,并且执行如下操作:
- 在内存中创建一个新的对象(空对象)
- 在这个对象内部的[[prototype]]属性会被赋值为该类的prototype属性
- 构造函数内部的this指向这个创建出来的新对象
- 执行构造函数内部的代码
- 如果构造函数没有返回其他非空对象,则返回创建出来的新对象
// * 类的声明
class Person{
// * 类的构造方法 通过new关键字创建一个对象时,会调用这个构造方法
// * 一个类只有一个构造方法
// * 通过new关键字会执行如下几个步骤:
// * 1 在内存中创建一个新的对象(空对象)
// * 2 将这个新对象的__proto__执行类的prototype
// * 3 将构造方法的this指向 这个新创建的对象
// * 4 执行构造函数内部的代码
// * 5 如果构造函数没有其他的返回对象,会将这个新对象返回
constructor(name,age){
this.name=name;
this.age=age;
}
}
var p=new Person("wjy",20);
console.log(p);//Person { name: 'wjy', age: 20 }
1.3 类的实例方法、访问器方法、静态方法
-
类的实例方法,只能通过对象.方法名 它是放在类的原型对象上
- eating和running都是实例方法
-
类的静态方法:通常用于定义直接使用类来执行的方法,不需要有类的实例,它是放类上,没有放在类的原型对象上
-
方法名前 需要加 static 修饰
-
只可以通过类名.方法名
-
比如 :randomPerson
-
-
访问器方法:get address()和set address,它是放在类的原型对象上
var names=["abc","efg","zs","ls","ww"]
class Person{
constructor(name,age){
this.name=name;
this.age=age;
this._address="怀化"
}
// * eating和running是实例方法,只能通过对象.方法名才能访问
eating(){
console.log(this.name+"在吃东西");
}
running(){
console.log(this.name+"在跑步");
}
// * 类的访问方法
get address(){
return this._address;
}
set address(value){
this._address=value
}
// * 类的静态方法(类名) 可以通过类名进行访问 也可以通过对象来访问
static randomPerson(){
// * Math.floor 向下取整
var nameIndex=Math.floor(Math.random()*names.length)
var name=names[nameIndex];
var age=Math.floor(Math.floor(Math.random()*100))//* 向下取整0-100但不包含100 向上取值就不会包含 0了 1-100
return new Person(name,age);
}
}
var p=new Person("wjy",20)
console.log(p);
// * 判断类定义的方法是每个实例对象都会创建一份,还是共享一份
p.eating();
p.running();
var p2=new Person("hyz",21)
// * 打印出现发现都是true,所以类定义的方法是共享的,不是每个实例对象内部会创建一份
console.log(p.eating==p2.eating);//true
console.log(p.running==p2.running);//true
console.log(p.eating==Person.prototype.eating);
console.log(Object.getOwnPropertyDescriptors(Person.prototype));
console.log(p.address);//怀化
p.address="北京"
console.log(p.address);//北京
for(var i=0;i<50;i++){
console.log(Person.randomPerson());
}
1.4 ES6类的继承 -extends
前面我们花了很大的篇幅讨论了在ES5中实现继承的方案,虽然最终实现了相对满意的继承机制,但是过程却依然非常繁琐的
- 在ES6中新增了使用了extends关键字,可以方便的帮助我们实现继承
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
}
class Student extends Person{ //* 实现了 Student 继承了Person
constructor(name,age,sno){
// * 怎么将父类的属性继承过来呢
super(name,age);
this.sno=sno;
}
}
1.4.1 super关键字
我们会发现在上面的代码中我使用了一个super关键字,这个super关键字有不同的使用方式:
- 注意:在子类(派生类)的构造函数中使用this或者返回默认对象之前,必须先通过super调用父类的构造函数!
- super使用的位置有三个:子类的构造函数、实例方法、静态方法
请注意看下面的:如果我不用父类的构造函数,在子类直接绑定在自己属性上,但是在创建实例对象的时候会报错
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
}
class Student extends Person{ //* 实现了 Student 继承了Person
constructor(name,age,sno){
this.name=name;
this.age=age;
this.sno=sno;
}
}
var stu=new Student("wjy",20,1)
43.png
1.4.1 super的两种用法
-
调用父类的构造方法
- super()
-
调用父类的实例方法
- super.方法名(参数)
-
调用父类的静态方法
- super.方法名(参数)
可以重写父类的方法也可以在重写的过程中,对父类代码的复用,通过使用super关键字来实现的。
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
running(){
console.log(this.name+"在跑步");
}
eating(){
console.log(this.name+"在吃东西");
}
personMethod(){
console.log("处理逻辑1");
console.log("处理逻辑2");
console.log("处理逻辑3");
}
static staticMethod(){
console.log("PersonStaticMethod");
}
}
class Student extends Person{ //* 实现了 Student 继承了Person
constructor(name,age,sno){
super(name,age);
this.sno=sno;
}
studying(){
console.log(this.name+"在学习");
}
// * 子类对父类方法的重写
running(){
console.log("student "+this.name+"在跑步");
}
// * 重写personMethod,并父类实例方法的代码逻辑
personMethod(){
super.personMethod()
// console.log("处理逻辑1");
// console.log("处理逻辑2");
// console.log("处理逻辑3");
console.log("处理逻辑4");
console.log("处理逻辑5");
console.log("处理逻辑6");
}
// * 重写父类静态方法,也可以复用静态方法的代码逻辑
static staticMethod(){
console.log("studentStaticMethod");
}
}
var stu=new Student("wjy",20,1)
console.log(stu);
stu.eating()
stu.running()
console.log(Object.getOwnPropertyDescriptors(stu.__proto__));//* Student原型上没有eating和running方法
stu.personMethod()
Student.PersonStaticMethod()
44.png
2.es6转es5代码分析
babel官网:https://babeljs.io/
2.1 ES6的class转ES5的源码阅读
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
eating(){
console.log(this.name+"在吃东西");
}
running(){
console.log(this.name+"在跑步");
}
}
// * 一些浏览器对es6的语法不支持,不能正确的识别
"use strict";
function _classCallCheck(instance, Constructor) {
// 判断这个构造哈函数的prototype是否在实例对象的原型链上
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;
}
// * 使用的是立即执行函数
// /*#__PURE__*/ 纯函数:没有副作用,webpack进行压缩的时候,会涉及到tree-shaking:将一些从来没有用到的代码会删掉
var Person = /*#__PURE__*/ (function () {
// * 定义了一个Person的构造函数
function Person(name, age) {
// 判断这个this是不是通过new关键字进行调用的
_classCallCheck(this, Person);
this.name = name;
this.age = age;
}
_createClass(Person, [
{
key: "eating",
value: function eating() {
console.log(this.name + "在吃东西");
}
},
{
key: "running",
value: function running() {
console.log(this.name + "在跑步");
}
}
]);
return Person;
})();
2.2 ES6的继承转ES5的源码阅读
class Person{
constructor(name,age){
this.name=name;
this.age=age;
}
running(){
console.log(this.name+"在跑步。");
}
}
class Student extends Person{
constructor(name,age,sno) {
super(name,age);
this.sno=sno;
}
studying(){
console.log(this.name+"在学习");
}
}
// * 转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) {
// * 判断传递superClass是否为function或者为null
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
// * 将子类的原型对象指向 对象(这个对象的__proto__指向的是父类的prototype)
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { value: subClass, writable: true, configurable: true }
});
Object.defineProperty(subClass, "prototype", { writable: false });
// * 这个是为了让静态方法的继承
if (superClass) _setPrototypeOf(subClass, superClass);
}
// * 设置子类的__proto__指向父类
function _setPrototypeOf(o, p) {
_setPrototypeOf =
Object.setPrototypeOf ||
function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
// * 创建一个新的父类函数
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
// * 变量可以定义多个
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
// Super:Person
// arguments:参数
// NewTarget:Student
// 会通过Super创建出来一个实例,这个实例的原型constructor指向的是NewTarget
// 会通过Person创建一个实例,这个实例的原型指向的是 Person
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;
}
}
// * 获取对象的__proto__属性
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf
? Object.getPrototypeOf
: function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
// * 判断是否是通过new关键字来调用类
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;
}
// * Person的立即执行函数
var Person = /*#__PURE__*/ (function () {
function Person(name, age) {
_classCallCheck(this, Person);
this.name = name;
this.age = age;
}
// * 在这里将类的实例方法、静态方法分别定义到类的原型对象上和类上
_createClass(Person, [
{
key: "running",
value: function running() {
console.log(this.name + "在跑步。");
}
}
]);
return Person;
})();
// * 立即执行函数
var Student = /*#__PURE__*/ (function (_Person) {
// * 将构造函数Student的原型对象指向 一个对象(这个对象的__proto指向的是Person.prototype)
// * 方法和之前的寄生组合式继承一样
_inherits(Student, _Person);
// * 去拿到_super这个函数,因为Person不能直接当成一个函数来调用,它在这个函数中创建一个新的函数了
var _super = _createSuper(Student);
function Student(name, age, sno) {
var _this;
_classCallCheck(this, Student);
_this = _super.call(this, name, age);
_this.sno = sno;
return _this;
}
_createClass(Student, [
{
key: "studying",
value: function studying() {
console.log(this.name + "在学习");
}
}
]);
return Student;
})(Person);
网友评论