这是我参与「第五届青训营 」伴学笔记创作活动的第 21 天
学习笔记
ES6的Class
Class的两种定义形式
声明形式
类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。如果忘记加上new,像函数那样调用Class(),将会报错。
class Person {
constructor() {
...
}
speak() {
...
}
}
表达式形式
与函数一样,类也可以使用表达式的形式定义。
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
//上面代码使用表达式定义了一个类。需要注意的是,这个类的名字是`Me`,
//但是`Me`只在 Class 的内部可用,指代当前类。在 Class 外部,这个类只能用`MyClass`引用。
//如果类的内部没用到的话,可以省略`Me`,也就是可以写成下面的形式。
const MyClass = class { /* ... */ };
立即执行的类:
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('张三');
person.sayName(); // "张三"
类的属性用表达式显示:
let methodName = 'getArea';
class Square {
constructor(length) {
// ...
}
[methodName]() {
// ...
}
}
上面代码中,`Square`类的方法名`getArea`,是从表达式得到的。
constructor() 方法
constructor()方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor()方法,如果没有显式定义,一个空的constructor()方法会被默认添加。constructor()方法默认返回实例对象(即this),完全可以指定返回另外一个对象。
类的实例
类的属性和方法,除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var point = new Point(2, 3);
point.toString() // (2, 3)
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true
上面代码中,x和y都是实例对象point自身的属性(因为定义在this对象上),所以hasOwnProperty()方法返回true,而toString()是原型对象的属性(因为定义在Point类上),所以hasOwnProperty()方法返回false。这些都与 ES5 的行为保持一致。
与 ES5 一样,类的所有实例共享一个原型对象。
var p1 = new Point(2,3);
var p2 = new Point(3,2);
p1.__proto__ === p2.__proto__
//true
上面代码中,p1和p2都是Point的实例,它们的原型都是Point.prototype,所以__proto__属性是相等的。
这也意味着,可以通过实例的__proto__属性为“类”添加方法。
__proto__并不是语言本身的特性,这是各大厂商具体实现时添加的私有属性,虽然目前很多现代浏览器的 JS 引擎中都提供了这个私有属性,但依旧不建议在生产中使用该属性,避免对环境产生依赖。生产环境中,我们可以使用Object.getPrototypeOf()方法来获取实例对象的原型,然后再来为原型添加方法/属性。
var p1 = new Point(2,3);
var p2 = new Point(3,2);
p1.__proto__.printName = function () { return 'Oops' };
p1.printName() // "Oops"
p2.printName() // "Oops"
var p3 = new Point(4,2);
p3.printName() // "Oops"
上面代码在p1的原型上添加了一个printName()方法,由于p1的原型就是p2的原型,因此p2也可以调用这个方法。而且,此后新建的实例p3也可以调用这个方法。这意味着,使用实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变“类”的原始定义,影响到所有实例。