美文网首页工作生活
ES6 class与继承

ES6 class与继承

作者: 椰果粒 | 来源:发表于2019-07-01 17:40 被阅读0次

class是什么

  • class是定义类的方法。ES6之前用构造函数的方式定义类,ES6引入了class。
  • class是语法糖。
  • class内部默认是严格模式。

使用

传统方式定义一个类

function Point1(x,y){
  this.x = x
  this.y = y
}
Point1.prototype.add = function(){
  return this.x + this.y
}
var p1 = new Point1(3,9)
console.log(p1.add())

class方式定义一个类

class Point2{
  constructor(x, y){
    this.x = x
    this.y = y
  }
  add(){  // 这里的add方法实际上是定义在原型上的。
    return this.x + this.y
  }
}
var p2 = new Point2(4,77)
console.log(p2.add())

类的数据类型

console.log(typeof Point1)  // function
console.log(typeof Point2)  // function

类的数据类型就是函数,类本身就是指向函数的
类的所有方法都是定义在类的prototype上的

class Point3{
  say(){
    // 
  }
  eat(){
    // 
  }
}
// 等同于以下
Point3.prototype.say = function(){
  // 
}
Point3.prototype.eat = function(){
  // 
}
// 等同于:
Point3.prototype = {
  say(){}, 
  eat(){},
}

所以:在类上调用方法,实际上是在原型上调用方法。

利用Object.assigin可以向类添加多个方法

Object.assign(Point3.prototype, {
  say(){},
  eat(){}
})

严格模式:在类的内部默认是严格模式,整个ES6都是严格模式

constructor方法

  1. constructor方法是类的默认方法。new生成实例时,会默认调用constructor方法
  2. 一个类必须有constructor方法,如果没有显式的声明,就会自动创建一个空的constructor方法
class Con{

}
// 等同于
class Con{
  constructor(){
    // 
  }
}
// JavaScript引擎会自动给Con类添加一个constructor方法, 这个方法默认会返回实例对象(this)

类的实例对象

  1. 和ES5一样,实例的属性除非显式的定义在本身(this上),否则都是定义在原型上的
  2. 所以类的所有实例都共享原型对象
var p1 = new Point2(1,2)
var p2 = new Point2(3,5)
console.log(p1.__proto__ === p2.__proto__)  // true
// 因为p1.__proto__ === Point2.prototype
//    p2.__proto__ === Point2.prototype

class表达式的形式

var person = new class{
  constructor(name){
    this.name = name
  }
  sayName(){
    console.log(this.name)
  }
}("小可爱")
person.sayName()

class不存在变量提升现象,必须要先声明后使用

this指向问题
如果方法里有this,this指向的是实例

类的静态方法

  1. 啥叫静态方法:类是实例的原型,所有类的方法都会被实例继承。但是,如果在一个方法前面加上static关键字,就代表这是一个静态方法,这个静态方法不会被实例继承,而是通过类直接调用
class Foo{
  static say(){
    console.log("hello!!")
  }
  eat(){
    console.log("eat")
  }
}
Foo.say();  // hello!!
var foo = new Foo();
// foo.say(); // 报错,这里报foo.say is not a function
// 这里说明静态方法是不可以被继承的
foo.eat();    // eat,这里说明非静态方法是可以被实例继承的。

如果静态方法有this关键字,静态方法的this指向的是类,而不是实例

静态方法可以与非静态方法可以重名

class Bar{
  static say(){
    this.foo()
  }
  foo(){
    console.log("no foo")
  }
  static foo(){
    console.log("static foo")
  }
}
Bar.say() // static foo
var bar = new Bar()
// bar.say() // 报错,因为say是静态的方法,实例不能调用
// 可以看出:静态方法可以与非静态方法重名

综上所述

// 定义一个类
class Point{
  // constructor方法,this代表实例对象
  constructor(x,y){
    this.x = x
    this.y = y
  }
  // 类的方法,除了constructor方法,都定义在类的prototype属性上
  add(){  // 定义在原型上
    return this.x + this.y
  }
}
const p = new Point(21,10);
const p2 = new Point(1,8);
console.log(typeof Point);  // 'function'
// 这里在类调用方法时,调用的是原型上的方法
console.log(p.add());       // 31
console.log(Point.prototype.constructor === Point); // true
console.log(Point.prototype === p.__proto__);  // true
// 这里在类调用方法时,调用的是原型上的方法           
console.log(p.add === Point.prototype.add); // true
// 关于实例对象的属性方法问题
// 实例属性除非定义在本身(this对象)上,否则都定义在原型上
console.log(p.hasOwnProperty("x")); // true
console.log(p.hasOwnProperty("y")); // true
console.log(p.hasOwnProperty("add")); // false
console.log(p.__proto__.hasOwnProperty("add")); // true

// 所有实例都共享同一个原型对象
console.log(p.__proto__ === p2.__proto__);  // true
// 所以说,可以通过p.__proto__来给类添加方法,但不推荐

class原型链的方式继承

class的继承是通过extends关键字实现的

基本语法
class Dog extends Animal{}
这里的Dog类,通过extends关键字,继承了Animal类所有的属性和方法,因为没有代码,所以相当于复制了一份

给继承添加代码

