一、基本语法
1.简介
传统JS中创建对象是通过**「构造函数」**,例如:
// 通过构造函数创建对象
function Person(name,age){
this.name=name;
this.age = age;
}
// 创建实例
let p1 = new Person("小红",18);
console.log("p1:",p1)
// Person的prototype和p1的__proto__指向同一个对象
console.log(Person.prototype === p1.__proto__) // true
基本上,ES6 的 class 可以看作只是一个语法糖
,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
用class的语法改写如下:
class PersonCopy{
constructor(name,age){
this.name=name;
this.age=age;
}
run(){
console.log(this.name +" is running")
}
}
let p2 = new PersonCopy("小明",12);
console.log("p2:",p2)
console.log(typeof PersonCopy); // function
console.log(PersonCopy.prototype.constructor===PersonCopy) //true
p2.run();
可以得知:
- 类的数据类型就是函数
- 类本身就指向构造函数
而且:
- 实例属性是添加到对象上的
- 方法是定义到原型对象prototype上的,各个实例共用同一个方法
{
"name": "小明",
"age": 12
[[prototype]]:{
constructor:f() class PersonCopy,
run: f run()
}
}
相当于:
function PersonCopy(name,age){
this.name = name;
this.age =age;
}
// 方法加到原型对象上,所有实例共用。这样就只会存在一个run函数对象
// 而不会为每个对象都创建一个run函数对象
PersonCopy.prototype.run = function(){
console.log(this.name +" is running")
}
let p3 = new PersonCopy("小王",13)
p3.run() // 小王 is running
由于类的方法都是定义在类的prototype上的,所以可以通过Object.assign()方法为类一次性添加多个方法:
Object.assign(PersonCopy.prototype,{
toString(){},
toValue(){}
})
2.constructor
constructor
方法是类
的默认方法,通过 new 命令生成对象实例时,自动调用该方法。一个类必须有 constructor 方法,如果没有显式定义,一个空的 constructor 方法会被默认添加。
类必须使用 new 调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用 new 也可以执行。
与 ES5 一样,实例的属性除非显式定义在其本身(即定义在 this 对象上),否则都是定义在原型上(即定义在 class 上)。
3.getter和setter
class Man{
constructor(){} // 可不写
// 会给prototype上添加一个name属性
get name(){
return this.nickname;
}
set name(name){
this.nickname = name;
}
}
let m = new Man();
console.log(m);
m.name="hello";
console.log(m.name) //hello
console.log(m.nickname); //hello
m.hasOwnProperty('name') // false
m.hasOwnProperty('nickname') //true
Man.prototype.hasOwnProperty('name') // true
4.属性表达式
类的属性名,可以采用表达式。属性和方法都可以用表达式。
// 属性表达式
let prop1 = "name";
let prop2 = "run";
class Course{
constructor(name){
this[prop1] = name;
}
[prop2](){
console.log(prop2)
}
}
let c = new Course("基础");
console.log(c)
console.log(c.name)
c.run()
5.class表达式
与函数一样,类也可以使用表达式的形式定义。
const A = class B{
getClassDeclareName(){
return B.name + A.name; // A.name也可以
}
}
let a = new A();
console.log( a.getClassDeclareName())
console.log(A.name) // B
console.log(B.name) // error
这个类的名字是B,但是只能在类内部使用,在外部只能使用A。A在内部和外部都可以使用。
6.静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
class A{
constructor(name){
this.name = name;
}
run(){
console.log(this.name +" is running")
}
static step(){
console.log(this.name +" is step") // 此处this指向类A
}
}
let a1 = new A("小红");
console.log("a1",a1)
a1.run()
A.step()
a1.step(); //error 实例没有该方法
- 实例调用静态方法会报错
- 如果静态方法包含 this 关键字,这个 this 指的是类,而不是实例
- 静态方法和非静态方法可以重名,因为一个是实例的,一个是类的。如下:调用a1.step()就不会报错了,调用的是非静态方法step
class A{
constructor(name){
this.name = name;
}
run(){
console.log(this.name +" is running")
}
step(){
console.log("实例方法step")
}
static step(){
console.log(this.name +" is step") // 此处this指向类A
}
}
let a1 = new A("小红");
console.log("a1",a1)
a1.run()
A.step()
a1.step(); // 实例方法step
7.实例属性的新写法
实例属性
除了定义在constructor()
方法里面的 this 上面,也可以定义在类
的最顶层。
class B{
name;
age;
getName(){
return this.name;
}
setName(value){
this.name = value;
}
}
let b =new B();
b.setName("hello")
console.log(b)
console.log(b.getName())
8.静态属性
静态属性
指的是Class
本身的属性,即 Class.propName
,而不是定义在实例对象( this )上的属性。
// 写法1
class A{
}
A.foo=1;
let a = new A();
console.log(A.foo); //1
console.log(a.foo) // undefined
// 写法2
class B{
static foo = 1;
}
let b = new B();
console.log(B.foo) //1
console.log(b.foo) // undifined
目前,只有写法1可行,因为 ES6 明确规定,Class
内部只有静态方法
,没有静态属性
。
写法2只是提案。
9.私有方法和私有属性
无
10.new.target
略
二、继承
1.简介
Class
可以通过extends
关键字实现继承
,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
class A{
constructor(username,password){
this.username = username;
this.password = password;
console.log("A constructor")
}
static run(){
console.log("run")
}
}
class B extends A{
constructor(sex,username,password){
console.log("B constructor")
super(username,password);
this.sex = sex;
}
}
let b = new B("男","admin","1234")
console.log(b)
B.run() // 继承父类的静态方法
子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类自己的 this 对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用 super 方法,子类就得不到 this 对象。
- 子类必须调用父类的构造方法
- 父类的构造方法必须在使用this关键字之前调用
- 父类的静态方法,也会被子类继承
2.Object.getPrototypeOf()
Object.getPrototypeOf
方法可以用来从子类上获取父类。
console.log(Object.getPrototypeOf(B) === A) // true
3.super关键字
- super()调用父类构造器
- 作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类