前言
今天给大家介绍ES6中的class类与模块。
1.class类
概述:ES6中,class被作为对象的模板引入,可以通过class关键字定义类。class本质就是function。可以看作是一个语法糖,让创建对象的原型写法更加清晰,更像面向对象编程的写法。
1.1:定义、声明
注意点:
- 不可重复声明。
- 类定义不可被提升。这代表,在访问前必须对类进行定义,否则会报错。
- 类方法不需要加function关键字。方法间不能有分号。
class test(){ //类声明
constructor(num){
this.num = num;
}
}
let test = class {//匿名类
constructor(num){
this.num = num;
}
}
let test = class test {//命名类
constructor(num){
this.num = num;
}
}
1.2:类的主体
类的实例化
- class的实例必须使用new关键字创建
- 实例化对象共享类的原型对象
class Test{}
let test = Test();//Class constructor Example cannot be invoked without 'new'
let test = new Test();
class Test{
constructor(){}
sum(num,num2){
return num+num2;
}
}
let test = new Test();
let test2 = new Test();
test.sum(15,15);//30
test2.sum(20,20);//40
test.__proto__.sum = function(num,num2){
return num-num2;
}//覆盖Test原型对象中的sum方法
test.sum(15,10);//5
test2.sum(15,15);//0
属性
prototype
ES6中,prototype依然可用,虽然可以直接在类中定义方法,但是方法还是定义在prototype上的。
可用于覆盖方法、初始化时添加方法。
class Test{
constructor(){
}
sum(){
return 15;
}
}
let test = new Test();
test.sum();//15
Test.prototype.sum = function sum(){//覆盖sum方法
return 20;
}
test.sum();//20
Test.prototype.num = function num(){//在Test原型对象中添加一个num方法
return 25;
}
test.num();//25
//另外一种往原型对象中添加方法的方法
Object.assign(Test.prototype,{
num(){//覆盖num方法
return 30;
}
});
test.num();//30
- 静态属性:class本身的属性,既定义在类内部的属性。不需要实例化就可访问。
class Test{
static num = 15;//声明一个静态属性
}
Test.num;//15
Test.num2 = 20;//声明一个静态属性
Test.num2;//20
- 实例属性:定义在实例对象(this)上的属性
class Test{
num = 20;//声明一个实例属性,注意这里不能加var和let、const
constructor(){
console.log(this.num);//20
}
}
let test = new Test();//20
test.num;//20
- 公共属性:定义在原型对象上的属性。所有实例对象都可访问。
class Test{
constructor(){}
}
Test.prototype.num = 5;
let test = new Test();
test.num;//5
let test2 = new Test();
test2.num;//5
- name属性:返回跟在class类后面的名字。
let Test = class{
constructor(){}
}
Test.name;//Test
let Test = class Emaple{
constructor(){}
}
Test.name;//Emaple
方法
- constructor方法:是类的默认方法,在创建类的实例化对象时调用。
class Test{
constructor(){//默认返回实例化对象this
console.log(666);
}
}
let test = new Test();//666
console.log(test instanceof Test);
class Example{}
class Test{
constructor(){
return new Example();//指定返回对象
}
}
let test = new Test();
console.log(test instanceof Test);//false
- 静态方法
class Test{
static num(){
return 15;
}
}
Test.num();//15
- 实例方法
class Test{
constructor(){
this.num = () =>{
return 20;
}
}
}
let test = new Test();
test.num();//20
- 原型方法
class Test{
num(){
return 15;
}
}
let test = new Test();
test.num();//15
封装与继承
- getter / setter
class Test{
constructor(num){
this.num = num;
}
get num(){
console.log('get');
return this.num;
}
set num(num){
console.log('set');
this.num = num;//这里会自身递归调用
}
}
let test = new Test(15);//无限调用set num方法,直到报错。
//解决方法
class Test{
constructor(num){
this.num = num;
}
get num(){
console.log('get');
return this._num;
}
set num(num){
console.log('set');
this._num = num;
}
}
let test = new Test(15);//输出set
test.num;//输出get,15
注意点:
- getter 不可单独出现
class Test{
constructor(num){
this.num = num;
}
get num(){
console.log('get');
return this._num;
}
}
let test = new Test();//TypeError: Cannot set property a of #<Example> which has only a getter
- getter / setter 必须同级出现,要么都放在父类,要么都放在子类。
class Animal {
constructor(){}
get num(){//get方法放到父类
return this._num;
}
}
class Dog extends Animal {
constructor(){
super();
}
set num(num){//set方法放到子类
this._num = num;
}
}
let dog = new Dog();
dog.num = 5;
console.log(dog.num);//undefined
继承:通过extends实现类的继承
注意点:
- 不可继承常规对象
let animal = {};
class Dog extends animal{}//TypeError: Class extends value #<Object> is not a constructor or null
//解决方案
let animal = {
name : '六十元'
};
class Dog{
constructor(){
console.log(super.name);
}
}
Object.setPrototypeOf(Dog.prototype,animal);//使用Object中的setPrototypeOf方法进行继承。
let dog = new Dog();//输出六十元
class 子类 extends 父类 {
}
super
注意点:
- 子类中的constructor方法中必须有super,而且是在this之前。
class Animal{
constructor(){}
}
class Dog extends Animal{
constructor(){}
}
let dog = new Dog();//ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
- 在子类调用父类构造函数,只能在子类的构造函数中调用。
class Animal{
constructor(){}
}
class Dog extends Animal{
constructor(){
super();
}
run(){
super();//SyntaxError: 'super' keyword unexpected here
}
}
let dog = new Dog();
- 调用父类方法,super作为对象。在子类的普通方法中,指向的是父类原型对象中的方法。在子类的静态方法中,指向的是父类本身的方法(也就是静态方法)。
class Animal{
constructor(){}
test(){//普通方法
return 10;
}
static testStatic(){//静态方法
return 15;
}
}
class Dog extends Animal{
constructor(){
super();
console.log(super.test());//10
}
static example(){
console.log(super.testStatic());//15
}
}
let dog = new Dog();//10
Dog.example();//15
2.模块
概述:
在ES6前,实现模块化使用的是RequireJs或者seaJs(分别是基于AMD规范的模块化库,和基本CMD规范的模块化库)
ES6引入了模块化,其设计思想是在编译时就能确定模块的依赖关系,以及输入和输出变量。
ES6的模块化分为导入(import)和导出(export)两个模块。
特点:
ES6模块自动开启严格模式,不管有没有在模块头部加上 user strict;
模块中可以导入和导出各种类型的变量,比如函数、对象、字符串、数值、布尔值、类等等。。
每个模块都有自己的上下文,每一个模块内声明的变量都是局部变量,不会污染到全局变量。
每一个模块只加载一次(单例的)。若再去加载同目录下同文件,直接从内存中读取。
优点:
- 减少命名冲突。
- 避免了引入js文件时的层层依赖
- 可以提升执行效率
基本用法:
- 导出的函数声明与类声明必须要有名称(export default命令另外考虑)
- export可以出现在模块的任何位置,但必需处于模块顶层。
- import命令会提升到整个模块的头部,首先执行。
- 不仅能导出声明还能导出引用(函数)。
//该js文件处于js/module1.js
let name = "六十元";
let age = 20;
let sayHi = function (){
alert("嗨喽!我叫"+name+",今年"+age+"岁。");
}
export {name,age,sayHi} //导出
//该文件处于index.html
import {name,age,sayHi} from "js/module1.js";//导入module1.js文件中导出的接口
alert(name);//输出六十元
alert(age);//输出20
sayHi();//输出嗨喽!我叫六十元,今年20岁。
要导出的类或者函数都需要有相应的名称。
2.1:as命令
导入和导出时可以使用as命令,给接口名称起别名。
注意点:
- export导出接口变量名称,需和模块内的变量有一一对应关系。
- 导入的变量名,需和导出的接口名称相同。顺序可以不一致。
- 当两个模块导出的接口名称相同时,就可以使用as重新定义名称。
//该js文件处于js/module1.js
let a = "六十元";
let b = 20;
let c = function (){
alert("嗨喽!我叫"+name+",今年"+age+"岁。");
}
export {a as name,b as age,c as sayHi} //导出
//该文件处于index.html
import {name as a,age as b,sayHi as c} from "js/module1.js";//导入module1.js文件中导出的接口
alert(a);//输出六十元
alert(b);//输出20
c();//输出嗨喽!我叫六十元,今年20岁。
2.2:import特点
- 只读属性:不允许修改导入的接口的引用指向,注意所有的基本类型都不可修改!但是可以修改如对象类型的变量的属性值。
let animal = class Animal{ //声明一个命名类
constructor(){//构造函数
alert("父类Animal!");
}
}
export {animal} //导出
import {animal as Animal} from "module1.js";
let animal = new Animal();//父类Animal!
animal.age = 3;
alert(animal.age);//3
Animal= class {}//TypeError: Assignment to constant variable.
- 单例模式:多次重复执行同一import语句,那么只会执行一次,而不会执行多次。import同一模块时,声明不同接口引用,会声明对应变量,import只执行一次。
import { name } "./xxx.js";
import { name } "./xxx.js";
// 相当于 import { name } "./xxx.js";
import { name } from "./xxx.js";
import { age } from "./xxx.js";
// 相当于 import { name, age } from "./xxx.js";
- 静态特性执行:import命令是静态执行的,所以不能使用变量和表达式。
import { "f" + "oo" } from "./xxx.js";
// 报错
let module = "methods";
import { foo } from module;
// 报错
if (true) {
import { foo } from "method1";
} else {
import { foo } from "method2";
}
// 报错
2.3:export default命令
注意点:
- 在一个文件或一个模块中,export default命令仅有一个。
- export default 后面对应的是接口变量,不能是声明。
- 通过export default导出的接口,导入时不需要使用{}。
- 导入时,可以使用任意变量名接收。
let name = "六十元";
export default name; // 仅有一个
export default let age = 20;
// error,default 对应的是导出变量名,不能跟着变量声明语句
import b from "./xxx.js"; // 不需要加{}, 使用任意变量接收
2.4:复合使用
export和import可以在同一模块中一起使用。
export { name, age } from "./xxx.js";
// 约等于下面两段语句.
import { name, age } from "./xxx.js";
export { name, age };
/*特点1*/
// 普通改名
export { name as name2 } from "./xxx.js";
// 将 name 转导成 default
export { name as default } from "./xxx.js";
// 将 default 转导成 foo
export { default as name } from "./xxx.js";
/* 特点 2 */
export * from "./xxx.js";//导出全部
下面是一个模块的简单案例:
//js/module1.js
let animal = class Animal{ //声明一个命名类
constructor(){//构造函数
alert("父类Animal!");
}
}
export {animal} //导出
//js/module2.js
import {animal as Animal} from "./module1.js";//导入module1.js文件中的Animal类
let dog = class Dog extends Animal {//继承Animal类
constructor(name,age) {
super();
this.name = name;
this.age = age;
}
sayHello(){
alert("哈喽,我是宠物狗,我的名字是:"+this.name+",今年"+this.age+"岁了。");
}
}
export {dog}//导出dog子类
//js/module3.js
let sayHi = function (){
alert("哈喽!");
}
export default sayHi;//默认导出sayHi
let run = function(){
alert("开始跑!");
}
let call = function (){
alert("大声喊叫!");
}
export {run,call}
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script type="module">
import {dog as Dog} from "./js/module2.js";//导入module3.js文件中的dog子类
import hello,{run,call} from "./js/module3.js";
let dog = new Dog("小黑",3);//创建子类dog
dog.sayHello();
hello();
run();
call();
</script>
</body>
</html>
结束语
今天ES6中的class类和模块就分享到这里了,下面还会陆续更新ES6中的其他内容。谢谢大家的观看!