ES6 class的基本语法

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方法

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

    2. 一个类必须有constructor方法,如果没有显式定义,会默认添加一个空constructor方法

    3. constructor默认返回实例对象(this),完全可以指定返回另外一个对象

       //改变返回的对象为空对象,因此通过new Foo()创建的实例不是继承自Foo
       class Foo {
       	constructor() {
       		return Object.create(null);
       	}
       }
       new Foo() instanceof Foo
       // false
      
    4. 类必须使用new调用,否则会报错.普通构造函数不用new也可以执行(虽然没有太大意义),但类不行

  • 类的实例对象

    1. 前面提到过的,生成类的实例对象的写法与ES5完全一致,使用new命令即可.但是如果没加new则会报错

    2. 与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
      
    3. 类的所有实例共享一个原型对象

       var p1 = new Point(2,3);
       var p2 = new Point(3,2);
       p1.__proto__ === p2.__proto__
       //true	
       //p1.__proto__===Point.prototype
      
  • class表达式

    1. 类可以用表达式的形式定义

       //这个类的名称是MyClass.Me是在类的内部使用,指代当前类,如果内部没用到可以省略
       const MyClass = class Me {
       	getClassName() {
       		return Me.name;
       	}
       };
      
    2. 采用class表达式可以写出立即执行的class

       let person = new class {
       	constructor(name) {
       		this.name = name;
       	}
       	sayName() {
       		console.log(this.name);
       	}
       }('张三');
       person.sayName(); // "张三"
      
  • 不存在变量提升:类不存在变量提升,必须先声明后使用.因此以后用到的类的继承必须保证子类在父类之后定义

  • 私有方法

    1. 私有方法ES6不提供,只能模拟实现

    2. 可以在命名上加上特殊标识区别(可以在外部调用,不安全,不建议)

       class Widget {
       	// 公有方法
       	foo (baz) {
       		this._bar(baz);
       	}
       	// 私有方法
       	_bar(baz) {
       		return this.snaf = baz;
       	}
       	// ...
       }
      
    3. 将私有方法移出模块,因为模块内部的方法都是对外可见的

       //通过
       class Widget {
       	foo (baz) {
       		bar.call(this, baz);
       	}
       	// ...
       }
       function bar(baz) {
       	return this.snaf = baz;
       }
      
    4. 利用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;
       	}
       	// ...
       };
      
  • 私有属性

    1. ES6不支持私有属性.有一个方案为class添加了私有属性.就是在属性名之前使用#表示.同时,私有属性可以和实例的属性同名(例如 #x和get x(){})

    2. 同时,也可以用#来表示私有方法

       class Foo {
       	#a;
       	#b;
       	#sum() { return #a + #b; }
       	printSum() { console.log(#sum()); }
       	constructor(a, b) { #a = a; #b = b; }
       }
      
  • this的指向

    1. 类的方法内部如果有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
      
    2. 可以在构造方法中绑定this

       class Logger {
       	constructor() {
       		this.printName = this.printName.bind(this);
       	}
       	// ...
       }
      
    3. 或者使用箭头函数,因为箭头函数有绑定this 的功能

       class Logger {
       	constructor() {
       		this.printName = (name = 'there') => {
       			this.print(`Hello ${name}`);
       		};
       	}
       	// ...
       }
      
    4. 也可以用Proxy在获取方法的时候自动绑定this,这里不再重写

  • name属性:总是返回紧跟class关键字后面的类名

  • class的取值函数(getter)和存值函数(setter)

    1. 可以在类的内部使用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'
      
    2. 存值函数和取值函数是设置在属性的 Descriptor 对象上的

  • class的Generator方法

    1. 如果某个方法之前加上*号,就表示该方法是一个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的静态方法

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

       class Foo {
       	static classMethod() {
       		return 'hello';
       	}
       }
       Foo.classMethod() // 'hello'
       var foo = new Foo();
       foo.classMethod()
       // TypeError: foo.classMethod is not a function
      
    2. 如果静态方法上包含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
      
    3. 父类的静态方法可以被子类继承

       class Foo {
       	static classMethod() {
       		return 'hello';
       	}
       }
       class Bar extends Foo {
       }
       Bar.classMethod() // 'hello'
      
    4. 静态方法也可以从super对象上调用的

       class Foo {
       	static classMethod() {
       		return 'hello';
       	}
       }
       class Bar extends Foo {
       	static classMethod() {
       		return super.classMethod() + ', too';
       	}
       }
       Bar.classMethod() // "hello, too"
      
  • class的静态属性和实例属性

    1. 静态属性是指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
      
    2. 类的实例属性:可以使用等式写入类的定义之中

       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
          		};
          	}
          }
        
    3. 类的静态属性:

      • 只要在实例属性写法前面加上static即可

          class MyClass {
          	static myStaticProp = 42;
          	constructor() {
          		console.log(MyClass.myStaticProp); // 42
          	}
          }
        
  • new.target属性:一般用于构造函数之中,返回new命令作用域哪个构造函数.如果构造函数不是通过new命令调用的,new.target返回undefined.因此这个属性可以用来确定构造函数是怎么调用的

    1. Class内部调用new.target,返回的是当前Class

       class P{
       	constructor{
       		console.log(new.target === P);	//true
       	}
       }
      
    2. 子类继承父类时,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); // 正确
      
    3. 在函数外部使用new.target会报错

  • 1
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值