传统的面向对象语言(比如 C++ 和 Java)是通过class类来生成实例对象,javascript则是通过构造函数+prototype来封装,所以,js在es6引入了class类的概念
用class定义一个类
//定义类
class Point {
constructor(x, y) {
// this关键字则代表实例对象
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
关于constructor
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
执行这个constructor方法默认返回实例对象(即this)
使用时
var point = new Point();
注意: 类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。
1、关于class的prototype属性
类的所有方法都定义在类的prototype属性上面。
class Point {
constructor() {
// ...
}
toString() {
// ...
}
}
// 等同于
Point.prototype = {
constructor() {},
toString() {},
};
在类的实例上面调用方法,其实就是调用原型上的方法。
point.constructor === Point.prototype.constructor // true
point是Point类的实例,它的constructor方法就是Point类原型的constructor方法。
Point.prototype.constructor指向的是Point这个类 而point.constructor的原型对象也是Point
此外,toString方法是Point类内部定义的方法,它是不可枚举的。这一点与 ES5 的行为不一致。
Object.keys(Point.prototype)
// 采用es6的class写法是 [], 用es5的 Point.prototype.toString = function() {
// 即返回["toString"]
};
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
2、类的实例对象
与 ES5 一样,实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。
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 的行为保持一致。
类的所有实例共享一个原型对象。
var p1 = new Point(2,3);
var p2 = new Point(3,2);
p1.__proto__ === p2.__proto__
//true
p1和p2都是Point的实例,它们的原型都是Point.prototype,所以proto属性是相等的。
3、类的私有方法和私有属性
ES6 不提供私有属性和私有方法,我们只能通过变通方法模拟实现。
一种是在命名上加以区别。私有方法用_foo(),即在方法前面添加下划线,表示这是一个只限于内部使用的私有方法。但是,这种命名是不保险的,在类的外部,还是可以调用到这个方法。
一种是利用Symbol值的唯一性,将私有方法的名字命名为一个Symbol值。
还有一种是索性将私有方法移出模块,因为模块内部的所有方法都是对外可见的。
class Widget {
foo (baz) {
bar.call(this, baz);
}
// ...
}
function bar(baz) {
return this.snaf = baz;
}
上面代码中,foo是公有方法,内部调用了bar.call(this, baz)。这使得bar实际上成为了当前模块的私有方法。
4、Class 的静态方法
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
Foo类的classMethod方法前有static关键字,表明该方法是一个静态方法,可以直接在Foo类上调用(Foo.classMethod()),而不是在Foo类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。
注意 如果静态方法包含this关键字,这个this指的是类,而不是实例