ES6 class类与模块

前言

今天给大家介绍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中的其他内容。谢谢大家的观看!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值