ECMAScript6
Posted wodeqiyuan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ECMAScript6相关的知识,希望对你有一定的参考价值。
七、ES6模块和Class
1.ES6模块
在早期,javascript并没有模块体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。在ES6之前,社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种。前者用于服务器,后者用于浏览器。
ES6在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。
一个模块就是一个独立的文件,外部无法获取该文件内部的所有变量,如果希望外部能够读取模块内部的某个变量,就必须使用export关键字将该变量与外部建立连接,例如:
//这是一个JS文件 let firstName = ‘Michael‘; let lastName = ‘Jackson‘; let year = 1958; function multiply(x, y){ return x * y; }; export {firstName, lastName, year, multiply};
在一个模块中,export可以调用多次。需要注意的是,export命令规定,对外接口必须与模块内部变量建立对应关系,不能直接导出一个值。
使用export命令定义了模块的对外接口以后,其他JS文件就可以通过import命令加载这个模块了,下面列举了几种常用方式:
解构导入
import {firstName, lastName, year} from ‘./profile‘;
重命名变量
import { lastName as surname } from ‘./profile‘;
重复导入
import {name} from ‘./module1‘;
import {age} from ‘./module1‘;
如果多次重复执行同一句import语句,那么只会执行一次模块代码。
模块的整体加载
import * as person from ‘./module1‘
使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载,但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出,如:export default function () { console.log(‘foo‘); }
其他模块加载该模块时,import命令可以为该匿名函数指定任意名字:
import customName from ‘./export-default‘;
customName(); //‘foo’(执行成功)
显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面不用加大括号,因为只可能对应一个方法或者对象。
如果在一个模块中,先输入后输出同一个模块,import语句就可以与export语句写在一起,如:
export { foo, bar } from ‘my_module‘;
//等同于
import { foo, bar } from ‘my_module‘;
export { foo, bar };
2.Class
JavaScript语言中,生成实例对象的传统方法是通过构造函数。ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
基本上,ES6的class可以看作是一个语法糖(更方便易用的语法特性),它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更加像面向对象编程的语法而已,所以ES6的类完全可以看作构造函数的另一种写法。
类的实例属性可以定义在构造函数中,例如:
class Person{ constructor(id,name,age){ this.id = id; this.name = name; this.age = age; } }
直接在类上定义的属性是静态属性,因为ES6明确规定,Class内部只有静态方法,没有静态属性,例如:
class Foo { } Foo.prop = 1; Foo.prop; //1
在类中可以直接定义方法,实际上类的所有方法都定义在类的prototype属性上面。在类的实例上面调用方法,其实就是调用原型上的方法:
class Point {
constructor() { ... }
toString() { ... }
toValue() { ... }
}
由于类的方法都定义在prototype对象上面,所以类的新方法可以添加在prototype对象上面。Object.assign方法可以很方便地一次向类添加多个方法,例如:
class Point {
constructor(){ ... }
}
Object.assign(Point.prototype, {
toString() { ... },
toValue() { ... }
});
constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。类必须使用new调用,否则会报错,这是它跟普通构造函数的一个主要区别,后者不用new也可以调用执行。
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为静态方法,例如:
class Foo { static classMethod() { return ‘hello‘; } } Foo.classMethod(); //‘hello‘
如果静态方法包含this关键字,那么这个this指的是类,而不是实例。
class可通过extends关键字实现继承(相当于ES5中通过修改原型链实现继承),例如:
class Animal { constructor(name){ this.name = name; } sayName(){ console.log(“my name is”,this.name); } } class Dog extends Animal{ }
子类必须在constructor方法中调用super方法(相当于ES5中通过构造函数实现属性继承),否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其完善。如果不调用super方法,子类就得不到this对象。子类构造函数可以省略。在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。
super这个关键字,既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。
当做函数使用
子类B的构造函数之中的super(),代表调用父类的构造函数。super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B,因此super()在这里相当于A.prototype.constructor.call(this)。
对象
在普通方法中,指向父类的原型对象;在静态方法中,指向父类。由于super指向父类的原型对象,所以定义在父类实例上的方法或属性,是无法通过super调用的。
ES6规定,通过super调用父类的方法时,super会绑定子类的this。
super.print();
//等同于
super.print.call(this);
无法直接打印出super,因为无法得知super到底是函数还是对象。
类的prototype属性和__proto__属性
class作为构造函数的语法糖,同时有prototype属性和__proto__属性,因此同时存在两条继承链:
子类的__proto__属性,表示构造函数的继承,总是指向父类;
子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性,例如:
class A { } class B extends A { } B.__proto__ === A; //true B.prototype.__proto__ === A.prototype; //true
类的继承是按下面这种模式实现的:
class A { }
class B { }
//B的实例继承A的实例
Object.setPrototypeOf(B.prototype, A.prototype);
//B的实例继承A的静态属性
Object.setPrototypeOf(B, A);
let b = new B();
以上是关于ECMAScript6的主要内容,如果未能解决你的问题,请参考以下文章