先看一个通过传统方法通过构造函数生成实例对象的例子。
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
}
var p = new Point(1, 2);
console.log(p.toString());
ES6 引入了 Class(类)的概念,作为对象的模板。
定义和使用
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var p = new Point(1, 2);
console.log(p.toString());
类的所有方法都定义在类的 prototype 属性上面。
class Point {
constructor() {
// ...
}
toString() {
// ...
}
toValue() {
// ...
}
}
// 等同于
Point.prototype = {
constructor() {},
toString() {},
toValue() {}
}
通过 Object.assign() 方法可以方便的一次向类添加多个方法。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
Object.assign(Point.prototype, {
toValue() {},
debuger() {}
});
类的内部所有定义的方法,都是不可枚举的。
console.log(Object.keys(Point.prototype));
// (2) ['toValue', 'debuger']
console.log(Object.getOwnPropertyNames(Point.prototype));
// (4) ['constructor', 'toString', 'toValue', 'debuger']
一个类必须有一个 constructor 方法,如果没有显式定义,会默认添加一个空的 constructor 方法。
class Point {
// ...
}
// 等同于
class Point {
constructor() {}
}
constructor 方法默认返回实例对象(即 this),也可以指定返回另外一个对象。
class Foo {
constructor() {
return Object.create(null);
}
}
console.log(new Foo() instanceof Foo); // false
类必须使用 new 调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用 new 也可以执行。
class Foo {
constructor() {
// return Object.create(null);
}
}
Foo(); // TypeError: Class constructor Foo cannot be invoked without 'new'
// 下面是普通构造函数
function Animal() {
return 'Animal';
}
console.log(Animal()); // 输出 Animal
使用 new 命令生成类的实例。
与 ES5 一样,实例的属性除非显式定义在其本身(即定义在 this 对象上),否则都是定义在原型上(即定义在 class 上)。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
Object.assign(Point.prototype, {
toValue() {},
debuger() {}
});
var point = new Point(2, 3); // 结果输出:(2, 3)
console.log(point.toString()); // 结果输出:true
console.log(point.hasOwnProperty('x')); // 结果输出:true
console.log(point.hasOwnProperty('y')); // 结果输出:true
console.log(point.hasOwnProperty('toString')); // 结果输出:false
console.log(point.hasOwnProperty('toValue')); // 结果输出:false
console.log(point.__proto__.hasOwnProperty('toString')); // 结果输出:true
console.log(point.__proto__.hasOwnProperty('toValue')); // 结果输出:true
与 ES5 一样,类的所有实例共享一个原型对象。
var point = new Point(2, 3);
var point2 = new Point(100, 500);
console.log(point.__proto__ === point2.__proto__); // 结果输出:true
可以通过实例的 __proto__ 属性为“类”添加方法,但这属于修改原型,会改变“类”的原始定义,并影响所有实例,必须相当谨慎,不推荐使用,仅做理解即可。
var point = new Point(2, 3);
var point2 = new Point(100, 500);
point.__proto__.printName = function () {
console.log('Test...')
}
point.printName(); // 结果输出:Test...
point2.printName(); // 结果输出:Test...
var point3 = new Point(2000, 7000);
point3.printName(); // 结果输出:Test...
与 ES5 一样,在“类” 的内部可以使用 get 和 set 关键字,对某个属性设置存值函数和取值函数,拦截该属性的行为。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
get z() {
return 'getter';
}
set z(value) {
console.log('setter: ' + value);
}
}
let pointZ = new Point(0, 0);
pointZ.z = 1000; // 控制台会输出:setter: 1000
console.log(pointZ.z); // 控制台会输出:getter
存值函数和取值函数是设置在属性的 Descriptor 对象上。
var descriptor = Object.getOwnPropertyDescriptor(Point.prototype, "z");
console.log("get" in descriptor); // true
console.log("set" in descriptor); // true
类的属性名可以使用表达式。
let methodName = 'getMsg';
class Messager {
constructor() {
}
[methodName]() {
console.log('您调用了 getMsg 方法....');
}
}
(new Messager()).getMsg();
(new Messager())[methodName]();
与函数一样,类也可以使用表达式的形式定义。
const MyClass = class Mc {
getClassName() {
return Mc.name;
}
getClassName2() {
return this.constructor.name;
}
}
new Mc(); // Uncaught ReferenceError: Mc is not defined
let mc = new MyClass();
console.log(mc.getClassName()); // 结果输出:Mc
console.log(mc.getClassName2()); // 结果输出:Mc
需要注意的是,上面的代码中这个类的名字是 Mc,但是 Mc 只能在 Class 内部可用,只带当前类。
在 Class 外部,这个类只能使用 MyClass 引用。
如果内部没有用到的话,可以省略 Mc,可以简写为:const MyClass = class { /_ ... _/}
采用 Class 表达式,可以写出立即执行的 Class。
let person = new class {
constructor(name) {
this.name = name;
}
sayHello() {
console.log('hello, ', this.name);
}
}('张三疯');
person.sayHello();
考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。
不存在提升
类不存在变量提升,这一点与 ES5 完全不同。
new Foo(); // Uncaught ReferenceError: Cannot access 'Point' before initialization
class Foo {}
🔥BuildAdmin是一个永久免费开源,无需授权即可商业使用,且使用了流行技术栈快速创建商业级后台管理系统。