class的基本语法
-
class是ES6提供的更接近于传统语言的的写法,作为对象的模板.通过class关键字,可以定义类
-
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改写 //注意:定义类的方法的时候,不需要加上function关键字 //方法之间不需要用逗号间隔,否则会报错 class Point(){ constructor(x,y) { this.x=x; this.y=y; } toString (){ return '(' + this.x + ', ' + this.y + ')'; } }
-
类完全可以看作构造函数的另一种写法:
Point === Point.prototype.constructor
-
使用类的时候直接像对象一样new即可
-
构造函数的prototype属性在类上依然存在,实际上,类中所有的方法都定义在类的prototype属性上
class Point { constructor() { // ... } toString() { // ... } toValue() { // ... } } // 等同于 Point.prototype = { constructor() {}, toString() {}, toValue() {}, };
-
在类的实例上调用方法实际上就是调用原型上的方法
class A{} var a=new A(); a.constructor===A.prototype.constructor //true
-
类的内部定义的方法都是不可枚举的(ES6中如此,ES5中可以枚举)
-
类的属性名可以采用表达式
let methodName = 'getArea'; class Square { constructor(length) { // ... } [methodName]() { // ... } }
-
类和模块的内部默认都是严格模式,所以不需要
use strict
指定运行模式 -
constructor方法
-
constructor方法是类的默认方法,通过new命令生成实例对象时,自动调用该方法.
-
一个类必须有constructor方法,如果没有显式定义,会默认添加一个空constructor方法
-
constructor默认返回实例对象(this),完全可以指定返回另外一个对象
//改变返回的对象为空对象,因此通过new Foo()创建的实例不是继承自Foo class Foo { constructor() { return Object.create(null); } } new Foo() instanceof Foo // false
-
类必须使用new调用,否则会报错.普通构造函数不用new也可以执行(虽然没有太大意义),但类不行
-
-
类的实例对象
-
前面提到过的,生成类的实例对象的写法与ES5完全一致,使用new命令即可.但是如果没加new则会报错
-
与ES5一样,实例的属性除非显式定义在this对象上,否则都是定义在原型上(也就是class上)
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } } var point = new Point(2, 3); point.toString() // (2, 3) point.hasOwnProperty('x') // true point.hasOwnProperty('y') // true x,y都是构造函数本身的属性,因此返回true point.hasOwnProperty('toString') // false toString是原型上的属性,因此返回false point.__proto__.hasOwnProperty('toString') // true
-
类的所有实例共享一个原型对象
var p1 = new Point(2,3); var p2 = new Point(3,2); p1.__proto__ === p2.__proto__ //true //p1.__proto__===Point.prototype
-
-
class表达式
-
类可以用表达式的形式定义
//这个类的名称是MyClass.Me是在类的内部使用,指代当前类,如果内部没用到可以省略 const MyClass = class Me { getClassName() { return Me.name; } };
-
采用class表达式可以写出立即执行的class
let person = new class { constructor(name) { this.name = name; } sayName() { console.log(this.name); } }('张三'); person.sayName(); // "张三"
-
-
不存在变量提升:类不存在变量提升,必须先声明后使用.因此以后用到的类的继承必须保证子类在父类之后定义
-
私有方法
-
私有方法ES6不提供,只能模拟实现
-
可以在命名上加上特殊标识区别(可以在外部调用,不安全,不建议)
class Widget { // 公有方法 foo (baz) { this._bar(baz); } // 私有方法 _bar(baz) { return this.snaf = baz; } // ... }
-
将私有方法移出模块,因为模块内部的方法都是对外可见的
//通过 class Widget { foo (baz) { bar.call(this, baz); } // ... } function 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; } // ... };
-
-
私有属性
-
ES6不支持私有属性.有一个方案为class添加了私有属性.就是在属性名之前使用
#
表示.同时,私有属性可以和实例的属性同名(例如 #x和get x(){}) -
同时,也可以用
#
来表示私有方法class Foo { #a; #b; #sum() { return #a + #b; } printSum() { console.log(#sum()); } constructor(a, b) { #a = a; #b = b; } }
-
-
this的指向
-
类的方法内部如果有this,它默认指向类的实例.如果要单独使用类的方法,很可能会报错
//本来this指向logger,但是因为单独获取了printName方法,再使用的时候this就指向了window,不能正确调用 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 undef ined
-
可以在构造方法中绑定this
class Logger { constructor() { this.printName = this.printName.bind(this); } // ... }
-
或者使用箭头函数,因为箭头函数有绑定this 的功能
class Logger { constructor() { this.printName = (name = 'there') => { this.print(`Hello ${name}`); }; } // ... }
-
也可以用Proxy在获取方法的时候自动绑定this,这里不再重写
-
-
name属性:总是返回紧跟class关键字后面的类名
-
class的取值函数(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'
-
存值函数和取值函数是设置在属性的 Descriptor 对象上的
-
-
class的Generator方法
-
如果某个方法之前加上
*
号,就表示该方法是一个Generator函数class Foo { constructor(...args) { this.args = args; } * [Symbol.iterator]() { //[Symbol.iterator]方法返回一个Foo类的默认遍历器,for...of循环会自动调用 for (let arg of this.args) { yield arg; } } } for (let x of new Foo('hello', 'world')) { console.log(x); } // hello // world
-
-
class的静态方法
-
类相当于实例的原型,所有在类中定义的方法,都会被实例继承.如果在一个方法前,加上 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指的的是类,而不是实例
//这里bar的this指的是Foo类,而不是Foo的实例,等同于调用Foo.baz.此外,静态方法和非静态方法可以重名 //调用的是同为静态方法的baz,如果没有静态的baz方法会报错 class Foo { static bar () { this.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"
-
-
class的静态属性和实例属性
-
静态属性是指class本身的属性,即
Class.propName
,而不是定义在实例对象(this)上的属性class Foo { } Foo.prop = 1; //为Foo定义了一个静态属性prop Foo.prop // 1 //目前只有这一种写法,因为ES6规定Class内部只有静态方法,没有静态属性 //以下写法都无效 class Foo { // 写法一 prop: 2 // 写法二 static prop: 2 } Foo.prop // undefined
-
类的实例属性:可以使用等式写入类的定义之中
class MyClass { myProp = 42; //定义实例属性myProp=42 constructor() { console.log(this.myProp); // 42 } }
-
对于在 constructor 里面已经定义的实例属性,新写法允许直接列出
class ReactCounter extends React.Component { state; constructor(props) { super(props); this.state = { count: 0 }; } }
-
-
类的静态属性:
-
只要在实例属性写法前面加上static即可
class MyClass { static myStaticProp = 42; constructor() { console.log(MyClass.myStaticProp); // 42 } }
-
-
-
new.target属性:一般用于构造函数之中,返回new命令作用域哪个构造函数.如果构造函数不是通过new命令调用的,new.target返回undefined.因此这个属性可以用来确定构造函数是怎么调用的
-
Class内部调用new.target,返回的是当前Class
class P{ constructor{ console.log(new.target === P); //true } }
-
子类继承父类时,new.target会返回子类
//不能独立使用、必须继承后才能使用的类.也就是Shape不能实例化,只能用于继承 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); // 正确
-
在函数外部使用new.target会报错
-