在前一篇的文章,我们学习了ES5通过原型 原型链实现继承的方式,那么ES6中是如何实现继承,其背后的原理又是怎样的?
建议在看这篇文章之前,要先阅读我的前一篇文章,会让你有醍醐灌顶的想法
重学JavaScript高级(六):以面向对象原型继承(ES5)搞懂原型原型链
Function
进行定义,这种方法和 普通函数过于相似class
提升到了关键字new Person
后,其内部的操作,与ES5的操作一致(具体操作可以看先前的文章)//拥有高内聚,低耦合的特点
class Person{
constructor(name,age){
//用于接收参数,默认调用
this.name= name
this.age = age
}
//这是实例方法,实际上是将方法添加到了Person.prototype中,是一种语法糖
running(){
console.log(this.name+"running")
}
}
let p1 = new Person("zhangcheng",18)
console.log(p1.__proto__ === Person.prototype)
//拥有高内聚,低耦合的特点
class Person1{
constructor(name,age){
//用于接收参数,默认调用
this.name= name
this.age = age
}
//这是实例方法,实际上是将方法添加到了Person.prototype中,是一种语法糖
running(){
console.log(this.name+"running")
}
}
//构造函数Person
function Person2(name) {
this.name = name;
}
//只能通过p1调用
Person.prototype.study = function () {
console.log("123");
};
let p1 = new Person1("zhangcheng",18)
let p2 = new Person2("lisi")
console.log(Person1.prototype === p1.__proto__)//true
console.log(Person2.prototype === p2.__proto__)//true
console.log(Person1.prototype.constructot) //Person1
console.log(Person2.prototype.constructot) //Person2
监听对某个属性的读取以及写入
//通过存储属性描述符进行编写
let obj = {
name: "zhangcheng",
};
let _name = "";
Object.defineProperty(obj, "name", {
set: function (value) {
_name = value;
console.log("set方法被调用了");
},
get: function () {
console.log("get方法被调用了");
return _name;
},
});
obj.name = "lisi";
console.log(obj.name);
class Person1 {
constructor(name, age) {
//用于接收参数,默认调用
this.newName = name;
this.age = age;
}
//这是实例方法,实际上是将方法添加到了Person.prototype中,是一种语法糖
running() {
console.log(this.name + "running");
}
//在定义set的时候,一定要传入参数,否则会报错
set newName(value) {
this.name = value;
}
get newName() {
return this.name;
}
}
let p1 = new Person1("zhangcheng", 123);
console.log(p1.newName);
class Shape {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
// position属性返回x值和y值
get position() {
return { x: this.x, y: this.y };
}
//size属性返回widht值和height值
get size() {
return { width: this.width, height: this.height };
}
}
let s1 = new Shape(10, 20, 100, 200);
console.log(s1.position); //{ x: 10, y: 20 }
console.log(s1.size);//{ width: 100, height: 200 }
通过类直接调用Person().running
static
即可class Person {
constructor(name) {
this.name = name;
}
static personRunning() {
//这里的this指向的就是Person类本身
console.log("personRunning");
}
}
//类直接进行调用
Person.personRunning();
class Person {
constructor(pName, age) {
this.pName = pName;
this.age = age;
}
running() {
console.log("running");
}
}
class Teacher extends Person {
constructor(pName, age, title) {
//super一定要用在子类this前面
super(pName, age);
this.title = title;
}
teach() {
console.log("teach");
}
}
class Student extends Person {
constructor(pName, age, sno) {
//super一定要用在子类this前面
super(pName, age);
this.sno = sno;
}
study() {
console.log("study");
}
}
let tea1 = new Teacher("wanglaoshi", 58, "教授");
let stu1 = new Student("zhangcheng", 18, 200);
console.log(tea1);
console.log(stu1);
tea1.running();
stu1.running();
class Person {
constructor(pName, age) {
this.pName = pName;
this.age = age;
}
running() {
console.log("running");
}
static walk() {
console.log("walk");
}
}
class Student extends Person {
constructor(pName, age, sno) {
//super一定要用在子类this前面
super(pName, age);
this.sno = sno;
}
//实例方法
study() {
console.log("study");
//只能用super调用父类的实例方法
super.running();
}
static play() {
console.log("play");
//只能用super调用父类的静态方法
super.walk();
}
}
let stu1 = new Student("zhangcheng", 18, 200);
stu1.study();
Student.play();
重写的理解
当我们对父类的方法,不满意的时候,在子类中可以重新编写一下这个方法,就叫重写
class Person {
constructor(pName, age) {
this.pName = pName;
this.age = age;
}
running() {
console.log("running");
}
static walk() {
console.log("walk");
}
}
class Student extends Person {
constructor(pName, age, sno) {
//super一定要用在子类this前面
super(pName, age);
this.sno = sno;
}
//实例方法重写
running() {
console.log("Student running");
}
//静态方法重写
static walk() {
console.log("Student walk");
}
}
let stu1 = new Student("zhangcheng", 18, 200);
stu1.running();
Student.walk();
比如继承js提供的内置类,对一些方法进行扩展
class ZcArray extends Array {
//打印数组的最后一位
lastItem() {
//这里的this指向的就是调用方法的实例,隐式绑定
//不理解的可以去看我的第一篇文章,this指向问题
console.log(this[this.length - 1]);
}
}
let arr = new ZcArray(10, 20, 30);
arr.lastItem();
JS的继承只能单继承,意思就是一个子类只能有一个父类
注意:主要是掌握这种思想,在后期React中,高阶组件会有类似的写法
Bird、Flyer、Animals
,需要实现Bird
要继承Flyer、Animals
class Animals {
running() {
console.log("running");
}
}
class Flyer {
fly() {
console.log("flying");
}
}
class Bird {
getName() {
console.log("my name is bird");
}
}
function mixinAnimals(BaseClass) {
return class extends BaseClass {
running() {
console.log("running");
}
};
}
function mixinFlyer(BaseClass) {
return class extends BaseClass {
fly() {
console.log("flying");
}
};
}
class Bird {
getName() {
console.log("my name is bird");
}
}
class NewBird extends mixinAnimals(mixinFlyer(Bird)) {}
let bird = new NewBird();
bird.running();
bird.fly();
bird.getName();
继承是多态的前提
/*
1.定义一个父类Shape
2.定义两个子类均继承父类
3.写一个函数,里面均让传入的参数调用getArea方法
4.实例出两个子类的对象,并传入函数中
5.这种表现形式可以称为多态
*/
class Shape {
getArea() {}
}
class Rectangle extends Shape {
constructor(width, height) {
super();
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Circle extends Shape {
constructor(r) {
super();
this.r = r;
}
getArea() {
return this.r * this.r * 3.14;
}
}
/**
严格意义的面向对象中
1.必须有继承
2.必须有父类引用指向子类对象
*/
function getShapeArea(shape) {
//在严格的面向对象编程语言中,形参是有类型控制的,类似于shape:Shape,传入的参数必须是Shape类型的,在此就是父类引用
console.log(shape.getArea());
}
let rect1 = new Rectangle(10, 10);
let circle = new Circle(10);
getShapeArea(rect1);
getShapeArea(circle);
从维基百科的定义出发,那么JS中到处都是多态
function sum(a,b){
return a+b
}
sum(200,300)
sum("hello",123)
//同一个标识符foo
let foo = 123
foo = "zhangcheng"
foo = {
a:100
}