JavaScript高级 ES6类

1. 认识class定义类

我们会发现,按照前面ES5的构造函数形式创建类,不仅仅和编写普通的函数过于相似,而且代码并不容易理解,而在ES6新的标准中使用了class关键字来直接定义类,但是类本质上依然是前面所讲的构造函数、原型链的语法糖而已

使用class来定义类的方法有两种:类声明和类表达式

	// ES5中定义类	
    // function Person() {}

    // ES6定义类
    class Person {

    }
    // 创建实例对象
    var p1 = new Person()
    var p2 = new Person()
    console.log(p1, p2)

    // 另外一种定义方法: 表达式写法(了解, 少用)
    var Student = class {

    }
    var stu1 = new Student()
    console.log(stu1)

2. 类和构造函数的异同

类和我们的构造函数的特性其实是一致的,但是不可以作为普通方法被调用

    // function定义类
    function Person1(name, age) {
 
    }


    var p1 = new Person1()

    // 不同点: 作为普通函数去调用
    Person1()

    // class定义类
    class Person2 {
      constructor() {

      }
    }

    var p2 = new Per)

    // 不同点: class定义的类, 不能作为一个普通的函数进行调用
    Person2()

3. 类的构造函数

每个类都可以有一个自己的构造函数(方法),这个方法的名称是固定的constructor,当我们通过new操作符,操作一个类的时候会调用这个类的构造函数constructor,每个类只能有一个构造函数,如果包含多个构造函数,那么会抛出异常

当我们通过new关键字操作类的时候,会调用这个constructor函数,并且执行如下操作:

  1. 在内存中创建一个新的对象(空对象)

  2. 这个对象内部的[[prototype]]属性会被赋值为该类的prototype属性

  3. 构造函数内部的this,会指向创建出来的新对象

  4. 执行构造函数的内部代码(函数体代码)

  5. 如果构造函数没有返回非空对象,则返回创建出来的新对象;

    // 编程: 高内聚低耦合
    class Person {
      // 1.类中的构造函数
      // 当我们通过new关键字调用一个Person类时, 默认调用class中的constructor方法
      constructor(name, age) {
        this.name = name
        this.age = age
      }
    }

    // 创建实例对象
    var p1 = new Person("why", 18)

    // 使用实例对象中属性
    console.log(p1.name, p1.age)

4. 类的实例方法

在上面我们定义的属性都是直接放到了this上,也就意味着它是放到了创建出来的新对象中,在前面我们说过对于实例的方法,我们是希望放到原型上的,这样可以被多个实例来共享,这个时候我们可以直接在类中定义

    // 编程: 高内聚低耦合
    class Person {
      // 1.类中的构造函数
      // 当我们通过new关键字调用一个Person类时, 默认调用class中的constructor方法
      constructor(name, age) {
        this.name = name
        this.age = age
      }

      // 2.实例方法
      // 本质上是放在Person.prototype
      running() {
        console.log(this.name + " running~")
      }
      eating() {
        console.log(this.name + " eating~")
      }
    }

    // 创建实例对象
    var p1 = new Person("why", 18)

    // 使用实例对象中属性和方法
    console.log(p1.name, p1.age)
    p1.running()
    p1.eating()

    // 研究内容
    console.log(Person.prototype === p1.__proto__) // true
    console.log(Person.running) // undefined
    console.log(Person.prototype.running) // 返回函数

5. 类的访问器方法

对象的属性描述符时有讲过对象可以添加setter和getter函数的

    // 针对对象
    // 方式一: 描述符
    // var obj = {
      // _name: "why"
    // }
    // Object.defineProperty(obj, "name", {
    //   configurable: true,
    //   enumerable: true,
    //   set: function() {
    //   },
    //   get: function() {
    //   }
    // })

    // 方式二: 直接在对象定义访问器
    // 监听_name什么时候被访问, 什么设置新的值
    var obj = {
      _name: "why",
      // setter方法
      set name(value) {
        this._name = value
      },
      // getter方法
      get name() {
        return this._name
      }
    }

    obj.name = "kobe"
    console.log(obj.name)

