博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Class的基本语法
阅读量:6655 次
发布时间:2019-06-25

本文共 6718 字,大约阅读时间需要 22 分钟。

本系列属于阮一峰老师所著的学习笔记


概念
// 生成实例对象的传统方法是通过构造函数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 + ')'  }}// 由于类的方法都定义在prototype对象上,所以类的新方法可以添加在prototype对象上面class Point{  constructor(){    // ...  }}Object.assign(Point,prototype,{  toString(){},  toValue(){}})// prototype对象的constructor属性,直接指向类本身,与ES5行为一致Point.prototype.constructor === Point // true// 类内部定义的方法,都是不可枚举的(non-enumerable)class Point{  constructor(x,y){    // ...  }  toString(){    // ...  }}Object.keys(Point.prototype) // []Object.getOwnPropertyNames(Point.prototype) // ["constructor","toString"]// 这一点与ES5不同,ES5中对象内部定义的方法是可枚举的var Point = function(x,y){  // ...}Point.prototype.toString = function(){  // ...}Object.keys(Point.prototype) // ["toString"]Object.getOwnPropertyNames(Point.prototype) // ["constructor","toString"]// 类的属性名,可以采用表达式let methodName = 'getArea'class Square {  constructor(length){    // ...  }  [methodName](){    // ...  }}
严格模式

类和模块的内部,默认就是严格模式。

考虑到未来所有的代码,其实都是运行在模块之中,所以ES6实际把整个语言升级到严格模式。

constructor方法

constructor方法是类的默认方法,通过new命名生成对象实例时,自动调用该方法。

// constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象class Foo{  constructor(){    return Object.create(null)  }}new Foo() instanceof Foo // false// 类必须用new调用,否则会报错。这是和普通构造函数的一个主要区别
类的实例对象
// 与ES5一样,实例属性除非显式定义在其本省(即定义在this对象上),否则都是定义在原型上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') // truepoint.hasOwnProperty('toString') // falsepoint.__proto__.hasOwnProperty('toString') // true// 与ES5一样,共享一个原型对象var p1 = new Point(1,2)var p2 = new Point(2,3)p1.__proto__ === p2.__proto__ // true// 这也意味着,可以通过实例的__proto__属性为类添加方法p1.__proto__.printName = function () { return 'Oops' }p1.printName() // 'Oops'p2.printName() // 'Oops'var p3 = new Point(4,2)p3.printName() // 'Oops'// 使用实例的__proto属性改写原型必须相当谨慎,因为这会改变类的原始定义,影响到所有实例,不推荐使用。
Class表达式
// 与函数一样,类也可以使用表达式的形式定义const MyClass = class Me {  getClassName(){    return Me.name  }}// 需要注意的是,这个类的名字是MyClass而不是Me,Me只在Class的内部代码可用,指代当前类let inst = new MyClass()inst.getClassName() // MeMe.name // ReferenceError: Me is not defined// 内部未使用的话可以省略Meconst MyClass = class { /* ... */ }// 利用Class表达式,可以写出立即执行的Classlet person = new class{  constructor(name){    this.name = name  }  sayName(){    console.log(this.name)  }}('Angus')person.sayName() // 'Angus'
不存在变量提升
// 与ES5完全不同的是,类不存在变量提升new Foo() // ReferenceErrorclass Foo {}// 该规定与类的继承有关,必须保证子类在父类之后定义
私有方法
// 私有方法是常见需求,但是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)  }  // 私有方法  [baz](baz){    return this[snaf] = baz  }}
私有属性

与私有方法一样,ES6不支持私有属性。目前有一个提案,为class加私有属性,方法是在属性名之前,使用#表示。

