前言 es5其实严格来说并没有类的写法,也没有继承的写法,如果想实现类还需要手写各种模式的构造函数,已经十分过时了。
在es6后续语法中将其统一,提供了官方的类的声明。
class来声明类 看代码:
class Parent {
constructor(name, age) {
this.name = name; // 意思是给类添加一个name属性,值为实例化后传进来的name
this.age = age;
}
showName() { // 写方法
console.log(this.name);
}
// 以下是直接赋值法(这个初学者很容易忽略)
cando = '吃饭' // 直接实例化对象添加属性
candoFn = function () {
console.log('candoFn');
} // 直接给实例化对象添加一个方法属性
// 静态方法
static nocandoFn() { // 静态方法,给类本身添加一个方法,实例化对象拿不到
console.log('nocandoFn')
}
}
let p1 = new Parent('xiaoqiang', 123); // 实例化一个类,获取一个新对象
p1.showName() // xiaoqiang
console.log(p1.cando); // 吃饭
p1.candoFn() // candoFn
// p1.nocandoFn() // 报错
console.log(Parent.cando); // undefined
// Parent.candoFn() // 报错
Parent.nocandoFn() // nocandoFn
咱们把p1打印出来看看
结合图片总结下
class能够直接声明一个类
constructor是指定的构造函数,当实例化以后被自动执行,里面写属性,实例化后的属性都给到对象本身
类中定义的方法最终是挂在对象的原型上的,实例化对象调用时,也是通过隐式引用到原型上执行
如果是直接赋值法定义的属性和方法,就被定义到实例化对象本身上了。且类是无法拿到这些东西的,必须实例化
静态方法只有类能直接拿到
extends赖继承 继承上面的父类:
class Parent {
constructor(name, age) {
this.name = name;
this.age = age;
this.weight = '120kg'
}
showName() {
console.log(this.name);
}
showWeight() {
console.log(this.weight);
}
}
class Child extends Parent{
}
let c1 = new Child('a')
c1.showName() // a
class Child extends Parent {
constructor(name) {
super(name) //super 必须先调这个,且当想在实例化的时候用到父类中某些定义的属性,需要手动写入参
}
}
let c1 = new Child('b')
c1.showName() // a
c1.showWeight() // 120kg
class Child extends Parent{
constructor(name, age, gender) {
super(name, age);
this.gender = gender // 子类自己的方法
}
// 可以添加子类新方法...
showGender(){
console.log('gender', this.gender)
}
}
let c1 = new Child('a', 13, 'male')
c1.showName() // a
打印出子类实例:
总结下:
extends声明继承关系,自动继承父类方法与所有constructor里的属性
所以如果子类不需要新增属性,那么constructor可以省略不写
如果子类需要新增属性,写constructor的同时,在里面必须第一个调用super()去继承父类的this,因为子类自己是没有this的,并且还可以通过入参指定继承父类哪些属性;
父类的方法自动继承到子类实例的原型的原型上(如果子类改写了父类的方法,也只是添加到子类实例的原型上,原型的原型上父类的方法还在)
继承的好处:
新建类时,可以继承有相同内容的类的基础上添加内容; 父类内容有扩展、变动时,继承的子类自动跟随; this的指向
class Person {
constructor(name) {
this.name = name
}
fn = function() { console.log(this) }
fn1(){
console.log(this, this.name)
}
fn2 = () => { console.log(this) } // 2 箭头函数,指向实例
fn3 = () => { console.log(this) }
static fn4() { console.log(this) } // 4 静态属性,指向class本身
}
const man = new Person('小夏')
man.fn() // 打印实例
man.fn1() // 打印实例 ‘小夏’
man.fn2() // 打印实例
man.fn3.call(this) // 箭头函数不能改变指向,打印实例
Person.fn4() // class本身
总结:
类定义的普通函数,指向实例化的对象 直接赋值法的箭头函数,普通函数,都指向实例化的对象 静态方法,指向类本身 有一个知识可以看看,如果是man.proto.fn1()去调用,this会指向什么?
this只会是指向原型自己,因为是__proto__自己去调用了,所以this.name为undefined。
那有人会说man调用fn1()也是通过原型去调的,为什么this指向就没问题,是因为JS底层帮你把this指向处理好了。
关于typeof的判断
typeof 类 === 'function' // true
说明class其实是由函数包装而来的一种语法糖;