那么类也是可以添加setter和getter函数

    // 1.访问器的编写方式
    class Person {
      // 程序员之间的约定: 以_开头的属性和方法, 是不在外界访问
      constructor(name, age) {
        this._name = name
      }

      set name(value) {
        console.log("设置name")
        this._name = value
      }

      get name() {
        console.log("获取name")
        return this._name
      }
    }

    var p1 = new Person("why", 18)
    p1.name = "kobe"
    console.log(p1.name)
    // console.log(p1._name)

    var p2 = new Person("james", 25)
    console.log(p2.name)


    // 2.访问器的应用场景
    class Rectangle {
      constructor(x, y, width, height) {
        this.x = x
        this.y = y
        this.width = width
        this.height = height
      }

      get position() {
        return { x: this.x, y: this.y }
      }

      get size() {
        return { width: this.width, height: this.height }
      }
    }

    var rect1 = new Rectangle(10, 20, 100, 200)
    console.log(rect1.position)
    console.log(rect1.size)

6. 类的静态方法

静态方法通常用于定义直接使用类来执行的方法,不需要有类的实例,使用static关键字来定义

    // class定义的类
    var names = ["abc", "cba", "nba", "mba"]
    class Person {
      constructor(name, age) {
        this.name = name
        this.age = age
      }

      // 实例方法
      running() {
        console.log(this.name + " running~")
      }
      eating() {}

      // 类方法(静态方法)
      static randomPerson() {
        console.log(this)
        var randomName = names[Math.floor(Math.random() * names.length)]
        return new this(randomName, Math.floor(Math.random() * 100))
      }
    }
    
    var randomPerson = Person.randomPerson()
    console.log(randomPerson)

7. ES6类的继承

在ES6中新增了使用extends关键字,可以方便的帮助我们实现继承

    // 定义父类
    class Person {
      constructor(name, age) {
        this.name = name
        this.age = age
      }

      running() {
        console.log("running~")
      }
      eating() {
        console.log("eating~")
      }

    }

    class Student extends Person {
      constructor(name, age, sno, score) {
        super(name, age)
        this.sno = sno
        this.score = score
      }
      studying() {
        console.log("studying~")
      }
    }
    
    class Teacher extends Person {
      constructor(name, age, title) {
        super(name, age)
        this.title = title
      }
      teaching() {
        console.log("teaching~")
      }
    }

    var stu1 = new Student("why", 18, 111, 100)
    stu1.running()
    stu1.eating()
    stu1.studying()

8. super关键字

我们会发现在上面的代码中我使用了一个super关键字,这个super关键字有不同的使用方式,但是在子类的构造函数中使用this或者返回默认对象之前,必须先通过super调用父类的构造函数

super() 执行父类的构造方法

super.method() 执行父类的方法

super的使用位置有三个:子类的构造函数、实例方法、静态方法

   class Animal {
      running() {
        console.log("running")
      }
      eating() {
        console.log("eating")
      }

      static sleep() {
        console.log("static animal sleep")
      }
    }

    class Dog extends Animal {
      // 子类如果对于父类的方法实现不满足(继承过来的方法)
      // 重新实现称之为重写(父类方法的重写)
      running() {
        console.log("dog四条腿")
        // 调用父类的方法
        super.running()
        // console.log("running~")
        // console.log("dog四条腿running~")
      }

      static sleep() {
        console.log("趴着")
        super.sleep()
      }
    }

    var dog = new Dog()
    dog.running()
    dog.eating()

    Dog.sleep()

9. 继承内置类

    // 1.创建一个新的类, 继承自Array进行扩展
    class HYArray extends Array {
      get lastItem() {
        return this[this.length - 1]
      }

      get firstItem() {
        return this[0]
      }
    }

    var arr = new HYArray(10, 20, 30)
    console.log(arr)
    console.log(arr.length)
    console.log(arr[0])
    console.log(arr.lastItem)
    console.log(arr.firstItem)

    // 2.直接对Array进行扩展
    Array.prototype.lastItem = function() {
      return this[this.length - 1]
    }

    var arr = new Array(10, 20, 30)
    console.log(arr.__proto__ === Array.prototype)
    console.log(arr.lastItem())

    // 函数apply/call/bind方法 -> Function.prototype

10. 类的混入

    // JavaScript只支持单继承(不支持多继承)
    function mixinAnimal(BaseClass) {
      return class extends BaseClass {
        running() {
          console.log("running~")
        }
      }
    }

    function mixinRunner(BaseClass) {
      return class extends BaseClass {
        flying() {
          console.log("flying~")
        }
      }
    }

    class Bird {
      eating() {
        console.log("eating~")
      }
    }

    // var NewBird = mixinRunner(mixinAnimal(Bird))
    class NewBird extends mixinRunner(mixinAnimal(Bird)) {
    }
    var bird = new NewBird()
    bird.flying()
    bird.running()
    bird.eating()
    console.log(bird);

