一、语法
1、仅仅是语法上更像一些高级面向对象的语言(如Java) 本质上还是function对象 无法直接调用 需要创建实例
2、类的内部所有定义的方法,都是不可枚举的 //使用function是可枚举的
3、类的属性名可以采用表达式
4、constructor:
- 类的默认方法
- 如果没有显式定义,一个空的
constructor
方法会被默认添加。 - constructor方法默认返回实例对象(即
this
),完全可以指定返回另外一个对象。
5、类的实例对象:
- 不能像函数对象一样直接调用
- 实例的属性除非显式定义在其本身(即定义在
this
对象上),否则都是定义在原型上(即定义在class
上) - 所有实例共享一个原型对象
6、prototype和__proto__
- 每个构造器方法都有一个prototype属性,该属性是在定义构造器方法时自动创建的
- prototype属性代表该函数创建的类的默认属性值,任何该类的新实例都可以使用这些方法
- 类的每一个实例都有一个__proto__属性,用于引用创建它的构造器方法prototype属性,__proto__最终还是通过prototype才能实现其功能
- prototype.__proto__表示方法的继承,总是指向父类的
prototype
属性。
7、Class表达式:
const MyClass = class Me { getClassName() { return Me.name; } };
- Me可以省略
- Me只能在类的内部使用 代指该类
- 类名是MyClass
- 立即执行类:const now=new class{....}([args...]); //now就是新实例
8、不存在变量提升
9、ES6实际上把整个语言升级到了严格模式,不需使用"use strict"指定运行模式
10、下面依次是用function关键字、class关键字创建的一个相同的类 //后者无法直接调用构造方法,必须借助new关键字
二、Class的继承
1、语法
class Child extends Parent {}
2、super关键字表示父类的构造函数,用来新建父类的this
对象
3、子类必须在constructor
方法中调用super
方法,这是因为子类没有自己的this
对象,而是继承父类的this
对象,如果不调用super
方法,子类就得不到this
对象
3、注意:
- Class类不能使用prototype属性添加其他属性 但可以使用prototype.__proto__添加其他属性 //prototype被内部占用 用来存放所有的属性和方法(除了对象本身的方法外)
- Class的子类既不能使用prototype属性添加其他属性 也不能使用prototype.__proto__属性添加其他属性 //prototype.__proto__被内部占用,指向父类的prototype属性
4、Extends 的继承目标
- 只要是一个有
prototype
属性的函数,就能被B
继承。由于函数都有prototype
属性,因此A
可以是任意函数。 - 子类继承Object类:class A extends Object {} //A其实就是构造函数
Object
的复制,A
的实例就是Object
的实例 - 不存在任何继承:class A {} //A作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承
Funciton.prototype但是,
A
调用后返回一个空对象(即Object
实例),所以A.prototype.__proto__
指向构造函数(Object
)的prototype
属性 - 子类继承
null
:class A extends null {} //A也是一个普通函数,所以直接继承Funciton.prototype
。但是,A调用后返回的对象不继承任何方法,所以它的__proto__
指向Function.prototype
,即实质上执行了下面的代码。
class A extends null { constructor() { return Object.create(null); } }
5、Object.getPrototypeOf(child) ; //返回父类
6、实例的__proto__属性
- 子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性。也就是说,子类的原型的原型,是父类的原型
- 通过子类实例的
__proto__.__proto__
属性,可以修改父类实例的行为
三、原生构造函数的继承
1、ECMAScript的原生构造函数大致有下面这些:
- Boolean()
- Number()
- String()
- Array()
- Date()
- Function()
- RegExp()
- Error()
- Object()
2、ES5和ES6继承的区别
- ES5是先新建子类的实例对象
this
,再将父类的属性添加到子类上,由于父类的内部属性无法获取,导致无法继承原生的构造函数 - ES6是先新建父类的实例对象
this
,然后再用子类的构造函数修饰this
,使得父类的所有行为都可以继承 - ES6改变了
Object
构造函数的行为,一旦发现Object
方法不是通过new Object()
这种形式调用,ES6规定Object
构造函数会忽略参数。
3、Class的取值函数(getter)和存值函数(setter)
4、Class的Generator方法
5、Class的静态方法
- 类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上
static
关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法” - 父类的静态方法,可以被子类继承
- 静态方法也是可以从
super
对象上调用的
6、Class的静态属性和实例属性
class Foo {} Foo.prop = 1; //静态属性 Foo.prop // 1
- 静态属性指的是Class本身的属性,即
Class.propname
,而不是定义在实例对象(this
)上的属性。 - ES6明确规定,Class内部只有静态方法,没有静态属性。
7、new.target属性
- new是从构造函数生成实例的命令。ES6为
new
命令引入了一个new.target
属性,(在构造函数中)返回new
命令作用于的那个构造函数。如果构造函数不是通过new
命令调用的,new.target
会返回undefined
- Class内部调用
new.target
,返回当前Class。 - 子类继承父类时,
new.target
会返回子类,利用这个特点,可以写出不能独立使用、必须继承后才能使用的类。 - 在函数外部,使用
new.target
会报错。
8、Mixin模式的实现
- Mixin模式指的是,将多个类的接口“混入”(mix in)另一个类。它在ES6的实现如下。
function mix(...mixins) { class Mix {} for (let mixin of mixins) { copyProperties(Mix, mixin); //对象本身的属性和方法 copyProperties(Mix.prototype, mixin.prototype); //原型链上的对象和方法 } return Mix; } function copyProperties(target, source) { for (let key of Reflect.ownKeys(source)) { if ( key !== "constructor" && key !== "prototype" && key !== "name" ) { let desc = Object.getOwnPropertyDescriptor(source, key); Object.defineProperty(target, key, desc); } } }