JavaScript ES6 继承 class
JavaScript ES5原型链补充
通过我们前面的学习,实际上上面的这个关于原型链的图是十分好理解的
但是这里我们还需要补充说明一个知识点:
数据类型 对应的包装类类型或者对应的构造函数(类) number Number string String array Array function Function object Object 这个就是我们的数据类型以及其对应的包装类型了
实际上的话,我们进行的每一个对数据类型操作都是使用的是其构造函数中对应的原型链上具有的方法
通过这些原型链我们就可以实现获得处理数据的方法了
对于我们的数据类型实例来说(函数除外)都是含有一个隐式原型的,其指向是为其构造函数的显式原型
函数的话,既具有显示原型,又具有隐式原型,所以说函数的原型链的话是最复杂的
对象的话,是每一种数据类型的父类,所以说,原型链的终点站就是在 Object 构造函数上进行分析的
javascript/** * 对于数值型的探究 * @type {number} */ const num = 10 // 等价于: const num = new Number(10) console.log(Object.getPrototypeOf(num) === Number.prototype) // true console.log(Object.getPrototypeOf(Number.prototype) === Object.prototype) // true /** * 对于字符型的探究 * @type {string} */ const str = "76433" // 等价于: const str = new String("76433") console.log(Object.getPrototypeOf(str) === String.prototype) // true console.log(Object.getPrototypeOf(String.prototype) === Object.prototype) // true /** * 对于数组类型的探究 * @type {number[]} */ let arr = [1, 2, 3, 55] // 等价于: let arr = new Array(1, 2, 3, 55) console.log(Object.getPrototypeOf(arr) === Array.prototype) // true console.log(Object.getPrototypeOf(Array.prototype) === Object.prototype) // true /** * 对于对象类型的探究 * @type {{name: string, age: number, info: string}} */ const obj = { name: "76433", age: 18, info: "我是一位在读本科大学生" } console.log(Object.getPrototypeOf(obj) === Object.prototype) // true console.log(Object.getPrototypeOf(Object.prototype) === null) // true /** * 注意上面的数据类型都是我们的实例对象上面只有隐式原型的实例 * 不具备显式原型 * * 但是函数就不同了,一个函数既是一个实例对象,又是一个类 * 所以说就含有了隐式原型和显式原型(函数本身也是函数的实例对象的情况) */
有点乱,将就看看吧 😢 😢 😢
JavaScrpt 构造函数的类方法和实例方法
JavaScript 实例方法
需要通过实例对象进行调用的方法,就是我们的实例方法,在类的 prototype 属性上的方法
前面我们可以知道一点的的是我们的一个函数具有显式原型和隐式原型的
但是都是终究是我们的属性的,所以说在原型上面定义的方法是不可以被类直接被调用的呐
那么这个时候有十分好奇了,为什么实例对象可以调用 prototype 上的方法呐???
- 直接回顾原型链 😄 😄 😄
javascript
const obj = {
name: "76433",
method: {
running: function () {
console.log(this);
}
}
}
// 这个时候,我们就不可以通过 obj 来直接调用我们的 running 方法了
// obj.running()
// obj.method.running() // 正确使用,但是这样调用,我们的this 绑定就是 window 了
obj.method.running.call(obj) // 正确使用
/**
* 那么这个时候一样的,我们在定义一个类中的方法的时候
* 我们使用的是在 prototype 中实现定义的呐
* 所以说,这个时候只有通过实例对象进行调用,所以说也叫做的是我们的实例方法
*/
javascript
/**
* 构造函数
* @param name
* @param age
* @constructor
*/
function Foo(name, age) {
this.__name = name
this.__age = age
}
// 定义实例方法
Foo.prototype.running = function() {
console.log(this.__name + " is running")
}
// Foo.running() 直接报错
new Foo("76433", 18).running() // 通过实例对象来调用实例方法
JavaScript 类方法
类方法: 就是直接添加到类身上的方法,就是我们的类方法,类方法只可以通过类本身来调用
实例方法和类方法在很多的面向对象的编程语言中都是有涉及的呐!!!
javascript
/**
* 构造函数
* @param name
* @param age
* @constructor
*/
function Foo(name, age) {
this.__name = name
this.__age = age
}
Foo.running = function () {
console.log("running")
}
Foo.running()
JavaScript ES6 class 定义类
通过前面讲解的 ES5 定义的类,我们可以发现的是, ES5 中定义类的方式实际上的话和定义函数的方式是及其相似的
这样的定义规则尊嘟不便于开发者进行区分,以及代码并不容易被理解
这个时候 ES6 就给我们提供了新的方法来实现定义类,就是使用 class 关键字
ES6 是我们的 ECMAScript 的新标准,在 ECMAScript2015 后面以及自己的 ECMAScript 的规范统称为 ES6
然后我们书写 ES6 的类,他就给我们提供了一个十分好用的语法糖 class
- 但是他的底层的原理的话,还是使用的是我们的构造函数和原型链的底层实现的
后续通过 babel 工具,我们是可以实现将我们的 ES6 的代码实现自动的转化为 ES5 的版本的代码格式的
定义的基本的格式为:
class class_name {}
同时我们实现通过使用 new 操作符的时候,就会默认的调用我们类中的 constructor 的构造函数的
javascript
class Person {
// 定义构造函数
constructor(name, age, phone, email) {
this.__name = name
this.__age = age
this.__phone = phone
this.__email = email
}
// 开始书写我们的实例方法
running() {
console.log(this.__name + " is running")
}
}
const per = new Person("76433", 18, 123321, 313)
console.log(Object.getPrototypeOf(per) === Person.prototype)
console.log(Person.prototype.running)
per.running()
// ====================================================================
// 开始 es6 之前的书写方法
function Per(name, age, phone, email) {
this.__name = name
this.__age = age
this.__phone = phone
this.__email = email
}
// 开始添加实例方法
Per.prototype.running = function () {
console.log(Per.__name + " is running")
}
JavaScript 类访问器(prototype accessor)
类访问器: 就是实现的是自动化的实现运行对一些属性的监听,自动化的监听一个属性的改变
这个在 VUE3 的源码中出现的十分多的,常见的就是我们的 getter 方法和 setter方法
getter 方法: 就是实现的是我们的获取属性值的方法
setter 方法: 就是实现的是给我们的值进行赋值的操作
javascript
// 通过描述符来实现书写我们的访问器
const obj01 = {}
Object.defineProperty(obj01, "name", {
configurable: true,
writable: true,
enumerable: true,
// 开始书写访问器
set: function (value) {},
get: function () {}
})
// 在对象中直接书写访问器
const obj02 = {
name: "76433",
// 访问器的书写是用来实现的是对一些变化的属性进行监听
// name 随意的,只是一个取名罢了
// setter 方法
set name(value) {
console.log(value)
},
// getter 方法
get name() {
return this.name
}
}
// 类中的访问器方法的编写
class Obj{
constructor(name, age){
this._name = name;
this._age = age;
}
// 开始书写访问器方法
set name(value) {
this._name = value;
}
get name() {
return this._name;
}
}
JavaScript 类的静态方法
类的静态方法 :就是直接可以通过类名来实现调用的方法,一般使用的是 static 关键字来实现定义,就是以前的类方法
javascript
class Obj{
constructor(name){
this._name = name
}
// 开始定义静态方法
static running = function() {
console.log("Running...");
}
static eating = function () {
console.log("Eating...");
}
static randomPeron = function () {
return new this("76433")
}
}
Obj.running()
Obj.eating()
// es6 之前的定义方式
function Obj01() {}
Obj01.running = function () {
console.log("Running...");
}
Obj01.eating = function () {
console.log("Eating...");
}
Obj01.running()
Obj01.eating()
JavaScript extends 关键字实现继承
javascript
class Obj{
constructor(name){
this._name = name
}
// 开始定义静态方法
static running () {
console.log("Running...");
}
static eating () {
console.log("Eating...");
}
static randomPeron () {
return new this("76433")
}
}
// 开始实现继承
/**
*
*/
class Person extends Obj {
constructor(name) {
super(name)
}
studying () {
console.log("Studying...");
}
}
Person.eating()
Person.running()
Person.eating()
new Person("76433").studying()
super() 表示的就是直接使用父类中的构造函数,直接通过父类来进行初始化即可
但是 super() 的调用尽量在使用 this 之前进行调用
在类的构造函数中使用 this 的时候或者说返回默认对象前,必须先调用父类的构造函数
super() 的使用的场景含有三种
- 子类的构造函数
- 实例方法
- 静态方法
同时我们还可以对父类中的方法今进行重写的
javascriptclass Obj{ constructor(name){ this._name = name } // 开始定义静态方法 running () { console.log("Running..."); } eating () { console.log("Eating..."); } static randomPeron () { return new this("76433") } } // 开始实现继承 /** * */ class Person extends Obj { constructor(name) { super(name) } studying () { console.log("Studying...") } running() { super.running() } eating() { super.eating() } } new Person("76433").studying()
JavaScript 继承内置类
javascript
/**
* 为什么这里我们使用 this
* new 操作符做的事情回顾:
* 1.创建一个空对象,就是自己的实例对象本身
* 2.this指向为 这个空对象
* 3.同时把空对象赋值给类的显式原型
* 4.构造函数自动化运行
*/
class MyArray extends Array {
// 开始为我们的内置类进行扩展
get ArrayLastItem() {
console.log(this)
return this[this.length - 1]
}
get ArrayFirstItem() {
console.log(this)
return this[0]
}
}
class MyObject extends Object {}
new MyArray().ArrayFirstItem
console.log(new MyArray())
JavaScript 实现多继承
我们的JavaScript 只是可以支持继承一个类的,但是只不过我们还是可以通过一些方法来实现继承多个类的
就是使用我们的混合继承
javascript
// 我们的 Javascript只是可以实现单继承,但是只不过还是有一些其他的方式的
function mixinExtendsFirstObject(BaseClass) {
return class extends BaseClass {
running() {
console.log("running")
}
}
}
function mixinExtendsSecondObject(BaseClass) {
return class extends BaseClass {
eating() {
console.log("eating");
}
}
}
class EvalClass {
constructor() {}
}
// 第一种实现多继承的方式
const FatherClass = mixinExtendsFirstObject(mixinExtendsSecondObject(EvalClass))
const Eval_class = new FatherClass()
console.log(Eval_class)
Eval_class.running()
Eval_class.eating()
// 或者说直接实现继承也行
class NewClass extends mixinExtendsFirstObject(mixinExtendsSecondObject(EvalClass)) {}
new NewClass().running()
实际上的话,分析底层源码的话,还是使用了我们的前面说过的原型链的查找规则来实现的
只要我们的原型链上含有这个方法,那么他最后实现的就是我们的可以通过调用多个类中的方法