回顾 定义类的方式
类声明与函数声明不同之处在于:
函数声明有声明提前,类声明没有
函数受函数作用域限制,而类受块作用域限制
1 2 3 4 5 6 7 { function fn ( ) {} class ClassFn {} } console .log (fn); console .log (ClassFn );
类表达式的名称是可选的
在把类表达式赋值给变量后,可以通过name 属性取得类表达式的名称字符串
但不能在类表达式作用域外部访问这个标识符
1 2 3 4 5 6 7 8 9 10 11 12 let Person = class PersonName { identify ( ) { console .log (Person .name , PersonName .name ); } } let p = new Person ();p.identify (); console .log (Person .name ); console .log (PersonName );
类的构成
类可以包含构造函数方法 、实例方法 、获取函数 、设置函数 、静态类方法 ,但都不是必须的,空的类定义照样有效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Foo {}class Bar { constructor ( ) {} } class Baz { get myBaz () {} } class Qux { static myQux ( ) {} }
类构造函数
constructor
关键词用于在类定义块内部创建类的构造函数(constructor
会告诉解释器,在new
操作符的时候应该调用这个函数创建新的实例)
构造函数的定义不是必需的,不定义构造函数相当于将构造函数定义为空函数
实例化
使用new 操作符实例化Person 的操作等于使用new 调用其构造函数
补课:new操作符会发生什么?包含手写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Animal {}class Person { constructor ( ) { console .log ('person ctor' ); } } class Vegetable { constructor ( ) { this .color = 'orange' ; } } let a = new Animal ();let p = new Person (); let v = new Vegetable ();console .log (v.color );
类实例化时传入的参数会用作构造函数的参数(如果不需要参数,则类名后面的括号也是可选的)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Person { constructor (name ) { console .log (arguments .length ); this .name = name || null ; } } let p1 = new Person ; console .log (p1.name ); let p2 = new Person (); console .log (p2.name ); let p3 = new Person ('Jake' ); console .log (p3.name );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Person { constructor (override ) { this .foo = 'foo' ; if (override) { return { bar : 'bar' }; } } } let p1 = new Person (),p2 = new Person (true ); console .log (p1); console .log (p1 instanceof Person ); console .log (p2); console .log (p2 instanceof Person );
1 2 3 4 5 6 7 function Person ( ) {}class Animal {}let p = Person ();let a = Animal ();
1 2 3 4 5 6 7 8 9 10 class Person {}let p1 = new Person ();p1.constructor ( ); let p2 = new p1.constructor ( );
类是特殊的函数
1 2 3 class Person {}typeof Person
类有prototype属性,原型有constructor属性指向类自身
1 2 3 4 class Person {}console .log (Person .prototype ); console .log (Person .prototype .constructor === Person )
instaceof操作符可以用于检查构造函数的原型是否存在于实例的原型链中
1 2 3 4 5 class Person {}let p = new Person ();console .log (p instanceof Person );
类本身在使用new操作符时会被当成构造函数,但是类的constructor方法不会被当作构造函数,因此instanceof操作符会返回false,但是创建实例时直接将类构造函数当作普通构造函数来使用,instanceof操作符返回true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Person {}let p1 = new Person () console .log (p1.constructor === Person ) console .log (p1 instanceof Person ) console .log (p1 instanceof Person .constructor ); let p2 = new Person .constructor ( ); console .log (p2.constructor === Person ) console .log (p2 instanceof Person ) console .log (p2 instanceof Person .constructor );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let classList = [ class { constructor (id ) { this .id_ = id; console .log (`instance ${this .id_} ` ); } } ]; function createInstance (classDefinition, id ) { return new classDefinition (id); } let foo = createInstance (classList[0 ], 3141 );
1 2 3 4 5 6 7 let p = new class Person { constructor (name ) { console .log (name); } } ('Katrina' ); console .log (p);
实例、原型和类成员 实例成员
每个实例都对应着一个唯一 的成员对象,这意味着所有成员都不会在原型上共享
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Person { constructor ( ) { this .name = 'Katrina' ; this .age = 18 ; this .sayName = function ( ) { console .log (this .name ); }; this .friends = ['Kate' , 'Lisa' ]; } } let p1 = new Person ();let p2 = new Person ();p1.sayName (); p2.sayName (); console .log (p1.name === p2.name ); console .log (p1.sayName === p2.sayName ); console .log (p1.friends === p2.friends ); p1.name = p1.friends [0 ]; p2.name = p2.friends [1 ]; p1.sayName (); p2.sayName ();
原型方法和访问器
为了在实例间共享方法,类定义语法把在类块中定义的方法 作为原型方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Person { constructor ( ) { this .name = 'Katrina' ; this .age = 18 ; this .sayName = function ( ) { console .log (this .name ); }; this .friends = ['Kate' , 'Lisa' ]; } sayHello ( ) { console .log ('Hello' ); }; } Person .prototype .sayHello (); let p1 = new Person ();let p2 = new Person ();console .log (p1.sayHello === p2.sayHello );
可以把方法定义在类构造函数中或者类块中,但不能在类块中给原型添加原始值或对象作为成员数据
1 2 3 4 5 class Person { name : 'Kate' ; }
类方法等同于对象属性,因此可以使用字符串、符号或计算的值作为键
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const symbolKey = Symbol ('symbolKey' );class Person { stringKey ( ) { console .log ('invoked stringKey' ); } [symbolKey]() { console .log ('invoked symbolKey' ); } ['computed' + 'Key' ]() { console .log ('invoked computedKey' ); } } let p = new Person ();p.stringKey (); p[symbolKey](); p.computedKey ();
类定义也支持获取和设置访问器。语法与行为跟普通对象一样
1 2 3 4 5 6 7 8 9 10 11 12 class Person { set name (newName ) { this .name_ = newName; } get name () { return this .name_ ; } } let p = new Person ();p.name = 'Katrina' ; console .log (p.name );
静态类方法
可以在类上定义静态方法
这些方法通常用于执行不特定于实例的操作,也不要求存在类的实例
与原型成员类似,静态成员每个类上只能有一个
静态类成员在类定义中使用static
关键字作为前缀
在静态成员中,this
引用类自身,而构造函数的this
是指向实例的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Person { constructor ( ) { this .locate = () => console .log ('instance' , this ); } locate ( ) { console .log ('prototype' , this ); } static locate ( ) { console .log ('class' , this ); } } let p = new Person ();p.locate (); Person .protype .locate (); Person .locate ();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Person { constructor (age ) { this .age_ = age; } sayAge ( ) { console .log (this .age_ ); } static create ( ) { return new Person (Math .floor (Math .random ()*100 )); } } console .log (Person .create ());
非函数原型和类成员
虽然类定义并不显式支持在原型或类上添加成员数据,但在类定义外部,可以手动添加
1 2 3 4 5 6 7 8 9 10 11 12 13 class Person { sayName ( ) { console .log (`${Person.greeting} ${this .name} ` ); } } Person .greeting = 'My name is' ;Person .prototype .name = 'Jake' ;let p = new Person ();p.sayName ();
迭代器与生成器方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Person { *createNicknameIterator ( ) { yield 'Jack' ; yield 'Jake' ; yield 'J-Dog' ; } static *createJobIterator ( ) { yield 'Butcher' ; yield 'Baker' ; yield 'Candlestick maker' ; } } let jobIter = Person .createJobIterator ();console .log (jobIter.next ().value ); console .log (jobIter.next ().value ); console .log (jobIter.next ().value ); let p = new Person ();let nicknameIter = p.createNicknameIterator ();console .log (nicknameIter.next ().value ); console .log (nicknameIter.next ().value ); console .log (nicknameIter.next ().value );
因为支持生成器方法,所以可以通过添加一个默认的迭代器,把类实例变成可迭代对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Person { constructor ( ) { this .nicknames = ['Jack' , 'Jake' , 'J-Dog' ]; } *[Symbol .iterator ]() { yield *this .nicknames .entries (); } } let p = new Person ();for (let [idx, nickname] of p) { console .log (nickname); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Person { constructor ( ) { this .nicknames = ['Jack' , 'Jake' , 'J-Dog' ]; } [Symbol .iterator ]() { return this .nicknames .entries (); } } let p = new Person ();for (let [idx, nickname] of p) { console .log (nickname); }