class Point{  #x  constructor(x=0){    #x = +x // 写成this.#x亦可  }  get x() { return #x }  set x(value) { #x = +value }}
this的指向
// 类的方法内部如果有this,则默认指向类的实例。但是一旦单独使用该方法,很可能报错class Logger {  printName(name = 'there'){    this.print(`Hello ${name}`)  }  print(text){    console.log(text)  }}const logger = new Logger()const { printName } = loggerprintName() // TypeError: Cannot read property 'prin' of undefined// 如果单独使用,this会指向该方法运行时所在的环境,因为找不到print方法而报错// 解决办法一:在构造方法中绑定thisclass Logger {  constructor(){    this.printName = this.printName.bind(this)  }}// 解决办法二:使用箭头函数class Logger{  constructor(){    this.printName = (name = 'there') => {      this.print(`Hello ${name}`)    }  }}
name属性
// 本质上,ES6的类只是ES5构造函数的一层包装,所以函数的许多特性都被class继承了,包括name属性class Point {}// name属性总是返回紧跟在class关键字后面的类名Point.name // 'Point'
Class的取值函数(getter)和存值函数(setter)
// 与ES5一样,在类的内部可以使用get和set关键字,对某属性设置存值函数和取值函数,拦截该属性的存取行为class MyClass{  constructor(){    // ...  }  get prop(){    return 'getter'  }  set prop(value){    console.log('setter:' + value)  }}let inst = new MyClass()inst.prop = 123 // setter: 123inst.prop // 'getter'// 存值函数和取值函数是设置在属性的Descriptor对象上的
Class的Generator方法
// 如果在方法之前加上星号(*),就表示该方法是一个Generator函数class Foo{  constructor(...args){    this.args = args  }  *[Symbol.iterator](){    for (let arg of this.args) {      yield arg    }  }}// Symbol.iterator方法返回一个Foo类的默认遍历器,for...of循环会自动调用这个遍历器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指的是类,而不是实例class Foo {  static bar(){    this.baz()  }  static baz(){    console.log('hello')  }  baz(){    console.log('world')  }}// this指的是Foo类,而不是Foo实例,等同于调用Foo.baz,另外静态方法可以与非静态方法重名Foo.bar() // 'hello'// 父类的静态方法,可以被子类继承class Foo {  static classMethod(){    return 'hello'  }}class Bar extends Foo {}Bar.classMehod() // 'hello'// 静态方法也可以从super对象上调用class Foo {  static classMethod(){    return 'hello'  }}class Bar extends Foo {  static classMethod(){    return super.classMethod() + ',too'  }}Bar.classMethod() // 'hello,too'
new.target属性
// 该属性一般用在构造函数中,返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的,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('Angus') // 正确var notAPerson = Person.call(person,'Angus') // 报错// Class内部调用new.target,返回当前Classclass Rectangle{  constructor(length,width){    console.log(new.target === Rectangle)    this.length = length    this.width = width  }}var obj = new Rectangle(3,4) // true// 子类继承父类时,new.target会返回子类class Square extends Rectangle {  constructor(length){    super(length,length)  }}var obj = new Square(3) // false// 利用这个特点可以写出不能独立使用,必须继承后使用的类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会报错

转载于:https://www.cnblogs.com/pengzhixin/p/7737335.html

你可能感兴趣的文章
Docker 网络及数据卷设置 [三]
查看>>
一张图让你看懂JAVA线程间的状态转换
查看>>
hibernate使用联合主键
查看>>
Yii PHP 框架分析(二)
查看>>
如何在bp框架上使用map构造帮助信息?
查看>>
shell 里的简单比较字符
查看>>
VIM 粘贴代码时恶心的缩进
查看>>
我是被一篇文章吸引过来的!~
查看>>
vsphere 性能排查 基础检查方法
查看>>
corosync+pacemaker实现高可用的MariaDB
查看>>
python 输出彩色终端信息
查看>>
Golang 建立RESTful webservice 接收客户端POST请求发送wav语音文件
查看>>
为什么需要堆?
查看>>
CentOS 7 使用rpm包安装mysql 5.7.18
查看>>
nginx+tomcat+redis实现负载平衡和session共享
查看>>
转帖-linux文件系统
查看>>
mac上面查看路由表
查看>>
Nginx location 斜线问题
查看>>
2018/10/22 Linux 第3周笔记
查看>>
linux特殊权限SUID、SGID、SBIT
查看>>