解决的问题
更加清晰的书写继承,面向对象编程
基本语法
constructor
- 构造方法,this关键字代表构造对象
- new一个类时,自动执行该方法
- 必须有constructor,没有的话一个空的方法会被默认添加
- constructor默认返回该对象实例(this),也可以手动更改
函数
不用加function以及逗号,以下为es5和es6对比
function Point(x, y) {
this.x = x;
this.y = y;}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';};
var p = new Point(1, 2);
//定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}}
使用
class Bar {
do Stuff(){
console.log('stuff');
}
}
var b = new Bar();
b.doStuff() // "stuff"
通过new
原理
可以把类,看作构造函数的另一种写法
class Pointer{
}
typeof Pointer //function
Pointer == Pointer.prototype.constructor //true
以上表示类的数据类型是函数,并且类本身指向其构造函数
类的实例对象
- 必须使用new操作符实例化对象
- 由于类的方法都是通过prototype实现的,因此可以像这样添加函数
var p1 = new Point(2,3);
var p2 = new Point(3,2);
p1.__proto__.printName = function () {
return 'Oops';
};
p1.printName() // "Oops"
p2.printName() // "Oops"
var p3 = new Point(4,2);
p3.printName() // "Oops"
其他知识点
- 不存在变量提升
- 可以使用class表达式
- 类和模块的内部,默认都是严格模式
- 类的name属性总是返回class后面的类名
2.继承
- 由于子类没有自己的
this
对象,因此子类的contstuctor内必须调用super方法,否则报错 - 如果子类没有定义constructor,会自动添加
- Class同时拥有
prototype
和__proto__
属性 - 子类的
__proto__
属性,表示构造函数的继承,总是指向父类 - 子类prototype属性的
__proto__
属性,表示方法的继承,总是指向父类的prototype属性。 - 只要是有prototype属性的函数都能被继承,除了Function.prototype
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); // 调用父类的constructor(x, y)
this.color = color;
}
toString() {
return this.color + ' ' + super.toString();
// 调用父类的toString()
}}
ES5的继承,实际是想创造子类的实例对象this,然后将父类的方法绑定子类的this(Parent.apply(this)
);ES6则不同,实质是先创造父类的实例对象(所以要先调用super()
),然后子类的构造函数再修改this。
继承目标
3种特殊情况
第一种,子类继承Object
class A extend Object{
}
A.__proto__ = Object;
A.prototype.__proto__ = Object.prototype;
这种情况A相当于Object的复制,A的实例就是Object的实例。
第二种 不存在任何继承
Class A{}
A.__proto__ = Function.prototype;
A.prototype.__proto__ = Object.prototype;
A作为一个基本对象,那么直接继承自Function,但由于new一个A后返回的是一个空对象,因此A的对象继承自Object.prototype.
第三种 子类继承null
class A extend null{}
A.__proto__ = Function.prototype;
A.prototype.__proto = undefined;
因为new一个A后的实例,不继承任何方法,因此为undefined。
super
- super作为函数调用时,super指父类构造函数
- super作为对象调用时,super代表父类,可以调用父类实例的方法属性以及静态方法
子类的__proto__
- 子类实例的
__proto__
不等于父类实例的__proto__
- 子类实例
__proto__
的__proto__
是父类实例的__proto__
- 也就是说子类原型的原型是父类的原型。
- 因此子类实例可以通过该方法修改父类实例方法。
继承原生构造函数
可以通过继承原生构造函数,如Array,从而获得Array所有属性和方法,并且可以扩充而不会影响Array的实例。
Class的get和set
class CustomHTMLElement {
constructor(element) {
this.element = element;
}
get html() {
return this.element.innerHTML;
}
set html(value) {
this.element.innerHTML = value;
}
}
var descriptor = Object.getOwnPropertyDescriptor(
CustomHTMLElement.prototype, "html");
"get" in descriptor // true
"set" in descriptor // true
- 当对html属性进行读取和设置值时,get和set修饰符会进行拦截。
- get和set函数是设置在属性的descriptor上的。
Class的静态方法
- 可以直接通过Class调用静态方法,并且不能通过实例调用
- 父类静态方法可以被子类继承,子类可以通过自身调用父类静态方法,或者通过super调用
Class的静态属性和实例属性
静态属性是指定义在class本事的属性,而不是对象this
属性。
class Foo {
}
Foo.prop = 1;
Foo.prop // 1
因为ES6规定,class内部只有静态方法,没有静态属性,所以只能这么写。
以下为ES7提案,被babel支持。
实例属性
class MyClass {
myProp = 42;
constructor() {
console.log(this.myProp); // 42
}
}
不用定义在constructor
里。
静态属性
class MyClass {
static myStaticProp = 42;
constructor() {
console.log(MyClass.myProp); // 42
}
}