二、面向对象与闭包
2.1 面向对象
2.1.1 类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
introduce() {
console.log(`我的名字是${this.name},今年${this.age}岁`)
}
}
class Student extends Person {
constructor(name, age, grade, mhobby) {
super(name, age);
this.grade = grade;
this.mhobby = mhobby;
}
introduce() {
super.introduce();
console.log(`读${this.grade}年级,爱好${this.mhobby}`)
}
set hobby(value) {
this.mhobby = value;
}
get hobby() {
return this.mhobby;
}
}
let someone = new Student("小明", 12, 5);
someone.hobby = '打球'
someone.introduce();
/*-----输出-----*/
//我的名字是小明,今年12岁
//读5年级,爱好打球
2.1.2 属性表达式
let methodName = 'getArea';
class Square {
constructor(length) {
// ...
}
[methodName]() {
// ...
}
}
上面代码中,Square类的方法名getArea,是从表达式得到的。
2.1.3 class 表达式
采用 Class 表达式,可以写出立即执行的 Class
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}('张三');
person.sayName(); // "张三"
2.1.4 静态方法
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
注意:
- 如果静态方法包含
this关键字,这个this指的是类,而不是实例 - 静态方法可以与非静态方法重名
- 父类的静态方法,可以被子类继承
2.1.5 静态属性
// 老写法
class Foo {
// ...
}
Foo.prop = 1;
// 新写法
class Foo {
static prop = 1;
}
2.1.6 实例属性的新写法
class foo {
bar = 'hello';
baz = 'world';
constructor() {
// ...
}
}
一眼就能看出,foo类有两个实例属性,一目了然。另外,写起来也比较简洁。
2.2 闭包
-
对于底层来说,闭包是一个栈,闭包可以使函数执行完这个栈也不回收。
-
对于高层来说,闭包是函数当作对象处理
2.3 ES6 模块化
浏览器还不支持,但是可以用 webpack
npm i webpack -g #-g 全局安装//webpack.config.js const path = require('path') //node 导入模块方式 module.exports={ //production 生产模式、development 开发模式 mode:'production' //同级目录必须加 ./ import同级目录也要加 ./ entry:'./index.js' output:{ //__dirname 当前脚本的工作目录 path:path.resolve(__dirname,'build'), filename:'bundle.js' } }
2.3.1 export
- 可输出变量、函数、类
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export { firstName, lastName, year };
- as 关键字
as 用来重命名对外的接口,并且重命名后可以输出两次
function v1() { ... }
function v2() { ... }
export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};
- 动态更新
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);
2.3.2 import
as关键字,动态更新同样适用。动态更新较难排查问题,因此 import 的变量尽量只作只读处理,不要轻易更改它的值
// main.js
import { firstName, lastName, year as time } from './profile.js';
//as 关键字同样适用
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
2.3.3 模块的整体加载
即用星号(
*)指定一个对象,所有输出值都加载在这个对象上面
import * as profile from './profile.js';
2.3.4 export default 命令
本质上,
export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字
// export-default.js
export default function () {
console.log('foo');
}
// import-default.js
import customName from './export-default';
customName(); // 'foo'
2.3.5 模块的继承
假设有一个
circleplus模块,继承了circle模块
// circleplus.js
export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
//注意,export *命令会忽略 circle 模块的default方法
// main.js
import * as math from 'circleplus';
import exp from 'circleplus';
console.log(exp(math.e));
2.3.6 跨模块常量
// constants.js 模块
export const A = 1;
export const B = 3;
export const C = 4;
// test1.js 模块
import * as constants from './constants';
console.log(constants.A); // 1
console.log(constants.B); // 3
// test2.js 模块
import {A, B} from './constants';
console.log(A); // 1
console.log(B); // 3
2.3.7 import()
// 报错
if (x === 2) {
import MyModual from './myModual';
//不支持动态加载
}
import()函数,支持动态加载模块
//import()返回一个 Promise 对象
const main = document.querySelector('main');
import(`./section-modules/${someVariable}.js`)
.then(module => {
module.loadPageInto(main);
})
.catch(err => {
main.textContent = err.message;
});
import()函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,就会加载指定的模块。import()函数与所加载的模块没有静态连接关系,这点也是与import语句不相同。import()类似于 Node 的require方法,区别主要是前者是异步加载,后者是同步加载