ECMAScript 6(24)class基本语法

1. class简介

1.1 class

  • ES6 的class可以看作只是一个语法糖,它的实现,依然是通过构造函数和原型链来实现的.
  • 传统构造函数
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 + ')';
  }
}
var p = new Point()
  • 在类的实例上面调用方法,其实就是调用原型上的方法。

1.2 注意细节

  1. class的关键字是全部小写
  2. class在定义的时候,每个属性都需要是函数,而不能是变量
class Foo {
    count:0     // error!报错!
}
class Foo {
    count:function(){}     // error!报错!
}
  1. 构造函数里可以创建变量和执行本身方法
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.toString()
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}
  1. this指向class本身,在new出来之后,是实例本身;
  2. class声明的类,不能直接调用,只能通过new关键字来生成实例,如new Foo();直接Foo()会报错,Foo指的是类本身.这是它跟普通构造函数的一个主要区别.
  3. 类方法之间不能用逗号
class Foo {
    count(){
      console.log(1)
    } // 这里加逗号会报错
    add(){
      console.log(2)
    }
}
  1. 类有prototype属性,而constructor挂在在prototype属性下,并且类的constructor属性指向类本身;
var f =new Foo()
f.constructor === FOO  // true
Foo.prototype.constructor === Foo  // true
f.constructor === Foo.prototype.constructor // true

1.3 通过Object.assign添加方法

class Point {
  constructor(){
    // ...
  }
}

Object.assign(Point.prototype, {
  toString(){},
  toValue(){}
});

1.4 不可枚举

  • 直接定义在类的内部所有方法,都是不可枚举的
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"]
  • 通过prototype新增的方法可枚举
class Point {
  constructor(){
    // ...
  }
}

Object.assign(Point.prototype, {
  toString(){},
  toValue(){}
});
Object.keys(Point.prototype)
// ["toString", "toValue"]

1.5 constructor

  • 通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
class Point {
}

// 等同于
class Point {
  constructor() {}
}
  • constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象。
class Foo {
  constructor() {
    return Object.create(null);
  }
}

new Foo() instanceof Foo
// false

1.6 实例

  • 和es5一样, 使用new来定义
  • 与 ES5 一样,类的所有实例共享一个原型对象。
var p1 = new Point(2,3);
var p2 = new Point(3,2);

p1.__proto__ === p2.__proto__   // true
p1.__proto__ === Point.prototype  // true
  • 实例的__proto__属性改写原型,必须相当谨慎,不推荐使用,因为这会改变“类”的原始定义,影响到所有实例。

1.7 取值函数(getter)和存值函数(setter)

  • “类”的内部可以使用get和set关键字
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'

1.8 注意点

  1. 类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式
  2. 不存在提升
  3. Generator 方法
  • 如果某个方法之前加上星号(*),就表示该方法是一个 Generator 函数。
class Foo {
  constructor(...args) {
    this.args = args;
  }
  * [Symbol.iterator]() {
    for (let arg of this.args) {
      yield arg;
    }
  }
}

for (let x of new Foo('hello', 'world')) {
  console.log(x);
}
  1. 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会指向该方法运行时所在的环境(由于 class 内部是严格模式,所以 this 实际指向的是undefined),从而导致找不到print方法而报错。
  • 解决方法
    1. bind
class Logger {
  constructor() {
    this.printName = this.printName.bind(this);
  }

  // ...
}
    1. 箭头函数
class Obj {
  constructor() {
    this.getThis = () => this;
  }
}

const myObj = new Obj();
myObj.getThis() === myObj // true
  • 箭头函数内部的this总是指向定义时所在的对象。

2. 静态方法

  • 如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
  • 静态方式没有绑定在prototype上而是就是直接绑定类上, 因为prototype上会被实例继承. 可以通过cosole.dir(Foo)来查看静态方法
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 bar() {
    this.baz(); // 如果没有static baz()方法, 会报错
  }
  static baz() {
    console.log('hello');
  }
  baz() {
    console.log('world');
  }
}

Foo.bar() // hello
  • 父类的静态方法,可以被子类继承。
class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod() // 'hello'
  • 静态方法也是可以从super对象上调用的。
class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

Bar.classMethod() // "hello, too"

3. 实例属性的新写法

  • 实例属性除了定义在constructor()方法里面的this上面,也可以定义在类的最顶层。
class foo {
  bar = 'hello';
  baz = 'world';

  constructor() {
    // ...
  }
}
  • 静态属性
class Foo {
}

Foo.prop = 1;
Foo.prop // 1

4. 私有方法和私有属性

  • es6暂时还不提供, 只能通过变通方法模拟实现。
  • 在命名上加以区别
class Widget {

  // 公有方法
  foo (baz) {
    this._bar(baz);
  }

  // 私有方法
  _bar(baz) {
    return this.snaf = baz;
  }

  // ...
}
  • 另一种方法是利用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;
  }

  // ...
};

5. new.target 属性

  • 简单说, 这个属性用于表示当函数通过new来调用时(即函数被当做构造函数),new命令作用的那个构造函数是谁。
  • 如果构造函数不是通过new命令或Reflect.construct()调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的。
function Person(name) {
  if (new.target !== undefined) {
    this.name = name;
  } else {
    throw new Error('必须使用 new 命令生成实例');
  }
}

// 另一种写法
function Person(name) {
  if (new.target === Person) {
    this.name = name;
  } else {
    throw new Error('必须使用 new 命令生成实例');
  }
}

var person = new Person('张三'); // 正确
var notAPerson = Person.call(person, '张三');  // 报错
  • 它有以下几个特点
  1. 只能在函数内部使用,函数外部直接调用会报错;
  2. 当new.target处于类的constructor属性中时, 指向类本身
class Foo {
  constructor() {
    console.log(new.target === Foo); // true
  }
  my(){
    console.log(new.target === Foo); // false  这里是undefined
  }
}
var f = new Foo() // true
f.my() // undefined
  1. 当父类被子类继承时,父类中的constructor的new.target指向子类(而非父类)。
  • 参考文献 阮一峰es6

你可能感兴趣的:(ES6系列)