当前位置:K88软件开发文章中心编程语言JavaScriptECMAScript → 文章内容

ECMAScript 6 Class

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-15 15:38:41

着,可以通过实例的__proto__属性为Class添加方法。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__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变Class的原始定义,影响到所有实例。不存在变量提升Class不存在变量提升(hoist),这一点与ES5完全不同。new Foo(); // ReferenceErrorclass Foo {}上面代码中,Foo类使用在前,定义在后,这样会报错,因为ES6不会把类的声明提升到代码头部。这种规定的原因与下文要提到的继承有关,必须保证子类在父类之后定义。{ let Foo = class {}; class Bar extends Foo { }}上面的代码不会报错,因为class继承Foo的时候,Foo已经有定义了。但是,如果存在class的提升,上面代码就会报错,因为class会被提升到代码头部,而let命令是不提升的,所以导致class继承Foo的时候,Foo还没有定义。Class表达式与函数一样,类也可以使用表达式的形式定义。const MyClass = class Me { getClassName() { return Me.name; }};上面代码使用表达式定义了一个类。需要注意的是,这个类的名字是MyClass而不是Me,Me只在Class的内部代码可用,指代当前类。let inst = new MyClass();inst.getClassName() // MeMe.name // ReferenceError: Me is not defined上面代码表示,Me只在Class内部有定义。如果类的内部没用到的话,可以省略Me,也就是可以写成下面的形式。const MyClass = class { /* ... */ };采用Class表达式,可以写出立即执行的Class。let person = new class { constructor(name) { this.name = name; } sayName() { console.log(this.name); }}('张三');person.sayName(); // "张三"上面代码中,person是一个立即执行的类的实例。私有方法私有方法是常见需求,但ES6不提供,只能通过变通方法模拟实现。一种做法是在命名上加以区别。class Widget { // 公有方法 foo (baz) { this._bar(baz); } // 私有方法 _bar(baz) { return this.snaf = baz; } // ...}上面代码中,_bar方法前面的下划线,表示这是一个只限于内部使用的私有方法。但是,这种命名是不保险的,在类的外部,还是可以调用到这个方法。另一种方法就是索性将私有方法移出模块,因为模块内部的所有方法都是对外可见的。class Widget { foo (baz) { bar.call(this, baz); } // ...}function bar(baz) { return this.snaf = baz;}上面代码中,foo是公有方法,内部调用了bar.call(this, baz)。这使得bar实际上成为了当前模块的私有方法。还有一种方法是利用Symbol值的唯一性,将私有方法的名字命名为一个Symbol值。const bar = Symbol('bar');const snaf = Symbol('snaf');export default class myClass{ // 公有方法 foo(baz) { this[bar](baz); } // 私有方法 [bar](baz) { return this[snaf] = baz; } // ...};上面代码中,bar和snaf都是Symbol值,导致第三方无法获取到它们,因此达到了私有方法和私有属性的效果。this的指向类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。class Logger { printName(name = 'there') { this.print(`Hello ${name}`); } print(text) { console.log(text); }}const logger = new Logger();const { printName } = logger;printName(); // TypeError: Cannot read property 'print' of undefined上面代码中,printName方法中的this,默认指向Logger类的实例。但是,如果将这个方法提取出来单独使用,this会指向该方法运行时所在的环境,因为找不到print方法而导致报错。一个比较简单的解决方法是,在构造方法中绑定this,这样就不会找不到print方法了。class Logger { constructor() { this.printName = this.printName.bind(this); } // ...}另一种解决方法是使用箭头函数。class Logger { constructor() { this.printName = (name = 'there') => { this.print(`Hello ${name}`); }; } // ...}还有一种解决方法是使用Proxy,获取方法的时候,自动绑定this。function selfish (target) { const cache = new WeakMap(); const handler = { get (target, key) { const value = Reflect.get(target, key); if (typeof value !== 'function') { return value; } if (!cache.has(value)) { cache.set(value, value.bind(target)); } return cache.get(value); } }; const proxy = new Proxy(target, handler); return proxy;}const logger = selfish(new Logger());严格模式类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以ES6实际上把整个语言升级到了严格模式。name属性由于本质上,ES6的类只是ES5的构造函数的一层包装,所以函数的许多特性都被Class继承,包括name属性。class Point {}Point.name // "Point"name属性总是返回紧跟在class关键字后面的类名。Class的继承基本用法Class之间可以通过extends关键字实现继承,这比ES5的通过修改原型链实现继承,要清晰和方便很多。class ColorPoint extends Point {}上面代码定义了一个ColorPoint类,该类通过extends关键字,继承了Point类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个Point类。下面,我们在ColorPoint内部加上代码。class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 调用父类的constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 调用父类的toString() }}上面代码中,constructor方法和toSt

上一页  [1] [2] [3] [4] [5]  下一页


ECMAScript 6 Class