ECMAScript 6 类 应用

近些日子在git上经常看到以es6编写的js代码。

以前有些了解,但没有深入学习。

现在用的人多了,也就找了些时间来学习了下。


应用:

在webpack 中会使用es6。

当然肯定需要模块来翻译成es5的代码:babel


es6文档看了下,我觉得对于类模块这块用处比较大。

在现实中使用也简单方便些。


js 语言传统是通过构造函数,定义并生成新对象。

在es6中引用了class 类。作为对象的模板。通过class关键字,定义类。

新的class写法只是让对象原型的写法更新清晰、更像面向对象编程的语法而以。

和后端写法类似了。

//定义类
class Point {

  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }

}
constructor j 是构造函数,this 表示实例对象。tostring 是类的方法。

方法的定义  实现是扩展了Point.prototype

实例对象:

var a=new Point(参数)

Point .name  //Point


Class 表达式:

const MyClass = class Me {
  getClassName() {
    return Me.name;
  }
};
以上创建了一个类,类名是MyClass而不是Me. me只能在类的内部代码可用。

let inst = new MyClass();
inst.getClassName() // Me
当然me是可以省略的

const MyClass = class { /* ... */ };
采用class 表达式,可以写出立即执行的class

let person = new class {
  constructor(name) {
    this.name = name;
  }

  sayName() {
    console.log(this.name);
  }
}("张三");

person.sayName(); // "张三"
class 不存在变量提升,


class 继承

class之间可以通过extends关键字实现继承。

class ColorPoint extends Point {}
上面定义了一个colorpoint 类,继承了point类的所有属性和方法。

但是由于没有部署任何任何,所以这两个类完全一样。等于复制了一个point。

class ColorPoint extends Point {

  constructor(x, y, color) {
    super(x, y); // 调用父类的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 调用父类的toString()
  }

}
上面代码定义了新的构造函数,和tostring方法。

方法内的super指向父类的方法。


class 的取值函数get和存值函数set

class MyClass {
  constructor() {
    // ...
  }
  get prop() {
    return 'getter';
  }
  set prop(value) {
    console.log('setter: '+value);
  }
}

let inst = new MyClass();

inst.prop = 123;
// setter: 123

inst.prop


class的静态方法

class Foo {
  static classMethod() {
    return 'hello';
  }
}

Foo.classMethod() // 'hello'

var foo = new Foo();
foo.classMethod()
// TypeError: undefined is not a function

staitc可以创建静态方法或属性。


模块的定义和引用。

js一直没有模块体系,无法将一个大程序拆分成互相依赖的小文件。

在es6之前社区制定了一些模块加载方案,最主要的有commonjs 和amd 两种。

前者用于服务器,后者用于浏览器

es6实现了模块功能,完全可以取代commonjs amd规范。


es6模块的设计思想,是尽量的静态化。使得编译时就能确定模块的依赖关系。

以及输入和输出的变量。

// CommonJS模块
let { stat, exists, readFile } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat, exists = _fs.exists, readfile = _fs.readfile;

上面代码的实质是整体加载fs模块,生成一个对象。然后从这个对象上获取三个方法。这种加载为“运行时加载”

因为只有运行时才能得到这个对象,导致完全没有办法 在编译时做“静态优化”


es6模块不是对象,而是通过export命令显式指定输出的代码,输出时采用静态命令的形式。

// ES6模块
import { stat, exists, readFile } from 'fs';
上面是加载fs模块的三个方法,其他方法不加载。

称为“编译加载”,即es6在编译时就完成模块加载。

当然这也导致了没法引用es模块本身,因为它不是对象。


export 命令

模块功能主要由export 和Import 。

export 命令用于规定模块的对外接口。

Import 命令用于输入其他模块提供的功能。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。

如果你希望外部能够读取模块内部的某个变量,就必须使用export 输出该变量。

// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
对外输出三个变量。

// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

export {firstName, lastName, year};
也可以这样写。

使用大括号指定要输出的一组变量。

export function multiply (x, y) {
  return x * y;
};
还可以输出函数或类。


function v1() { ... }
function v2() { ... }

export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};

export输出的变量就是本来的名字,但是 可以使用as 重合名。

export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);
export语句输出的值是动态绑定,绑定其所在的模块。

上在代码输出变量foo 什为bar ,500ms后变成baz


import 命令

js文件可通过import命令加载这个模块。

// main.js

import {firstName, lastName, year} from './profile';

