es6 class学习笔记

前言

虽然class在实际开发中已经被大量使用,仍然打算做一篇整理好好梳理一下。


基本语法

js语言中,生成实例对象的传统方法就是通过构造函数:

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);
p.toString(); //"(1,2)"

ES6中的class可以看做只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰,更像面向对象编程语法而已
上面的代码用class改写:

//定义类
class Point{
  constructor(x,y){
    this.x = x;
    this.y = y;
  }
  toString () {
    return `( ${this.x} , ${this.y} )`
  }
}
var p = new Point(1,2);
p.toString(); // "(1,2)"

定义了一个Point类,constructor即为构造方法;而this关键字则代表实例对象,也就是说,ES5的构造函数Point,对应ES6的Point类的构造方法。范例中Point类除了构造方法,还定义了一个toString方法,定义类的方法的时候,前面不需要加function这个关键字,方法之间不需要逗号分隔
构造函数的prototype属性,在ES6的类上继续存在,实际上,类的所有方法都定义在类的prototype属性上面。

一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。
constructor方法默认返回实例对象this。
类必须使用new调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new也可以执行。

var p1 = new Point(2,3);
var p2 = new Point(3,2);
p1.__proto__ === p2.__proto__ // true
// 实例的原型原型都是Point.prototype,所以__proto__相等
// 生产环境我们可以使用 Object.getPrototypeOf 方法来获取实例对象的原型,
// 然后再来为原型添加方法/属性,尽量避免使用__proto__。

细化

  1. 在类和模块的内部默认就是严格模式,所以不需要use strict指定运行模式,只要代码写在类或者模块之中,就只有严格模式可用
  2. 与 ES5 一样,在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
  3. 属性表达式
let methodName = 'getArea';
class Square {
  constructor(length) {}
  [methodName]() {}
}
// Square类中添加了getArea方法
  1. class表达式
//采用Class表达式,可以写出立即执行Class
let person = new class {
  constructor (name) {
    this.name = name ;
  }
  sayName() {
    console.log(this.name);
  }
}("张三");
person.sayName() //"张三"
//person是一个立即执行类的实例
  1. 不存在变量提升
    这个和ES5完全不一样,会提示ReferenceError,更贴近let、const的风格。这种规定的原因和继承有关,必须保证子类在父类之后定义。
  2. name属性总是返回紧跟class关键字后的类名
  3. 如果某个方法之前加上星号(*),就表示该方法是一个 Generator 函数(这一部分重心应该在函数生成器上,衍生开来又是一大篇知识)
  4. 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会指向该方法运行时所在的环境,因为找不到print方法而导致报错。
// 正确的方法是使用bind、箭头函数或Proxy
  1. 私有方法、私有属性ES6不提供,只能通过变通方法模拟实现。
    命名上私有方法前缀_,私有属性前缀#。(有提案,通过前缀#完成类似private词缀)

静态方法、静态属性

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

class Foo {
  static classMethod() {
    return 'hello';
  }
}
Foo.classMethod() // 'hello'
var foo = new Foo()
foo.classMethod() // TypeError: foo.classMethod is not a function

Foo类的classMethod方法前有static关键字,表明该方法是一个静态方法,可以直接在Foo类上调用(Foo.classMethod()),而不是在Foo类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。
*注意,如果静态方法包含this关键字,这个this指的是类,而不是实例。

class Foo {
  static bar () {
    this.baz();
  }
  static baz () {
    console.log('hello');
  }
  baz () {
    console.log('world');
  }
}
Foo.bar() // hello

静态方法bar调用了this.baz,这里的this指的是Foo类,而不是Foo的实例,等同于调用Foo.baz。另外,从这个例子还可以看出,静态方法可以与非静态方法重名
父类的静态方法,可以被子类继承。

class Foo {
  static classMethod() {
    return 'hello';
  }
}
class Bar extends Foo {}
Bar.classMethod() // 'hello

静态方法也是可以从super对象上调用的。
至于静态属性。指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。

// 老写法
class Foo {}
Foo.prop = 1;
// 新写法
class Foo {
  static prop = 1;
}

继承

Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

class Point {}
class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  }
  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }
}

子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this

class ColorPoint extends Point {
// 如果子类没有定义constructor方法,这个方法会被默认添加
}
// 等同于
class ColorPoint extends Point {
  constructor(...args) {
    super(...args);
  }
}

Object.getPrototypeOf方法可以用来从子类上获取父类。

继承中的super关键词
  1. super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。子类构造函数外的地方不能调用否则报错。
    其代表父类的构造函数,但返回的是子类的实例。相当于Parent.prototype.constructor.call(this)
  2. super作为对象时,在普通方法中指向父类的原型对象;在静态方法中指向父类。

prototype 属性和__proto__属性

关于这两者我在__proto__和prototype中已经做过整理。阮一峰前辈所表达的主要内容:

关于类的prototype和__proto__

Class 作为构造函数的语法糖,同时有prototype属性和proto属性,因此同时存在两条继承链。

  1. 子类的__proto__属性,表示构造函数的继承,总是指向父类。
  2. 子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
class A {}
class B extends A {}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true
// 上面的结果是因为类的继承按照下面的模式去实现的
// B 的实例继承 A 的实例
Object.setPrototypeOf(B.prototype, A.prototype);
// B 继承 A 的静态属性
Object.setPrototypeOf(B, A);
// 关于setPrototypeOf方法的实现
Object.setPrototypeOf = function (obj, proto) {
  obj.__proto__ = proto;
  return obj;
}
关于实例的__proto__ 属性

子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性。也就是说,子类的原型的原型,是父类的原型。


心得总结

class归根结底还是es5原型链的语法糖,学过基础的面向对象课程应该会比较容易上手起来,反倒是先前的prototype使用起来可能会比较反人类。
核心内容其实只要掌握了构造函数、静态方法静态变量、继承。还有就是搞清prototype与__proto__之间的关系,这块内容基本就拿下了。


参考

阮一峰的ES6--Class的基本语法
阮一峰的ES6--Class 的继承

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容