es5 构造函数 实例:
1 function Point(x, y) { 2 this.x = x; 3 this.y = y; 4 } 5 6 Point.prototype.toString = function () { 7 return '(' + this.x + ', ' + this.y + ')'; 8 }; 9 10 var p = new Point(1, 2);
作为对象的模板,通过class关键字,可以定义类,相当于创建一个 实例
1 //定义类 2 class Point { 3 constructor(x, y) { 4 this.x = x; 5 this.y = y; 6 } 7 8 toString() { 9 return '(' + this.x + ', ' + this.y + ')'; 10 } 11 } 12 typeof Point // "function" 13 Point === Point.prototype.constructor // true 14 //ES6 的类,完全可以看作构造函数的另一种写法。
可以看到里面有一个constructor方法,这就是构造方法,this则代表实例对象
ES5 的构造函数Point,对应 ES6 的Point类的构造方法。
定义“类”的方法的时候,前面不需要加上function这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。
类的数据类型就是函数,类本身就指向构造函数。 直接对类使用new命令,跟构造函数的用法完全一致。
构造函数的prototype属性,在 ES6的“类”上面继续存在。 事实上,类的所有方法都定义在类的prototype属性上面。
class Point { constructor() { // ... } toString() { // ... } toValue() { // ... } } // 等同于 Point.prototype = { constructor() {}, toString() {}, toValue() {}, };
Object.assign方法可以很方便地一次向类添加多个方法:
1 Object.assign(Point.prototype, { 2 toString(){}, 3 toValue(){} 4 });
5// 类的内部所有定义的方法,都是不可枚举的 6 7 Object.keys(Point.prototype) 8 // [] es5 : ["toString"] 9 Object.getOwnPropertyNames(Point.prototype) 10 // ["constructor","toString"]
类的属性名,可以采用表达式。
let methodName = 'getArea'; class Square { constructor(length) { // ... } [methodName]() { // ... } }
类和模块的内部,默认就是严格模式 只要你的代码写在类或模块之中,就只有严格模式可用。 ES6 实际上把整个语言升级到了严格模式。
constructor方法
constructor方法是类的默认方法, 通过new命令生成对象实例时,自动调用该方法。
一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。 相当于es5 new Vue
1 class Point { 2 } 3 4 // 等同于 5 class Point { 6 constructor() {} //JavaScript 引擎会自动为它添加一个空的constructor方法。 7 } 8 // 等同于 9 new Point (){ 10 11 } 12 constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。 13 14 class Foo { 15 constructor() { 16 return Object.create(null); 17 } 18 } 19 20 new Foo() instanceof Foo 21 // false
类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行, 与 ES5 一样,类的所有实例共享一个原型对象。
私有属性
表达式定义一个类
1 const MyClass = class Me { 2 getClassName() { 3 return Me.name; 4 //这个类的名字是MyClass而不是Me,Me只在 Class 的内部代码可用,指代当前类。 5 } 6 }; 7 //内部没用到Me的话可以省略Me 8 const YouClass = class { 9 //... 10 }; 11 采用 Class 表达式,可以写出立即执行的 Class。 12 13 let person = new class { 14 constructor(name) { 15 this.name = name; 16 } 17 18 sayName() { 19 console.log(this.name); 20 } 21 }('张三'); 22 23 person.sayName(); // "张三"
不存在变量提升(hoist),必须保证子类在父类之后定义),这一点与 ES5 完全不同。
new Foo(); // ReferenceError
class Foo {}
继承类
{
let Foo = class {};
class Bar extends Foo { //Bar继承Foo } }
私有方法
私有方法是常见需求,但 ES6 不提供,只能通过变通方法模拟实现。
有三种方法可模拟
//第一种 class Widget { // 公有方法 foo (baz) { this._bar(baz); } // 私有方法 _bar(baz) { return this.snaf = baz; } // _bar方法前面的下划线,表示这是一个只限于内部使用的私有方法 但是,在类的外部,还是可以调用到这个方法。 } //第二种 class Widget { foo (baz) { bar.call(this, baz); } // 内部调用了bar.call(this, baz),成为了当前模块的私有方法 } //私有方法移出模块 function bar(baz) { return this.snaf = baz; } //第三种 const bar = Symbol('bar'); const snaf = Symbol('snaf'); export default class myClass{ // 公有方法 foo(baz) { this[bar](baz); } // 私有方法 [bar](baz) { return this[snaf] = baz; } // 利用Symbol值的唯一性,将私有方法的名字命名为一个Symbol值。 };
私有属性的提案
方法是在属性名之前,使用#表示。
class Point { #x=0;// 私有属性可以指定初始值,在构造函数执行时进行初始化。 constructor(x = 0) { #x = +x; // 写成 this.#x 亦可 } get #x() { return #x } set #x(value) { #x = +value } #sum() { return #a + #b; } //私有方法 // #x是一个私有属性,它的读写都通过get #x()和set #x()来完成。 #x和x是两个不同的属性 } //JavaScript 是一门动态语言,没有类型声明,使用独立的符号似乎是唯一的比较方便可靠的方法,能够准确地区分一种属性是否为私有属性。"@" 已经留给了 Decorator。
私有属性不限于从this引用,类的实例也可以引用私有属性
class Foo { #privateValue = 42; static getPrivateValue(foo) { return foo.#privateValue; } } Foo.getPrivateValue(new Foo()); // 42 console.log(Foo.#privateValue) // 报错
//class 的取值函数(getter)和存值函数(setter) class MyClass { constructor() { // ... } get prop() { return 'getter'; } set prop(value) { console.log('setter: '+value); } } let inst = new MyClass(); inst.prop = 123; // setter: 123 inst.prop // 'getter'