function setName(element) {
  element.textContent = firstName + ' ' + lastName;
}
import 命令接受一个对象,里面指定要多其他模块导入的变量名。

大括号里的变量名,必须与被导入模块对外接口名称相同。

import { lastName as surname } from './profile';
as 可将输入的的名称,从命名。

import 命令具有提升效果,会提升到整个模块的头部,首先执行。

foo();

import { foo } from 'my_module';
上面代码不会报错,因为Import 的执行早于foo的调用。

export { es6 as default } from './someModule';

// 等同于
import { es6 } from './someModule';
export default es6;
模块中可以先输入加载模块,再输出此模块

export 输出和import输入可以结合一起。

import 'lodash'

模块的整体加载

除了指定加载某个输出值,还可以使用整体加载,即用* 指定一个对象,所有输出值都加载在这个对象上面。

export function area(radius) {
  return Math.PI * radius * radius;
}

export function circumference(radius) {
  return 2 * Math.PI * radius;
}
输出三个方法。

// main.js

import { area, circumference } from './circle';

console.log("圆面积:" + area(4));
console.log("圆周长:" + circumference(14));
 上面写法是指定要加载的方法,整体加载如下:

import * as circle from './circle';

console.log("圆面积:" + circle.area(4));
console.log("圆周长:" + circle.circumference(14));

export default 命令

import命令使用时,用户需要知道所需加载的变量名,否则无法加载。

但为了方便,可以用default 命令,为模块指定默认输出。

// export-default.js
export default function () {
  console.log('foo');
}
默认输出一个函数

// import-default.js
import customName from './export-default';
customName(); // 'foo'
加载时import 可以为默认函数指定任意的名称。

需要注意的是,这时Import命令后面,不使用大括号 。

// export-default.js
export default function foo() {
  console.log('foo');
}

// 或者写成

function foo() {
  console.log('foo');
}

export default foo;
default 命令用在非匿名函数前,也是可以的。

// 输出
export default function crc32() {
  // ...
}
// 输入
import crc32 from 'crc32';

// 输出
export function crc32() {
  // ...
};
// 输入
import {crc32} from 'crc32';
// modules.js
function add(x, y) {
  return x * y;
};
export {add as default};
// 等同于
// export default add;

// app.js
import { default as xxx } from 'modules';
// 等同于
// import xxx from 'modules';
本质上export default 就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字。

// 正确
export var a = 1;

// 正确
var a = 1;
export default a;

// 错误
export default var a = 1;
因为export default 命令其实中是输出一个叫做default的变量,所以它后面不能跟变量声明语句。

import customName, { otherMethod } from './export-default';
可以在一条import语句中,同时输入默认方法和其他变量。


模块的继承

模块间可以继承

// circleplus.js

export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
  return Math.exp(x);
}
以上继承了circle模块。

export * 表示输出circle 模块的所有属性和方法,当然它会忽略default。

export { area as circleArea } from 'circle';
可以将circle 改名后再输出 。




import * as math from "circleplus";
import exp from "circleplus";
console.log(exp(math.e));
加载模块再定义新引入对象,再获取模块的默认方法定义成exp


import * as math from "circleplus";
import exp from "circleplus";
console.log(exp(math.e));

import * as math from "circleplus";
import exp from "circleplus";
console.log(exp(math.e));

es6模块加载的实质

es6模块加载的机制,与commonjs模块完全不同。

commonjs模块输出的是一个值的拷贝,而es模块输出的是值的引用。

拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。

// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};

// main.js
var mod = require('./lib');

console.log(mod.counter);  // 3
mod.incCounter();
console.log(mod.counter); // 3
lib加载以后,它的内部变化就不影响输出的counter了。这是因为mod.counter是一个原始类型的值,会被缓存。


es6模块的运行机制,与commonjs不一样。

它遇到模块加载命令Import时,不会执行模块,而是只生成一个动态的只读引用。

等到真的需要用到时,再到模块里去取值。

es6输出有点像地址引用,原始值变了,输入的值也会变。

因此es6模块是动态引用,模块里的变量绑定其白发在的模块。

// lib.js
export let counter = 3;
export function incCounter() {
  counter++;
}

// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4

// m1.js
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);

// m2.js
import {foo} from './m1.js';
console.log(foo);
setTimeout(() => console.log(foo), 500);

// lib.js
export let obj = {};

// main.js
import { obj } from './lib';

obj.prop = 123; // OK
obj = {}; // TypeError
由于es6输入的模块变量,只是一个“符号连接”,所以这个变量是只读的。对它进行重新赋值会报错。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值