在这里插入图片描述

11. ES5 ES6 class 代码转换

babeljs.io

ES6 class

    class Person {
      constructor(name, age) {
        this.name = name
        this.age = age
      }

      running() {}
      eating() {}

      static randomPerson() {}
    }

    var p1 = new Person()

es5

"use strict";

// 检测是否使用函数调用
// 如果作为函数调用, instance 传入的值为 window,Constructor 传入的值为 Person 构造函数
// new 调用传入的则是 this 指向的空对象
function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

function _defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
    var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    Object.defineProperty(target, descriptor.key, descriptor);
  }
}

function _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  Object.defineProperty(Constructor, "prototype", { writable: false });
  return Constructor;
}

// 纯函数: 相同输入一定产生相同的输出, 并且不会产生副作用
// 立即执行函数进行调用
var Person = /*#__PURE__*/ (function () {
  debugger

  function Person(name, age) {
    _classCallCheck(this, Person);

    this.name = name;
    this.age = age;
  }

  _createClass(
  	// 对应方法的三个参数 Constructor构造函数, protoProps实例方法, staticProps类方法
    Person,
    [
      {
        key: "running",
        value: function running() {}
      },
      {
        key: "eating",
        value: function eating() {}
      }
    ],
    [
      {
        key: "randomPerson",
        value: function randomPerson() {}
      }
    ]
  );

  return Person;
})();

var p1 = new Person("why", 18)

12. ES5 ES6 extends 代码转换

ES6 class

 	 class Person {
      constructor(name, age) {
        this.name = name
        this.age = age
      }

      running() {}
      eating() {}

      static randomPerson() {}
    }

    class Student extends Person {
      constructor(name, age, sno, score) {
        super(name, age)
        this.sno = sno
        this.score = score
      }

      studying() {}
      static randomStudent() {}
    }

    var stu = new Student()

