ES6新特性 class(类定义和使用)
intro
在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 definedvar 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
声明,或写在外部也可以(两种都行)。
NOTE - 构造方法中不得访问静态成员。
- 静态成员之间不得互相调用(静态方法内访问静态属性,静态方法互相调用)
- 构造方法
- 向对象添加成员(成员方法,成员属性)
向类名
添加成员,就相当于静态成员。
向类名.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 #<Object> is not a constructor or null
var Father = {name:”JT”};
class Child extends Father {};操作原型链
var Father = {name:”JT”};
class Child {};
Object.setPrototypeOf(Child.prototype, Father);(new Child()).name; // “JT”
还没有评论,来说两句吧...