在ES6中,引入了class
关键字用于快速地定义“类”。
在JS中,“类”的本质是function
可以将其看做一个语法糖,让对象原型的写法更简洁清晰,更像面向对象编程的语法。
涉及关键字:
class 声明类模板
constructor 构造方法
get getter方法
set setter方法
static 静态成员
extends 继承
super 在子类中表示父类对象
class
的本质是函数function
。class Person {
constructor(a) {
this.a = a;
}
}
console.log(typeof Person); // function
// class Person {} // 报错:不能重复定义
// Uncaught SyntaxError: Identifier 'Person' has already been declared
var p = class Person {}; // 有名类表达式
new p(); // Person {}
// new Person(); // Uncaught ReferenceError: Person is not defined
var p = class {}; // 匿名类表达式
var a = class AAA {};
var b = class AAA {}; // 不报错
console.log(a == b); // false, a和b是两个不同的类(类声明)。
类定义不会提升(hoisting)
使用class
简洁地定义类,应该先定义,后使用。否则报错。
类中的方法
方法不能使用function
关键字。
方法之间使用分隔符;
,可省略(会自动补全)。
原型变量之间的分隔符也是;
。
ES6中,虽然新增了class
关键字用于类定义,但其实质还是function
,只不过是为了方便书写,随后经过了某种源代码编译过程。
prototype
仍然存在。在类体中定义的方法还是定义在prototype
上。
class Person {}
类 :Person
原型:Person.prototype
原型成员
无static
关键字的成员。
this.xxx
类名.prototype.xxx
class Person {
// xxx
name = "无";
eat() { // 原型方法 推荐写法
console.log("eat func");
};
// this.xxx 打印对象时会打印出来。
constructor(a) {
this.age = a; // 原型属性 推荐写法
this.drink = function() {
console.log("drink func");
};
};
}
// 类名.prototype.xxx 打印对象时不会打印。但可以访问到。
Person.prototype.gender = "男"; // 不推荐
Person.prototype.foo = function() { // 可以
console.log("foo func");
};
var a = new Person(22);
a; // Person {name: "无", age: 22, drink: ƒ}
Person.prototype; // {gender: "男", foo: ƒ, constructor: ƒ, eat: ƒ}
推荐:
constructor
中用this.xxx
即可。类名.prototype
对象中。这两种都行。静态成员
带关键字static
类名.xxx
ES6中规定,class内部只有静态方法,没有静态属性。
但是在新提案中,有静态属性。
现阶段:
constructor()
中不能调用静态成员(方法,属性)xxx is not defined
class Person {
stativ version = "1.0"; // 不推荐,并非ES6中实现的标准
static sum(a, b) {return a + b;}; // 静态方法 推荐写法
constructor() {}
}
Person.desc = "人类"; // 静态属性 推荐写法
Person.sub = function(a, b) {return a - b;};
因为版本实现问题,推荐写法:
类名.xxx = value;
定义。static
声明,或写在外部也可以(两种都行)。向对象添加成员(成员方法,成员属性)
向类名
添加成员,就相当于静态成员。
向类名.prototype
添加成员,就相当于原型成员。
有两种方法:
obj.key = value;
Object.assign(target, ...sources);
constructor
constructor
方法是类的默认构造方法,创建类对象的时候被调用。constructor
方法中返回指定的类型对象(可与本类不相同)。class One {
constructor() {
console.log("One constructor()");
// return this; // 默认返回this实例对象
}
}
class Two {
constructor() {
return new One();
}
}
var a = new Two(); // One constructor()
console.log(a instanceof Two); // false
console.log(a instanceof One); // true
可见,使用new
操作符实例化出的对象,实质是什么类型,取决于其构造方法constructor()
的返回值。
getters, setters
set
和get
,置于方法名前(如get name() {...}
和set name() {...}
)。setter, getter
中访问成员属性的格式:this._xxx
而非this.xxx
。get
方法set
方法
this.prop = value
;obj.prop = value
;getter, setter
不可单独出现
setter
,不能通过obj.xxx
读取xxx
的值。但可以通过打印obj
查看xxx
的值。getter
,
constructor()
中有对应的this.xxx=value;
的语句,则 因为没有setter,所以实例化时会报错。constructor()
中没有this.xxx=value;
的语句。则实例化时不报错。set
值,用不了,只能get
到undefined
的值)。getter, setter
必须同级出现(继承关系中)。class Person {
constructor(age) {
this.age = age;
};
get age() {
console.log("get age()");
return this._age; // 注意是this._age
};
set age(age) {
console.log("set age()");
this._age = age; // 注意是this._age
}
}
var p = new Person(22); // set age()
console.log(p); // Person {_age: 22}
console.log(p.age); // 22
console.log(p._age); // 22
在getter, setter
中调用原型属性:this._xxx
,
而非this.xxx
,否则会在实例化对象(调用构造方法时)报错RangeError
。
decorator
decorator
是一个函数,用来修改类的行为,在代码编译时产生作用。decorator
方法不是ES6标准,是ES7的新提案之一。extends
关键字:extends, super
super
super
表示父类。super(...argsList)
表示父类的构造方法super.xxx
可以访问父类的成员属性super.xxx()
可以访问父类的成员方法在子类的构造犯法中访问this
或返回返回值之前,必须先调用父类的构造方法super()
。
Must call super constructor in derived class before accessing 'this' or returning from derived constructor
。
class Father {
constructor(firstName) {
console.log("father...");
this.firstName = firstName;
}
}
class Child extends Father {
constructor(firstName, lastName) {
super(firstName);
console.log("child...");
this.lastName = lastName;
}
}
Father.prototype.isPrototypeOf(Child.prototype); // true
var a = new Child("J", "DD");
// father...
// child...
// Child {firstName: "J", lastName: "DD"}
super.xxx()
,其中xxx
为原型方法名。super.xxx()
或父类名.xxx()
,其中xxx
为静态方法名。class Father {
foo() {console.log("foo...");}
static bar() {console.log("bar...")}
}
class Child extends Father {
f() {
super.foo(); // 在子类的原型方法中调用父类的原型方法
}
static b() {
super.bar(); // 在子类的静态方法中调用父类的原型方法
Father.bar();
}
}
Child.b() // 用类名调用静态方法
// bar...
// bar...
(new Child()).f() // 用对象调用原型方法
// foo...
即:
父类的构造方法可以在子类的构造方法中被调用。
父类的原型方法可以在子类的原型方法中被调用。
父类的静态方法可以再子类的静态方法中被调用。
否则出错。
var obj = {name:"JT", showName:function() {console.log(this.name)}};
Child
,也拥有这个对象的成员。Uncaught TypeError: Class extends value #
var Father = {name:"JT"};
class Child extends Father {};
var Father = {name:"JT"};
class Child {};
Object.setPrototypeOf(Child.prototype, Father);
(new Child()).name; // "JT"