es5

	"use strict";
	
	function _typeof(obj) {
	  "@babel/helpers - typeof";
	  return (
	    (_typeof =
	      "function" == typeof Symbol && "symbol" == typeof Symbol.iterator
	        ? function (obj) {
	            return typeof obj;
	          }
	        : function (obj) {
	            return obj &&
	              "function" == typeof Symbol &&
	              obj.constructor === Symbol &&
	              obj !== Symbol.prototype
	              ? "symbol"
	              : typeof obj;
	          }),
	    _typeof(obj)
	  );
	}
	
	// 继承机制
	function _inherits(subClass, superClass) {
	  if (typeof superClass !== "function" && superClass !== null) {
	    throw new TypeError("Super expression must either be null or a function");
	  }
	  // 核心代码
	  subClass.prototype = Object.create(superClass && superClass.prototype, {
	    constructor: { value: subClass, writable: true, configurable: true }
	  });
	  Object.defineProperty(subClass, "prototype", { writable: false });
	  // 核心代码结束
	  if (superClass) _setPrototypeOf(subClass, superClass);
	}
	
	function _setPrototypeOf(o, p) {
	  _setPrototypeOf = Object.setPrototypeOf
	    ? Object.setPrototypeOf.bind()
	    : function _setPrototypeOf(o, p) {
	        o.__proto__ = p;
	        return o;
	      };
	  return _setPrototypeOf(o, p);
	}
	
	function _createSuper(Derived) {
	  var hasNativeReflectConstruct = _isNativeReflectConstruct();
	  return function _createSuperInternal() {
	    var Super = _getPrototypeOf(Derived),
	      result;
	    if (hasNativeReflectConstruct) {
	      var NewTarget = _getPrototypeOf(this).constructor;
	      result = Reflect.construct(Super, arguments, NewTarget);
	    } else {
	      result = Super.apply(this, arguments);
	    }
	    return _possibleConstructorReturn(this, result);
	  };
	}
	
	function _possibleConstructorReturn(self, call) {
	  if (call && (_typeof(call) === "object" || typeof call === "function")) {
	    return call;
	  } else if (call !== void 0) {
	    throw new TypeError(
	      "Derived constructors may only return object or undefined"
	    );
	  }
	  return _assertThisInitialized(self);
	}
	
	function _assertThisInitialized(self) {
	  if (self === void 0) {
	    throw new ReferenceError(
	      "this hasn't been initialised - super() hasn't been called"
	    );
	  }
	  return self;
	}
	
	function _isNativeReflectConstruct() {
	  if (typeof Reflect === "undefined" || !Reflect.construct) return false;
	  if (Reflect.construct.sham) return false;
	  if (typeof Proxy === "function") return true;
	  try {
	    Boolean.prototype.valueOf.call(
	      Reflect.construct(Boolean, [], function () {})
	    );
	    return true;
	  } catch (e) {
	    return false;
	  }
	}
	
	function _getPrototypeOf(o) {
	  _getPrototypeOf = Object.setPrototypeOf
	    ? Object.getPrototypeOf.bind()
	    : function _getPrototypeOf(o) {
	        return o.__proto__ || Object.getPrototypeOf(o);
	      };
	  return _getPrototypeOf(o);
	}
	
	function _classCallCheck(instance, Constructor) {
	  if (!(instance instanceof Constructor)) {
	    throw new TypeError("Cannot call a class as a function");
	  }
	}
	
	function _defineProperties(target, props) {
	  for (var i = 0; i < props.length; i++) {
	    var descriptor = props[i];
	    descriptor.enumerable = descriptor.enumerable || false;
	    descriptor.configurable = true;
	    if ("value" in descriptor) descriptor.writable = true;
	    Object.defineProperty(target, descriptor.key, descriptor);
	  }
	}
	
	function _createClass(Constructor, protoProps, staticProps) {
	  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
	  if (staticProps) _defineProperties(Constructor, staticProps);
	  Object.defineProperty(Constructor, "prototype", { writable: false });
	  return Constructor;
	}
	
	var Person = /*#__PURE__*/ (function () {
	  function Person(name, age) {
	    _classCallCheck(this, Person);
	    this.name = name;
	    this.age = age;
	  }
	  _createClass(
	    Person,
	    [
	      {
	        key: "running",
	        value: function running() {}
	      },
	      {
	        key: "eating",
	        value: function eating() {}
	      }
	    ],
	    [
	      {
	        key: "randomPerson",
	        value: function randomPerson() {}
	      }
	    ]
	  );
	  return Person;
	})();
	
	function inherit(SubType, SuperType) {
	  SubType.prototype = Object.create(SuperType.prototype)
	  SubType.prototype.constructor = SubType
	}
	
	debugger
	var Student = /*#__PURE__*/ (function (_Person) {
	  _inherits(Student, _Person);
	
	  var _super = _createSuper(Student);
	
	  function Student(name, age, sno, score) {
	    var _this;
	
	    _classCallCheck(this, Student);
	
	    _this = _super.call(this, name, age);
	    _this.sno = sno;
	    _this.score = score;
	    return _this;
	  }
	
	  _createClass(
	    Student,
	    [
	      {
	        key: "studying",
	        value: function studying() {}
	      }
	    ],
	    [
	      {
	        key: "randomStudent",
	        value: function randomStudent() {}
	      }
	    ]
	  );
	
	  return Student;
	})(Person);
	
	var stu = new Student("why", 18, 111, 100);

13. JavaScript中的多态

维基百科对多态的定义:多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口,或使用一个单一的符号来表示多个不同的类型

那么从上面的定义来看,JavaScript是一定存在多态的

    // 继承是多态的前提
    // shape形状
    class Shape {
      getArea() {}
    }

    class Rectangle extends Shape {
      constructor(width, height) {
        super()
        this.width = width
        this.height = height
      }

      getArea() {
        return this.width * this.height
      }
    }

    class Circle extends Shape {
      constructor(radius) {
        super()
        this.radius = radius
      }

      getArea() {
        return this.radius * this.radius * 3.14
      }
    }

    var rect1 = new Rectangle(100, 200)
    var rect2 = new Rectangle(20, 30)
    var c1 = new Circle(10)
    var c2 = new Circle(15)

    // 表现形式就是多态
    /*
      在严格意义的面向对象语言中, 多态的是存在如下条件的:
        1.必须有继承(实现接口)
        2.必须有父类引用指向子类对象
    */
    function getShapeArea(shape) {
      console.log(shape.getArea())
    }

    getShapeArea(rect1)
    getShapeArea(c1)



    // 多态的表现: JS到处都是多态
    function sum(a1, a2) {
      return a1 + a2
    }

    sum(20, 30)
    sum("abc", "cba")
    
    // 多态的表现
    var foo = 123
    foo = "Hello World"
    console.log(foo.split())
    foo = {
      running: function() {}
    }
    foo.running()
    foo = []
    console.log(foo.length)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ximingx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值