1、定义 “类”
之前都是使用new function来定义一个函数的类型,现在我们可以使用class关键字来定义一个函数类
1.1 普通函数定义的类
function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
People.prototype.play = function () {
console.log(`我是${this.name},今年${this.age}岁,我喜欢打游戏`)
}
People.prototype.sing = function () {
console.log(`${this.name}还喜欢唱个歌`)
}
var zhangSan = new People("张三", 25, "男")
zhangSan.play();
zhangSan.sing();
1.2 class定义的类
class People {
constructor(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
play() {
console.log(`我是${this.name}今年${this.age}岁,我喜欢打游戏`)
}
sing() {
console.log(`${this.name}还喜欢唱歌`)
}
}
var lisi = new People("李四", 23, "女");
lisi.play();
lisi.sing();
虽然es6增加了class关键字,但是JavaScript中还是没有类的概念!依然是基于面向对象,而不是真正的面向对象,基于原型链来模型实现class,机理和普通函数定义的类是一样的
console.log(xiaohong.singsong === People.prototype.singsong) // true
console.log(xiaohong.hasOwnProperty("singsong")) // false
console.log(xiaohong.hasOwnProperty("name")) // true
2、继承
2.1普通函数的继承
function People(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
People.prototype.play = function () {
console.log(`我是${this.name},今年${this.age}岁了,我喜欢打游戏`);
}
People.prototype.sing = function () {
console.log("我花喜欢唱歌");
}
function Student(name, age, sex, id, score, school) {
this.name = name;
this.age = age;
this.sex = sex;
this.id = id;
this.score = score;
this.school = school;
}
Student.prototype = new People();
Student.prototype.study = function () {
console.log(`我的学校是${this.school},学号${this.id},成绩${this.score}`);
}
var xiaoming = new Student("小明", 22, "男", "100001", "90", "实验中学");
xiaoming.play();
xiaoming.study();
2.2继承的原理
2.3ES6实现继承
class People {
constructor(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
play() {
console.log(`我是${this.name}今年${this.age}岁,我喜欢打游戏`)
}
sing() {
console.log(`${this.name}还喜欢唱歌`)
}
}
class Student extends People {
constructor(name, age, sex, id, score, school) {
super(name, age, sex);
this.id = id;
this.score = score;
this.school = school;
}
study() {
console.log(`我的学校是${this.school},学号${this.id},成绩${this.score}`);
}
}
var xiaoming = new Student("小明", 30, "男", 10001, 89, "实验中学")
xiaoming.play();
xiaoming.study();
3、constructor方法
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
class定义的构造函数,必须使用new关键字调用,否则会报错
class Foo {
constructor() {
return Object.create(null);
}
}
Foo()
4、class类定义的构造函数没有变量声明的提升
4.1 普通函数创建构造函数,执行
var xiaoming = new People("小明",19,"男")
console.log(xiaoming);
function People(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
4.2 class定义的构造函数
var xiaohong = new People("小红",18,"女")
console.log(xiahong);
class People{
constructor(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
}
5、继承的构造函数必须要在constructor中调用super否则会报错
class Point { /* ... */ }
class ColorPoint extends Point {
constructor() {
//内部没有调用super
}
}
let cp = new ColorPoint();
继承的子类可以不用写constructor,但是不代表没有定义,系统会自动悄悄的给你加上下面的代码
constructor(...args) {
super(...args);
}
继承的子类使用this之前必须要先调用super后再定义
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColorPoint extends Point {
constructor(x, y, color,name) {
this.name = name; // 报错
super(x, y);
this.color = color; // 正确
}
}
var aa = new ColorPoint();
console.log(aa)
6、Object.getPrototypeOf方法
Object.getPrototypeOf()方法用来查询当前的子类的父亲
class People {
constructor(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
play() {
console.log(`我是${this.name}今年${this.age}岁,我喜欢打游戏`)
}
sing() {
console.log(`${this.name}还喜欢唱歌`)
}
}
class Student extends People {
constructor(name, age, sex, id, score, school) {
super(name, age, sex);
this.id = id;
this.score = score;
this.school = school;
}
study() {
console.log(`我的学校是${this.school},学号${this.id},成绩${this.score}`);
}
}
var xiaoming = new Student("小明", 30, "男", 10001, 89, "实验中学")
xiaoming.play();
xiaoming.study();
console.log(Object.getPrototypeOf(Student) === People);
console.log(Object.getPrototypeOf(Student));
7、super关键字
super这个关键字,既可以当作函数使用,也可以当作对象使用
super 当函数使用
super
作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super
函数。
class A {
constructor() {
console.log("我是A的constructor")
}
}
class B extends A {
constructor() {
super();
}
}
var b = new B()
上面代码中,子类B
的构造函数之中的super()
,代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错。
虽然super当做函数调用的时候,调用的是父函数的constructor构造器,但是上下文还是子类的自己的上下文
new.target
class A {
constructor() {
console.log(new.target);
console.log(new.target.name) // A
}
}
new A()
class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
new A() // A
new B() // B
上面代码中,new.target
指向当前正在执行的函数。可以看到,在super()
执行时,它指向的是子类B
的构造函数,而不是父类A
的构造函数。也就是说,super()
内部的this
指向的是B
。
作为函数时,super()
只能用在子类的constructor 构造函数之中,用在其他地方就会报错。
class A {}
class B extends A {
m() {
super(); // 报错
}
}
super 当对象使用
super方法作为对象的时候,指向父类的原型对象
class A {
p() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.p()); // 2
}
}
let b = new B();
上面代码中,子类B
当中的super.p()
,就是将super
当作一个对象使用。这时,super
在普通方法之中,指向A.prototype
,所以super.p()
就相当于A.prototype.p()
。
8、函数的get和set关键字
与ES5一样,在Class内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为
class MyClass {
constructor() {
// ...
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}
let inst = new MyClass();
inst.prop = 666;
console.log(inst.prop)
9、静态方法 static
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
静态化之前的 构造函数
class Foo {
classMethod() {
console.log("静态函数");
return 'hello';
}
}
console.log(Foo.classMethod) //undefined 必须通过 new
加了static关键字后
class Foo {
static classMethod() {
return 'hello';
}
}
console.log(Foo.classMethod) // 返回classMethod函数体
console.log(Foo.classMethod() )// 'hello'
此时如果使用new产生实例后
var foo = new Foo();
foo.classMethod()
10、new.target属性
ES6为new命令引入了
function Person(name) {
if (new.target !== undefined) {
this.name = name;
} else {
throw new Error('必须使用new生成实例');
}
}
var xiaohong = new Person("小红")
console.log(xiaohong); // 小红
Person("xiaoming")//报错
一个new.target属性,(在构造函数中)返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的
11、混入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);
}
}
}
上面代码的mix
函数,可以将多个对象合成为一个类。使用的时候,只要继承这个类即可。
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);
}
}
}
class Foo1{
fun1(){
console.log("fun1")
}
}
class Foo2{
fun2(){
console.log("fun2")
}
}
class Foo3{
fun3(){
console.log("foo3")
}
}
class DistributedEdit extends mix(Foo1, Foo2,Foo3) {
}
new DistributedEdit().fun1();
new DistributedEdit().fun2();
new DistributedEdit().fun3();