JS类与类继承
2023-09-25 23:36:56
#JS
class 的基本语法
关于 class
- ES6 引用了关键字
class定义“类”,但是底层仍然是基于原型实现继承的方式,class 只是语法糖
1 | // ES6 class 定义类并生成新对象 |
1 | // ES5 构造函数定义并生成新对象 |
- 类的数据类型就是函数,类本身就指向构造函数
1 | typeof Point // function |
- 类的所有方法都定义在类的
prototype属性上
1 | class Point { |
- 在类的实例上调用方法,其实就是调用原型上的方法
1 | class A = {} |
- 类的新方法可以添加在 prototype 对象上,
Object.assign方法可以一次向类添加多个方法
1 | class Point { |
- 类的内部定义的所有方法都是不可枚举的
1 | class Point { |
- 类的属性名可以采取表达式
1 | let methodName = 'getArea'; |
constructor 方法
constructor方法是类的默认方法,通过 new 命令生成对象实例时自动调用该方法- 一个类必须有 constructor 方法,如果没有显式定义,一个空的 constructor 方法会被默认添加
1 | class Point { |
- constructor 方法默认返回实例对象(即 this),也可以指定返回另外一个对象
1 | class Point { |
- 类必须使用
new来调用,否则会报错
1 | class Point { |
类的实例对象
- 实例的属性除非显式定义在其本身(即 this 对象)上,否则都是定义在原型(即 class)上
1 | point.hasOwnProperty('x'); // true |
- 类的所有实例共享一个原型对象
1 | const p1 = new Point(1, 2); |
class 表达式
- class 可以使用表达式的形式定义
1 | // 类的名字是 Myclass 而不是 Me, Me 只在 class 的内部代码有用 |
不存在变量提升
- 类不存在变量提升,必须保证子类在父类之后定义
1 | new Point(); // ReferenceError |
this 的指向
- 类的方法内部的 this 默认指向类的实例,如果将该方法提取出来单独使用,this 会指向该方法运行时所在的环境
1 | class Logger { |
- 对于上述问题解决方案1:在构造函数中绑定 this
1 | class Logger { |
- 对于上述问题解决方案2:使用箭头函数
1 | class Logger { |
class 的继承
关于 class 继承
- class 通过
extends关键字实现继承 - 子类没有自己的 this 对象,而是继承父类的 this 对象
- ES6 的继承机制实质是,先创造父类的实例对象 this,然后再用子类的构造函数修改 this
- 在子类的构造函数中,只有调用 super 之后才可以使用 this 对象,否则会报错,因为子类实例的构建是基于对父类实例加工,只有 super 方法才能返回父类实例
1 | class Point { |
getPrototypeOf() 方法
Object.getPrototypeOf()方法可以用来从子类上获取父类,因此可以使用这个方法判断一个类是否继承了另一个类
1 | Object.getPrototypeOf(ColorPoint) === Point // true |
super 关键字
super关键字既可以当作函数使用,也可以当作对象使用,使用 super 的时候,必须显式指定是作为函数还是作为对象使用,否则会报错
1 | class A {} |
- 由于对象总是继承其他对象的,所以可以在任意一个对象中使用 super 关键字
1 | let obj = { |
super 作为函数
- ES6 要求,子类的构造函数必须执行一次 super 函数,否则会报错
1 | class A {} |
- super 作为函数时,super() 只能用在子类的构造函数中,用在其他地方会报错
1 | class A {} |
- super 虽然代表了父类 A 的构造函数,但是返回的是子类 B 的实例,即 super 内部的 this 指向的是 B
1 | class A { |
super 作为对象
- 在普通方法中指向父类的原型对象
1 | class A { |
- 由于 super 指向父类的原型对象,因此,定义在父类实例上的属性不能通过 super 获取
1 | class A { |
- 定义在父类的原型对象上的属性,可以通过 super 获取
1 | class A {} |
- 通过 super 调用父类的方法时,super 会绑定子类的 this
1 | class A { |
- 如果通过 super 对某个属性赋值,这时 super 就是 this,赋值的属性会变成子类实例的属性
1 | class A { |
- 在静态方法中指向父类
1 | class A { |
类的 prototype 属性和 __proto__ 属性
- 每个对象都有 __proto__ 属性,指向对应的构造函数的 prototype 属性
- class 作为构造函数的语法糖,同时有 prototype 属性和 __proto__ 属性,因此同时存在两条继承链
- 子类的 __proto__ 属性表示构造函数的继承,总是指向父类
- 子类 prototype 属性的 __proto__ 属性表示方法的继承,总是指向父类的 prototype 属性
1 | class A {} |
实例的 __proto__ 属性
- 实例的 __proto__ 属性指向其构造函数的 prototype 属性
- 子类实例的 __proto__ 属性的 __proto__ 属性指向父类实例的 __proto__ 属性,即子类的原型的原型就是父类的原型
1 | class A {} |
注意!
- Javascript 中一切皆对象,函数也属于对象
- 所有对象都含有
__proto__属性 - 只有函数才有 prototype 属性,即函数既有
prototype属性也有__proto__属性 (原型对象只是对象,不是函数,即原型对象无原型) - 所有函数的默认原型都是 Object 的实例
- 函数都是由 Function 函数生成的(Function 和 Object 是函数也是对象,都是由 Function 函数生成的)
- 只要是对象,就一定有相应的构造函数(除了 Object 的原型对象)