您的位置:首页 > 其它

ECMAScript 6 类 应用

2016-03-07 16:08 281 查看
近些日子在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输入的模块变量,只是一个“符号连接”,所以这个变量是只读的。对它进行重新赋值会报错。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: