1.class简介
-
1.1 javascript 语言的传统方法是通过构造函数定义并生成新对象。下面是一个例子
{ function Point(x, y){ this.x = x; this.y = y; } Point.prototype.sum = function(){ console.log(this.x + this.y) } let p = new Point(1,2); p.sum(); //3 }
-
1.2 ES6采用Class关键字来定义类
{ class Point { constructor(x,y){ this.x = x; this.y = y; } sum(){ console.log(this.x + this.y) return (this.x + this.y) } } let p2 = new Point(2,3); p2.sum(); //5 } 上面的代码定义了一个“类”,可以看到里面有一个constryctor方法,这就是构造方法,this关键字代表实例对象 还定义了一个sum求和方法
-
1.3 注意
{ class Point2 { //... } console.log(typeof Point2); //function console.log(Point2 === Point2.prototype.constructor); //true } 上面代码表明,类的数据类型就是函数,类本身就指向构造函数。 使用的时候也是直接使用new命令,和构造函数的用法一致。
-
1.4构造函数的prototype属性在ES6的“类”上继续存在。事实上,类的所有方法都定义在类的prototype属性上。
{ class Point { constructor(){ //... } toString(){ //... } toValue(){ //.... } } // 等同于添加到Point的prototype属性上 Point.prototype = { constructor() {}, toString() {}, toValue() {} } //由于类的方法(constructor除外)都定义在prototype对象上,所以类的新方法可以添加在prototype对象上 // Object.assign 方法可以很方便的一次向类添加多个方法。 class Point {}; Object.assign(Point.prototype,{ toString(){}, toValue(){} })
}
-
1.5 在类的实例上调用方法,实际上就是调用原型上的方法。
{ class B {} let b = new B(); b.constructor === B.prototype.constructor;
}
2.contructor方法
constructor方法是类的默认方法,通过new命令生成对象实例时会自动调用该方法,如果没有显示定义,一个空的constructor就会被默认添加。
{
class Point {}
=> 等同于
class Point {
constructor (){}
}
}
3.类的实例对象
class命令生成实例对象的语法和ES5一样,也是使用new命令
{
class Point {}
let p = new Point();
}
这点与ES5一样,实例的属性除非显示定义在其本身(this对象)上,否则都是定义在原型(class)上。
{
//定义类
class Point {
constructor(x,y){
this.x = x;
this.y = y;
}
toSum(){
console.log(this.x + this.y);
}
}
let p = new Point(2,3);
let p2 = new Point(1,2);
p.toSum();
// /5
console.log(p.hasOwnProperty('x'));
//true
console.log(p.hasOwnProperty('y'));
//true
console.log(p.hasOwnProperty('toSum'));
//false
console.log(p.__proto__ === p2.__proto__);
//true
//所有的实例对象都共享一个原型对象
}
// 因为定义在this变量上,所以x,y是p本身的属性,而toSum则是原型对象的属性。
4.class表达式
{
let myClass = class {
constructor(){
console.log('123');
}
}
}
5.calss不存在变量提升
{
new Foo();
//ReferenceError
class Foo {}
}
// 上面的代码中,Foo类使用在前,定义灾后,这样会报错,因为ES6不会把变量声明替身到代码头部。
// 这与下文要提到的继承有关,必须保证子类在父类之后定义
{
let Foo = class {};
class Bar extends Foo {};
}
// 上述代码不会报错,是因为Bar在继承Foo的时候,Foo已经声明了。如果存在class提升,那样Bar继承时Foo还没有定义就会报错
6.私有方法和私有属性
私有方法是常见需求,但是ES6不提供,只能通过变通的方法来模拟实现。
在命名上加以区别
{
class Widget {
//公有方法
foo(){
console.log('something');
}
//私有方法
_bar() {
console.log('nothing');
}
}
}
但是这种方法并不保险,在类的外部依旧可以调用这个方法。
将私有方法一处模块,因为模块内部所有方法是对外可见的
{
class getSomething {
getWidth(height){
getHeight.call(this,height)
}
}
function getHeight(height){
this.height = height;
console.log(height);
}
let b = new getSomething();
b.getWidth('100px');
b.getHeight();
//b.getHeight is not a function
}
这使得getHeight成为当前模块的私有方法
利用symbol值的唯一性将私有方法得名字命名为一个symbol值
{
let bar = Symbol('bar');
let snaf = Symbol('snaf');
class myClass {
//公有方法
foo(baz) {
this[bar](baz)
}
//私有方法
[bar](baz) {
this[snaf] = baz;
console.log(this[snaf]);
}
}
let p2 = new myClass();
p2.foo('123');
}
因为Object.getOwnPropertySymbols() 的出现,基本可以认为是私有变量和私有属性
7.this指向
类的方法内部如果含有this,他将默认指向类的实例.但是必须非常小心,一旦单独使用该方法,很可能会报错.
{
class Logger {
printName(name = 'there') {
this.print(`Hello ${name}`);
}
print(text){
console.log(text);
}
}
const p = new Logger();
p.printName();
//hello threr
p.print(123);
//123
const { printName } = p;
printName();//报错
}
上面的代码中,printName方法中的this默认指向Logger的实例,但是吐过将这个方法取出来单独使用,this指向该方法
运行时所在的环境,因为找不到print方法报错.
1.在构造函数方法中绑定this,这样不会报错了
{
class Logger2 {
constructor(){
this.printName = this.printName.bind(this);
}
printName(name = 'there') {
this.print(`Hello ${name}`);
}
print(text){
console.log(text);
}
}
const p = new Logger2();
const { printName } = p;
printName();
//hello there
}
2.使用箭头函数
{
class Logger {
constructor(){
this.printName = (name = 'there') => {
this.print(`hello ${name}`)
}
}
print(text){
console.log(text);
}
}
const p = new Logger();
const { printName } = p;
printName();
//hello there
}
8.name 属性
{
class Point {}
Point.name
//Point
name 属性返回紧跟在calss关键字后面的类名
}
9.class静态方法
类相当于实例的原型,所有在类中定义的方法都会被实例继承.如果这个方法时静态方法,就不会被继承.
{
class Foo {
static sum(a,b)
{
console.log(a + b);
}
}
let p = new Foo();
// p.sum(2,3);
//报错
Foo.sum(1,2);
//3
class Bar extends Foo {}
Bar.sum(19,2);
//21
}
// 因为类Foo时static关键字定义的,所以属于静态方法,所以实例不能继承,但是父类可以调用
//子类也可以通过继承类使用父类的静态方法
10.class的静态属性和实例属性
-
ES6明确规定class内部只有静态方法,没有静态属性
只能这样声明静态属性
{
class Foo {}foo.name = 'kjh' Foo.name //kjh
}
或者{
class Foo {
static name = “123”
}
}静态属性也和方法一样子类不能调用,但是可以继承到
-
class的实例属性可以直接写入到类的定义中去 { class myClass { name = "kjh"; sayName(){ console.log(this.name); } } let p = new myClass(); p.sayName(); //kjh }
-
参考文献 <ES6标准入门>