ES6
经常使用但被忽略的方法 系列文章,整理做者认为一些平常开发可能会用到的一些方法、使用技巧和一些应用场景,细节深刻请查看相关内容链接,欢迎补充交流。// 传统写法
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);
// class 写法
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
复制代码
function
这个关键字,直接把函数定义放进去了就能够了。方法之间不须要逗号分隔,加了会报错。 类的全部方法都定义在类的prototype
属性上面。class Point {
constructor() {...}
toString() {...}
toValue() {...}
}
// 等同于
Point.prototype = {
constructor() {},
toString() {},
toValue() {},
};
typeof Point // "function"
Point === Point.prototype.constructor // true
复制代码
// es6
class Point {
constructor(x, y) {...}
toString() {...}
}
Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
// es5
var Point = function (x, y) {...};
Point.prototype.toString = function() {...};
Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
复制代码
constructor
方法是类的默认方法,经过new
命令生成对象实例时,自动调用该方法。一个类必须有constructor
方法,若是没有显式定义,一个空的constructor
方法会被默认添加。constructor
方法默认返回实例对象(即this
),彻底能够指定返回另一个对象。class Foo {
constructor() {
return Object.create(Object);
}
}
new Foo() instanceof Foo // false
new Foo() instanceof Object // true
复制代码
ES5
同样,在“类”的内部可使用get
和set
关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。存值函数和取值函数是设置在属性的 Descriptor
对象上的。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'
const descriptor = Object.getOwnPropertyDescriptor(
MyClass.prototype, "prop"
);
"get" in descriptor // true
"set" in descriptor // true
复制代码
let methodName = 'getArea';
class Square {
constructor(length) {...}
[methodName]() {...}
}
复制代码
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
复制代码
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('detanx');
person.sayName(); // "detanx"
复制代码
严格模式git
use strict
指定运行模式。不存在提高es6
new Foo(); // ReferenceError
class Foo {}
复制代码
name
属性github
ES6
的类只是ES5
的构造函数的一层包装,因此函数的许多特性都被Class
继承,包括name
属性。class Point {}
Point.name // "Point"
复制代码
name
属性老是返回紧跟在class
关键字后面的类名。Generator
方法数组
*
),就表示该方法是一个 Generator
函数。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
复制代码
static
关键字,就表示该方法不会被实例继承,而是直接经过类来调用,这就称为“静态方法”。class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
复制代码
this
关键字,这个this
指的是类,而不是实例。class Foo {
static classMethod() {
return 'detanx';
}
}
class Bar extends Foo {
}
Bar.classMethod() // 'detanx'
复制代码
super
对象上调用的。class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
static classMethod() {
return super.classMethod() + ', detanx';
}
}
Bar.classMethod() // "hello, detanx"
复制代码
constructor()
方法里面的this上面,也能够定义在类的最顶层。class IncreasingCounter {
_count = 0
=> // 或写在constructor
// constructor() {
// this._count = 0;
// }
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}
复制代码
Class
自己的属性,即Class.propName
,而不是定义在实例对象(this
)上的属性。class Foo { }
Foo.prop = 1;
Foo.prop // 1
复制代码
stage 3
) 提供了类的静态属性,写法是在实例属性的前面,加上static
关键字。// 新写法
class Foo {
static prop = 1;
}
复制代码
ES6
不提供,只能经过变通方法模拟实现。_
开头或者 $
开头的为私有。class Widget {
foo (baz) {
bar.call(this, baz);
}
}
function bar(baz) {
return this.snaf = baz;
}
复制代码
Symbol
值的惟一性,将私有方法的名字命名为一个Symbol
值。ES6经常使用但被忽略的方法(第三弹Symbol、Set 和 Map )之Symbol的应用const bar = Symbol('bar');
const snaf = Symbol('snaf');
export default class myClass{
// 公有方法
foo(baz) {
this[bar](baz);
}
// 私有方法
[bar](baz) {
return this[snaf] = baz;
}
};
复制代码
Reflect.ownKeys()
依然能够拿到它们。const inst = new myClass();
Reflect.ownKeys(myClass.prototype)
// [ 'constructor', 'foo', Symbol(bar) ]
复制代码
Stage 3
),为class
加了私有属性和方法。方法是在属性名和方法以前,使用 #
表示。私有属性也能够设置 getter
和 setter
方法。class Counter {
#xValue = 0;
constructor() {
super();
// ...
}
get #x() { return #xValue; }
set #x(value) {
this.#xValue = value;
}
}
复制代码
static
关键字,表示这是一个静态的私有属性或私有方法。new.target
属性new
是从构造函数生成实例对象的命令。ES6
为new
命令引入了一个new.target
属性,该属性通常用在构造函数之中,返回new
命令做用于的那个构造函数。若是构造函数不是经过new
命令或Reflect.construct()
调用的 ,new.target
会返回undefined
,所以这个属性能够用来肯定构造函数是怎么调用的。Class
内部调用new.target
,返回当前 Class
。class Rectangle {
constructor(length, width) {
console.log(new.target === Rectangle);
this.length = length;
this.width = width;
}
}
var obj = new Rectangle(3, 4); // 输出 true
复制代码
new.target
会返回子类。 利用这个特色,能够写出不能独立使用、必须继承后才能使用的类。class Shape {
constructor() {
if (new.target === Shape) {
throw new Error('本类不能实例化');
}
}
}
class Rectangle extends Shape {
constructor(length, width) {
super();
}
}
var x = new Shape(); // 报错
var y = new Rectangle(3, 4); // 正确
复制代码
super
方法。class Point { /* ... */ }
class ColorPoint extends Point {
constructor() {
}
}
let cp = new ColorPoint(); // ReferenceError
复制代码
ColorPoint
继承了父类Point
,可是它的构造函数没有调用super
方法,致使新建实例时报错。super
以后,才可使用this
关键字,不然会报错。class Point {
constructor(x) {
this.x = x;
}
}
class ColorPoint extends Point {
constructor(x, color) {
this.color = color; // ReferenceError
super(x);
this.color = color; // 正确
}
}
复制代码
Object.getPrototypeOf
方法能够用来从子类上获取父类。可使用这个方法判断,一个类是否继承了另外一个类。Object.getPrototypeOf(ColorPoint) === Point // true
复制代码
super
这个关键字,既能够看成函数使用,也能够看成对象使用。在这两种状况下,它的用法彻底不一样。super
做为函数调用时,表明父类的构造函数。ES6
要求,子类的构造函数必须执行一次super
函数。不然 JavaScript
引擎会报错。class A {
constructor() {
console.log(new.target.name);
}
}
class B extends A {
constructor() {
super();
}
}
new A() // A
new B() // B
复制代码
super
虽然表明了父类A
的构造函数,可是返回的是子类B
的实例,super()
至关于A.prototype.constructor.call(this)
,super()
内部的this
指向的是B
。super()
只能用在子类的构造函数之中,用在其余地方就会报错。class A {}
class B extends A {
m() {
super(); // 报错
}
}
复制代码
super
做为对象时
static
前缀的方法)中,指向父类。class A {
p() {
return 2;
}
}
class B extends A {
constructor() {
super();
console.log(super.p()); // 2
}
}
let b = new B();
复制代码
super
指向父类的原型对象,因此定义在父类实例上的方法或属性,是没法经过super
调用的。若是属性定义在父类的原型对象上,super
就能够取到。class A {
constructor() {
this.p = 2;
}
}
class B extends A {
get m() {
return super.p;
}
}
let b = new B();
b.m // undefined
// 定义到原型上
class A {}
A.prototype.x = 2;
class B extends A {
constructor() {
super();
console.log(super.x) // 2
}
}
let b = new B();
复制代码
super
调用父类的方法时,方法内部的this
指向当前的子类,而不是子类的实例。class A {
constructor() {
this.x = 1;
}
static print() {
console.log(this.x);
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
static m() {
super.print();
}
}
B.x = 3;
B.m() // 3
复制代码
super
的时候,必须显式指定是做为函数、仍是做为对象使用,不然会报错。class A {}
class B extends A {
constructor() {
super();
console.log(super); // 报错
}
}
复制代码
super
关键字。var obj = {
toString() {
return "MyObject: " + super.toString();
}
};
obj.toString(); // MyObject: [object Object]
复制代码
prototype
属性和__proto__
属性ES5
实现之中,每个对象都有__proto__
属性,指向对应的构造函数的prototype
属性。存在两条继承链。
__proto__
属性,表示构造函数的继承,老是指向父类。prototype
属性的__proto__
属性,表示方法的继承,老是指向父类的prototype
属性。class A {}
class B extends A {}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
复制代码
class A {}
class B {}
// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);
// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);
const b = new B();
复制代码
Object.setPrototypeOf
方法的实现。Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
return obj;
}
复制代码
__proto__
属性的__proto__
属性,指向父类实例的__proto__
属性。子类的原型的原型,是父类的原型。var p1 = new Point(2, 3);
var p2 = new ColorPoint(2, 3, 'red');
p2.__proto__ === p1.__proto__ // false
p2.__proto__.__proto__ === p1.__proto__ // true
复制代码
ECMAScript
的原生构造函数大体有:Boolean()
、Number()
、String()
、Array()
、Date()
、Function()
、RegExp()
、Error()
、Object()
...ES6
能够自定义原生数据结构(好比Array
、String
等)的子类,这是 ES5
没法作到的。例如实现一个本身的带其余功能的数组类。class VersionedArray extends Array {
constructor() {
super();
this.history = [[]];
}
commit() {
this.history.push(this.slice());
}
revert() {
this.splice(0, this.length, ...this.history[this.history.length - 1]);
}
}
var x = new VersionedArray();
x.push(1);
x.push(2);
x // [1, 2]
x.history // [[]]
x.commit();
x.history // [[], [1, 2]]
x.push(3);
x // [1, 2, 3]
x.history // [[], [1, 2]]
x.revert();
x // [1, 2]
复制代码
Object
的子类,有一个行为差别。class NewObj extends Object{
constructor(){
super(...arguments);
}
}
var o = new NewObj({attr: true});
o.attr === true // false
复制代码
NewObj
继承了Object
,可是没法经过super
方法向父类Object
传参。这是由于 ES6
改变了Object
构造函数的行为,一旦发现Object
方法不是经过new Object()
这种形式调用,ES6
规定Object
构造函数会忽略参数。Mixin
模式的实现Mixin
指的是多个对象合成一个新的对象,新对象具备各个组成成员的接口。使用的时候,只要继承这个类便可。function mix(...mixins) {
class Mix {
constructor() {
for (let mixin of mixins) {
copyProperties(this, new mixin()); // 拷贝实例属性
}
}
}
for (let mixin of mixins) {
copyProperties(Mix, mixin); // 拷贝静态属性
copyProperties(Mix.prototype, mixin.prototype); // 拷贝原型属性
}
return Mix;
}
function copyProperties(target, source) {
for (let key of Reflect.ownKeys(source)) {
if ( key !== 'constructor'
&& key !== 'prototype'
&& key !== 'name'
) {
let desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
}
// 使用
class DistributedEdit extends mix(Loggable, Serializable) {
// ...
}
复制代码