【ES6学习】— (6)class定义类语法与Module模块化简介

来源:互联网 发布:principle软件 编辑:程序博客网 时间:2024/06/01 22:16

ES6中提供了类似于传统面向对象语言的Class类概念来定义类用来创建对象,使得创建对象的方式更加简便。同时也在语言层面提供了Module模块机制来实现模块化,有利于开发大型复杂的项目。本篇笔记主要介绍这两个知识点的相关内容。

一、class定义类

1.传统创建对象的方式

面向对象编程将我们的世界抽象为一个个的对象,传统的面向对象语言Java、C++中提供了class类的概念来定义对象。但是在传统的JavaScript语言中并没有class类,而是通过直接定义构造函数,然后通过new关键字创建对象。

//定义Vehicle构造函数var Vehicle = function (){  this.price = 1000;};//通过new关键字创建对象var v = new Vehicle();v.price // 1000

关于JavaScript对象的更详细描述可以参考这里

2.class基本语法与特性

为了使对象创建的写法更加接近于面向对象的样式,提供更加清晰的对象原型写法,ES6引入了Class类来作为对象模板,其功能和语法基本接近于传统语言的Class类。但就本质而言,class相当于语法糖,对ES5的构造函数作了进一步的包装,因此继承了许多函数的特性。
下面是用class定义类

class A{  constructor(x, y){    this.x = x;    this.y = y;  }}let a = new A(1,2);

下面简单介绍class类的一些特性:

  • 类的数据类型就是函数,类本身指向其构造函数
  • 类的所有方法都定义在类的prototype属性上,关于prototype属性不太了解的童鞋可以参阅这里
  • ES6的类内部定义的方法是不可枚举的,而ES5是可枚举的。
  • Class不存在变量提升,因此必须先定义后使用
  • ES6的Class类也有name属性,返回紧跟在class关键字后面的类名
  • 在类中定义的函数不需要使用function关键字,末尾也不用跟逗号,否则会报错

constructor方法

ES6中Class默认提供了constructor方法,类似于Java中的构造方法,该方法在通过new创建对象时会自动调用。一个类必须有constructor()方法,如果没有显式定义那么会默认添加一个空的方法:constructor(){}.

constructor()方法默认返回该类的实例对象(this)对象,当然也可以通过return语句自行指定返回另一个对象。

实例对象

ES6通过new命令创建实例对象的方式与ES5一样。其属性如果没有通过this显式定义在实例对象上,则全部定义在原型(Class)对象上。

ES6中所有的实例对象共享一个原型对象,这一点与ES5相同。

class表达式

Class也可以通过表达式的方式定义

//下面定义的类其类名是MyClass而不是M,M只在内部有用指代当前类const Myclass = class M{  constructor(){}}

3.Class类的继承

和传统面向对象一样,ES6的Class类也可以通过extends关键字实现继承。

class Point{  constructor(x, y){}}class ColorPoint extends Point{  constructor(x, y, color){    super(x, y);    this.color = color;  }  toString(){    //这里通过super调用父类的toString方法    return this.color + ":" + super.toString();  }}

几点注意:

  • 必须在子类的constructor方法中调用super方法,否则创建实例会报错。另外必须在super之后才能使用this.因为子类没有自己的this对象,是继承的父类的this对象
  • 子类实例的构建是基于对父类实例的加工,只有super才能返回父类实例.
  • 作为一个对象,子类的原型(proto属性)是父类。作为一个构造函数,子类的原型(prototype属性)是父类的实例
  • super关键字代表的是父类的实例
  • 如果在类中的某个方法前面加上*就表示该方法是一个Generator方法

设值与取值
Class内部可以通过get和set关键字来对某个属性值进行存值函数和取值函数。从而拦截该属性的存取行为

class MyClass{  constructor(){  }  get distanse(){    return 12;  }  set distanse(value){    console.log(value);  }}let m = new MyClass();m.distanse = 123;var dis = m.distanse;

静态方法与静态属性

类作为实例的原型,其定义的所有方法都会被实例继承,如果在方法前加上static关键字则意味着该方法不会被实例继承,只能通过类调用。如果通过实例调用会报错。另外父类的静态方法可以被子类继承通过子类调用并且可以通过super调用。

ES6规定Class内部没有静态属性只有静态方法,在ES7中有关于静态属性的提案,通过Babel转码可以使用

//ES6静态属性读写方法class Foo{}Foo.prop = 1;let p = Foo.prop;//ES7规定的实例属性写法Class MyClass1{  myProp = 11;  constructor(){    console.log(this.prop);  }}//ES7规定的静态属性写法class MyClass2{  static staticProp = 12;  constructor(){    console.log(MyClass2.prop);  }}

4.原生构造函数的继承

在ES5中,是通过先新建子类的实例对象this,在将父类的属性添加到子类上,由于父类的内部属性无法获取,因此我们无法继承原生的构造函数。但是在ES6中是先创建父类的实例对象this,然后在用子类的构造函数修饰this,因此我们可以继承父类所有的行为。因此我们可以继承已有的原生构造函数并定义自己的数据结构。
下面是ES6标准入门中一个自定义Error的例子

class ExtendableError extends Error{  constructor(message){    super();    this.message = message;    this.stack = (new Error()).stack;    this.name = this.constructor.name;  }}class MyError extends ExtendableError{  constructor(m){    super(m);  }}

原生构造函数如下:

