重新回头学习归纳ES6的知识点---------类(class)和继承

JavaScript 语言中,生成实例对象的传统方法是通过构造函数;

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。

基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。

注意: 

  1. 类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。
  2.  ES6里面class没有提升功能,在ES5,用函数模拟可以,默认函数提升
  3. ES6里面this 比之前轻松多了它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。
  4. 类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

特点:

    es5构造函数;

    class Person{}; 

构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。

let wjp = "toString";
class Person {
  constructor() {
    // ...
    console.log("new 实例对象时自动执行")//=------构造方法(函数), 调用new,自动执行
  }
  [wjp]() {
    // ...
  }
  toValue() {
    // ...
  }
}
// 等同于
Person.prototype = {
  constructor() {},
  toString() {},
  toValue() {},
};

类的数据类型就是函数,类本身就指向构造函数。

class Point {
  // ...
}
typeof Point // "function"
Point === Point.prototype.constructor // true

const point = new Point();
point.constructor = Point.prototype.constructor

与 ES5 一样,类的所有实例共享一个原型对象。

var p1 = new Point(2,3);
var p2 = new Point(3,2);
p1.__proto__ === p2.__proto__   //true

静态方法:

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

取值函数(getter)和存值函数(setter)

与 ES5 一样,在“类”的内部可以使用getset关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

实例属性; 静态属性的新写法 

// 老写法
class Foo {
  // ...
  constructor(){
    this._count = 0
  }
}
Foo.prop = 1;
// 新写法
class Foo {
  _count = 0;
  static prop = 1;因为 ES6 明确规定,Class 内部只有静态方法,没有静态属性。现在有一个提案提供了类的静态属性,写法是在实例属性的前面,加上static关键字。
}

    

 


继承

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。

如果子类没有定义constructor方法,这个方法会被默认添加,代码如下。也就是说,不管有没有显式定义,任何一个子类都有constructor方法。

在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。

最后,父类的静态方法,也会被子类继承

super 关键字

super这个关键字,既可以当作函数使用,也可以当作对象使用。有两种情况。

    第一种情况,super作为函数调用时,代表父类的构造函数。  作为函数时,super()只能用在子类的构造函数之中( constructor )

class A {
  constructor() {
    console.log(this);
  }
}
class B extends A {
  constructor() {
    super();
  }
}
new A() // A
new B() // B

    第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

    由于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()。

    在子类普通方法中通过super调用父类的方法时,方法内部的this指向当前的子类实例

    super.print()   实际上执行   super.print.call(this)

   下面代码中,super.x赋值为3,这时等同于对this.x赋值为3。而当读取super.x的时候,读的是A.prototype.x,所以返回undefined

class A {
  constructor() {
    this.x = 1;
  }
}
class B extends A {
  constructor() {
    super();
    this.x = 2;
    super.x = 3;
    console.log(super.x); // undefined
    console.log(this.x); // 3
  }
}
let b = new B();

    如果super作为对象,用在静态方法之中,这时super将指向父类,而不是父类的原型对象。

    同样,在子类的静态方法中通过super调用父类的方法时,方法内部的this指向当前的子类,而不是子类的实例。

原型链的概念

     __proto__ 属性,它是对象所独有的,可以看到__proto__属性都是由一个对象指向一个对象,即指向它们的原型对象(也可以理解为父对象),那么这个属性的作用是什么呢?它的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(可以理解为父对象)里找,如果父对象也不存在这个属性,则继续往父对象的__proto__属性所指向的那个对象(可以理解为爷爷对象)里找,如果还没找到,则继续往上找…直到原型链顶端null(可以理解为原始人。。。),再往上找就相当于在null上取值,会报错(null为原型链的终点),由以上这种通过__proto__属性来连接对象直到null的一条链即为我们所谓的原型链。
  其实我们平时调用的字符串方法、数组方法、对象方法、函数方法等都是靠__proto__继承而来的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值