介绍
本章主要介绍ES6的类,类是用于创建对象的模板,他们用代码封装数据以处理该数据,JS中的类建立在原型上,但也具有某些语法和语义未与ES5类相似语义共享。
定义类
实际上,类是“特殊的函数”,就像你能够定义的函数表达式和函数声明一样,类语法有两个组成部分:类表达式和类声明。
类声明
定义类的一种方法是使用类声明。要声明一个类,你可以使用带有class
关键字的类名(这里是Rectangle)。
class Rectangle {
constructor(height,width) {
this.height = height;
this.width = wdith;
}
}
提升
函数声明和类声明之间的一个重要区别在于,函数声明会提升,类声明不会。你首先需要声明你的类,然后再访问它,否则类似以下的代码将抛出ReferenceError:
let p = new Rectangle(); // ReferenceError
class Rectangle {}
类表达式
类表达式是定义类的另一种方法。类表达式可以命名或不命名。命名类表达式的名称是该类体的局部名称。(不过,可以通过类的(而不是一个实例的)name属性来检索它)。
// 未命名/匿名类
let Rectangle = class {
constructor(height,width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name); // output: "Rectangle"
// 命名类
let Rectangle = class Rectangle2 {
constructor(height,width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle); // Rectangle2
类表达式也同样受到类声明部分中提到的类型提升的限制。
类体和方法定义
一个类的类体是一对花括号/大括号{}中的部分。这是你定义类成员的位置。如方法或构造函数、
严格模式
类声明和类表达式的主体都执行在严格模式下,比如,构造函数,静态方法,原型方法,getter和setter都在严格模式下执行。
构造函数
constructor
方法是一个特殊的方法,这种方法用于创建和初始化一个由class
创建的对象。一个类的只能拥有一个名为"constructor"的特殊方法。如果类包含多个constructor的方法,则将抛出一个SyntaxError。
一个构造函数可以使用super关键字来调用一个父类的构造函数。
原型方法
class Rectangle {
// constructor
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea()
}
// Method
calcArea() {
return this.height * this.width;
}
}
const square = new Rectanle(10,10);
console.log(square.area); // 100
静态方法
static
关键字用来定义一个类的静态方法。调用静态方法不需要实例化该类。但不能通过一个类实例调用静态方法。静态方法通常用于为一个应用程序创建工具函数。
class Point {
constructor(x ,y) {
this.x = x;
this.y = y;
}
static displayName = "Point";
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
p1.dispalyNmae; // undefined
p1.distance; // undefined
console.log(Point.displayName); // Point
console.log(Point.distance(p1, p2)); // 7.0710678118654755
console.log(Math.hypot(-5,-5)); // 7.0710678118654755
用原型和静态方法绑定this
当调用静态或原型方法时没有指定this的值,那么方法内的this值将被置为undefined
。即使你未设置"use strict"
,因为class
体内部的代码总是在严格模式下执行。
class Animal {
speak() {
return this;
}
static eat() {
return this;
}
}
let obj = new Animal();
obj.speak(); // Animal {}
let speak = obj.speak;
speak(); // undeinfed
Animal.eat(); // class Animal
let eat = Animal.eat;
eat(); // undeinfed
如果上述代码通过传统的基于函数的语法来实现,那么依据初始的this值,在非严格模式下方法调用会发生自动装箱,若初始值是undefined
,this值会被设定为全局对象。
严格模式下不会发生自动装箱,this值将保留传入状态。
function Animal() { }
Animal.prototype.speak = function(){
return this;
}
Animal.eat = function(){
return this;
}
let obj = new Animal();
let speak = obj.speak;
speak(); // global object
let eat = Animal.eat;
eat(); // global object
上面的两个例子,分别使用类和函数返回this,由类创建的方法,在赋值后this
指向因为严格模式成为undeinfed,
而函数因为赋值后使用全局对象上的变量调用的方法,所以this指向全局对象window,关于this指向详细请浏览我的另一篇文章JavaScript-this指向。
使用extends
扩展子类
extends
关键字在 类声明或类表达式中用于创建一个类作为另一个类的子类。
class Animal {
constructor(name){
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name){
super(name); // 调用超类构造函数并传入name参数
}
speak() {
console.log(`${this.name} barks.`)
}
}
var d = new Dog('Mitzie');
d.speak(); // 'Mitzie barks.'
如果子类中定义了构造函数,那么它必须先调用super
才能使用this
。
也可以继承传统的基于函数的“类”:
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(this.name + ' makes a noise.')
}
calss Dog extends Animal {
speak() {
super.speak(); // 执行原型对象身上的speak方法
console.log(this.name + ' barks.');
}
}
var d = new Dog('Mitzie');
d.speak(); // Mitzie makes a noise. Mitzie barks.
请注意,类不能继承常规对象(不可构造的)。如果要继承常规对象,可以改用Object.setPrototypeOf()
:
var Animal = {
speak(){
console.log(this.name + ' makes a noise.');
}
}
class Dog {
constructor(name){
this.name = name;
}
}
Object.setPrototypeOf(Dog,prototype, Animal); // 修改原型对象,如果不这样做在调用speak时会返回TypeError
vra d = new Dog('Mitzie');
d.speak(); // Mitzie makes a noise.
Es5中的类和Es6中的类
Es5中的类
1.构造函数
function Person(name ,age){
this.name = name;
this.age = age;
}
2.prototype 原型对象 (在类的原型对象上定义方法)
Person.prototype.eat = function() {
console.log("log eat funciton");
}
3.继承方法:子类的构造函数通过父类.call
继承属性
子类的原型对象等于new 父类 继承方法
此时原型对象的constructor是父类的原型对象,正常情况下,一个类的constructor
应该指向此类的原型对象,所以我们要把Dog赋值给Dog.prototype.constructor
function Dog(name, age){
//继承 在子类的构造函数 通过父类.call继承属性
Person.call(this, name, age);
}
Dog.prototype = new Person();
Dog.prototype.constructor = Dog;
Es6中的类
1.es6中类的定义比es5方便很多,直接就可以在constructor中定义我们的数据,在里边直接定义函数方法
class person {
constructor(name) {
this.name = name;
}
//方法
eat() {
console.log("水蜜桃")
}
static say() {
console.log('hello');
}
}
2.类的继承通过extends
方法继承父类,constructor
中新增我们的数据,利用super
继承父类的数据,就继承完毕了
class Dog extends person {
constructor(name, age) {
super(name);
this.age = age;
}
}
var d = new Dog('旺财','3');
console.log(d); // Dog {name: '旺财', age: '3'}