class Animal{
  constructor(name, age){
    this.name = name
    this.age = age
  }
  sayName(){
    return "it's name is " + this.name
  }
}
class Cat extends Animal{
  constructor(name, age, color){
    super(name, age)  // super关键字调用父元素的方法
    this.color = color
  }
  sayColor(){
    return "the color is " + this.color
  }
}
var bosimiao = new Cat("bosi", 2, 'yellow')
console.log(bosimiao.sayName()) // it's name is bosi
console.log(bosimiao.sayColor())  // the color is yellow

关于super关键字:
1、在子构造函数中,必须用super方法,否则新建对象的时候会报错
2、子构造函数中,使用this前必须先用super,否则会报错

报错例子1
报错情况:extends继承了,但是没有用super

class Person{
  constructor(name, age){
    this.name = name
    this.age = age
  }
  sayName(){
    return this.name
  }
}
class Worker extends Person{
  constructor(name, age, job){
    this.name = name
    this.age = age
    this.job = job
  }
  sayJob(){
    return this.job
  }
}
var xiaoming = new Worker("xiaoming", 28, "teacher")
console.log(xiaoming.sayName())
console.log(xiaoming.sayJob())

解决:在constructor中,先用一下super

constructor(name, age, job){
    super()
    this.name = name
    this.age = age
    this.job = job
  }

报错例子2:先写了this,然后调用的super()

constructor(name, age, job){
    this.name = name
    super()
    this.job = job
  }

为啥这两种情况会报错呢?
ES6的继承机制:先创建父类的实例对象,再创建子类的实例,然后修改父类的this

子类没有自己的this对象,而是继承父类的this对象,然后对其加工。如果不调用super方法, 就得不到this对象

子类调用父类的方法:

  class Person{
    constructor(name, age){
      this.name = name
      this.age = age
    }
    sayName(){
      return "person name is " + this.name
    }
  }
  class Worker extends Person{
    constructor(name, age, job){
      super(name, age)
      this.job = job
    }
  }
  var p1 = new Worker("lanlan", 18, "student")
  console.log(p1.sayName()) // worker name is lanlan

通过原型链实现继承

原理

我们每创建一个函数,都有一个prototype属性(自带的),这个prototype属性是一个指针,指向了一个对象,这个对象是原型对象。

这个原型对象上有一个constructor属性。这个constructor属性是一个指针,指向了构造函数。

构造函数的new出来的实例对象,有一个__proto__属性(内部属性),这个属性指向原型对象。

所以实例也能访问原型对象的属性和方法。
原型对象通过constructor指针指向了构造函数

所有的构造函数也是Function的实例
所有构造函数的原型对象是Object的实例

所以,实例,构造函数,原型对象的关系是:
[].__proto__ === Array.prototype === 原型对象
[].__proto__.constructor === Array
Animal.prototype.constructor === Animal

prototype:每个函数都有显式原型prototype
__proto__:每个对象实例都有隐式原型__proto__
constructor:构造函数的原型有一个constructor属性,指向创建该对象的构造函数

演示constructor

var arr = [];
console.log(arr.constructor === Array); // true

题目

手写class B继承class A

ES5的方式

function A(){
  this.superType = "A"
}
A.prototype.getSuperType = function(){
  console.log(this.superType)
}
function B(){
  this.type = "B"
}
B.prototype = new A();
var b = new B();
b.getSuperType();

ES6的方式

class A{
  constructor(superType){
    this.superType = superType
  }
  getSuperType(){
    return "superType is " + this.superType
  }
}
class B extends A{
  constructor(superType, type){
    super(superType)
    this.type = type
  }
}
let b = new B("superType", "type")
console.log(b.getSuperType());  // superType is superType

相关文章

  • ES6

    ES6是一个语言标准,不是一个框架。 ES6中的class与继承 class是创建类对象与实现类继承的语法糖,旨在...

  • react组件

    es6 的class类的继承 运用es6 class继承 通过继承React.Component类来定义一个组件

  • JavaScript继承

    es6继承 class Square extends Polygon {constructor(length) {...

  • [JavaScript] class

    ES6中的class基于原型继承创建类。 (1)class只是原型继承的语法糖 相当于: 注: class定义中...

  • ES6 class与继承

    class是什么 class是定义类的方法。ES6之前用构造函数的方式定义类,ES6引入了class。 class...

  • JavaScript ES6 class多重继承实践与总结

    ES6中,class原生是不支持多重继承的,根据阮一峰ES6参考资料中的方法,通过以下方式即可实现class继承多...

  • es6中class类的全方面理解(二)------继承

    继承是面向对象中一个比较核心的概念。ES6 class的继承与java的继承大同小异,如果学过java的小伙伴应该...

  • ES6新特性

    ES6新特性 1.类(支持继承 extends)class TestClass { constructor(...

  • JS中类的继承封装和多态

    子类继承父类的属性和方法(原生继承,call继承,寄生组合继承,ES6中class类继承)原生继承:让子类的原型指...

  • Javascript原型和原型链

    JavaScript在ES6之前没有类似class,extend的继承机制,JavaScript的继承主要是通过原...

网友评论

    本文标题:ES6 class与继承

    本文链接:https://www.haomeiwen.com/subject/svfncctx.html