  • Boolean(), Number(), String(), Array(), Date()
  • Function(), RegExp(), Error(), Object()

5.new.target属性

ES6为new命令引入了new.target属性,在构造函数中返回new命令所作用的构造函数,如果构造函数不是通过new命令调用则返回undefined.另外子类继承父类时new.target会返回子类。
通过上述特性可以确保构造函数只被new命令调用或者写出只能通过子类继承才能使用的类

class MyClass1{  constructor(){    if(new.target !== undefined){      //...    }else{      throw new Error("只能通过new命令调用");    }  }}class MyClass2{  constructor(){    if(new.target === MyClass2){      throw new Error("本类不能实例化");    }else{      //...    }  }}

二、Module模块化

在ES6之前JavaScript一直没有模块体系,无法将大型程序拆分为互相依赖的小文件在根据需要拼装起来。社区提供了CommonJS和AMD规范来实现浏览器和服务的模块化。ES6在语言层面上提供了Module实现模块功能。
有关JavaScriptm模块化和CommonJS/AMD规范可以参阅阮一峰老师的这篇文章

简单来说,ES6提供的模块功能主要是通过export命令显式静态指定输入的代码,然后通过import命令静态引入。在
编译时就确定模块的依赖关系以及输入输出的变量。

1.export命令与import命令

ES6模块功能主要是由export命令和import命令构成。export命令用来规定模块的对外接口;import命令用于输入其他模块所提供的功能。示例如下:

//test.jsexport var name = "tom";let person1 = "p1";let person2 = "p2";export {person1, person2};function f1(){}export {f1 as func1};

关于export的几点注意:

  • export可以对外输出变量、函数和类
  • 一个模块就是一个独立的文件,export命令可以出现在模块的任一位置,但必须位于模块层级。如果处于块级作用域则会报错。import也是如此。
  • 通常情况下export输出的变量就是其变量名,但可以通过as进行重命名
  • export语句输出的值是动态绑定,绑定其所在的模块

通过上面的export命令对外输出后,就可以在其他JS文件中通过import命令引入加载该模块

import {name, person1, person2} from './test';function printData(){  console.log(name  + person1);}

关于import命令的几点注意:

  • import命令具有提升效果,会提升到整个模块的头部首先执行
  • import接受一个对象(用大括号表示),里面的指定从外部模块导入的变量名,必须与被导入模块中的对外接口名称相同。当然也可以使用as对其进行重命名

2.模块的整体加载

上面我们是单独指定每一个变量进行加载导入。另外也可以通过星号*或者module命令实现模块的整体加载

//math.jsexport funciont sum(x, y){  return x+y;}export funciont sub(x, y){  return x-y;}//通过*整体加载,使用as指定对象名称import * as math from './math'console.log(math.sum(4,5));//module命令可以代替import实现整体加载,后跟一个变量表示模块定义在该变量上module math from './math';console.log(math.sub(4,5));

3.export default 命令

该命令用来指定模块的默认输出。本质上来说就是输出一个叫做default的变量或者方法,然后系统允许你为它起任何名字

//test.jsexport default function fun(){  console.log('foo');}//引入import customFun from './test';customFun();//foo//可以同时输入默认方法和其他的方法变量//import customFun,{otherMethod} from './test';

几点注意:

  • 一个模块只能有一个默认输出,因此export default只能使用一次
  • 从代码可以看出,引入默认输出的import命令后面不需要跟大括号。
  • 如果输出默认的值只有将值跟在export default之后。
  • 该命令也可以用来输出类

4.模块继承

模块之间可以继承的,可以在原有模块的基础上再次输出自定义的变量和默认方法。

//有circle模块,circleplus模块继承该模块export * from circle;//输出circle模块的内容//也可以指定输出 export {area as circleArea} from 'circle';export var pi = 3.14;export default function(value){  Math.exp(value);}//加载module math from 'circleplus';//整体加载import exp from 'circleplus';//加载默认方法为exp

4.ES6模块加载性质与循环加载

ES6的模块加载对外输出的是值的引用。这么CommonJS对外输出值的拷贝有着本质的不同。
ES6模块加载机制:在遇到import命令时不会去执行模块,而是生成一个动态的只读引用,等到需要的时候在到模块中取值。因此ES6模块是动态引用,并且不会缓存值,模块里的变量绑定其所在的模块。换句话说就是加载模块的值与模块实时的运算值相同,完全反映其模块内部的变化。

//lib.jsexport let counter = 3;export function initCounter(){  counter ++;}//引入import {counter, initCounter} from './lib';console.log(counter);//3initCounter();//这里输出的是4,会随着模块的执行而变化。如果是CommonJS则输出3。从这一点来看ES6提供了模块机制要优于CommonJconsole.log(counter);//4   

需要注意的是,ES6输入的模块是一个符号引用,这个变量指向的地址是只读的,对它重新赋地址值会报错。

循环加载

循环加载指的是a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本。CommonJS是值得拷贝,如果加载时代码没有执行则就没有值,会出现。ES6模块是动态引用,只要引用存在,代码就能执行。
以下代码使用Module模块机制可以执行,但是如果使用CommonJS就会报错。
代码示例:

//even.jsimport {odd} from './odd';export var counter = 0;export funxtion(n){  counter ++;  return n == 0 || odd(n-1);}//odd.jsimport {even} from './even';export function odd(n){  return n!=0 && even(n-1);}

以上就是关于Class和Module的相关简记,希望对看到的同学有所帮助。

0 0
原创粉丝点击