es6 学习心得

 

1、let/ const声明变量(项目中常用)

之前使用var关键字声明变量,无论声明在何处,都会被视为声明在它的作用域的最顶部(不在大括号内{}即在全局作用域的最顶部),而在es6之前JavaScript只有函数作用域和全局作用域。这就是变量提升。

 

console.log(a);  //输出undefined

if (true) {

    var a = 1;

}

 

上面的代码实际上是:

var a;

console.log(a); //输出undefined

if (true) {

    a = 1;

}

 

(1)let声明变量

let实际上为JavaScript新增了块级作用域。let声明的变量只在它所在的代码块内有效。

将上面变量提升的例子稍微变化一下:可以看出let声明的a只在代码块内有效。

if(true) {

    var a = 1;

}

console.log(a)  //输出1
if(true) {

    let a = 1;

}

console.log(a) //报错ReferenceError: a is not defined

 

再看一个常见的例子:

var a = [];

for (var i = 0; i < 10; i++) {

    a[i] = function () {

        console.log(i);

    };

}

a[6](); // 10

 

     每次循环的i指向的其实都是同一个i,很显然最后的结果是10。那么如何达到想要的效果?

     ·闭包(ES5)

var a = [];

for (var i = 0; i < 10; i++) {

    a[i] = (function (num) {

        return function () {

            console.log(num);

        };

    })(i)

}

a[6](); // 6

 

·let声明变量(ES6)

var a = [];

for (let i = 0; i < 10; i++) {

    a[i] = function () {

        console.log(i);

    };

}

a[6](); // 6

 

变量ilet声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量。

使用let声明变量的话,不存在变量提升的情况,必须在声明以后才使用,否则会报错,这在语法上称为“暂时性死区”。并且let不允许在相同作用域内重复声明变量。

(2)const声明变量

const声明的变量是一个只读变量,一旦声明就必须马上初始化并且不能改变值,因此如果用const只声明而不赋值也会报错。

const的作用域与let相同,只在当前的块级作用域内有效。同样的,也不能变量提升,存在暂时性死区,不能重复声明。

本质:

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

const foo = {};

 

// 为 foo 添加一个属性,可以成功

foo.prop = 123;

foo.prop // 123

 

// 将 foo 指向另一个对象,就会报错

foo = {}; // TypeError: "foo" is read-only

 

总结:因此,可以知道什么时候用let什么时候用const:值会改变的变量用let声明,值是一个不会改变的常量就用const声明。

 

2、箭头函数(项目中常用)

Es6使用“箭头”(=>)定义函数。箭头函数使得表达更简洁。

 

箭头函数的写法:

const fn = (a, b) => a + b;

 

 

就相当于ES5标准下的:

var fn = function (a, b) {

    return a + b;

}

 

箭头函数还可以与变量解构结合使用。

const full = ({ first, last }) => first + ' ' + last;

 

// 等同于

function full(person) {

    return person.first + ' ' + person.last;

}

 

使用箭头函数有几点需要注意。

(1)      函数体内的this对象,是定义时所在的对象,而不是使用时所在的对象。如果在箭头函数内使用this,那么该this是外层的this,换句话说,就是箭头函数里根本没有自己的this,而是引用外层的this。

(2)      不可以当做构造函数,也就是说,不可以使用new命令,否则抛出错误。

(3)      不可以使用arguments对象,该对象在函数体内不存在。如果一定要用,可以使用rest参数代替。

上面的第一点尤其要注意,this对象的指向是可变的,但在箭头函数中,它是固定的。

function foo() {

    setTimeout(() => {

        console.log('id:', this.id);

    }, 100);

}

var id = 21;

foo.call({ id: 42 });  // id: 42

 

上面代码中,setTimeout的参数是一个箭头函数,该箭头函数生效是在foo函数生成时,而它真正执行则是在100毫秒后。如果是普通函数,执行时的this执行全局对象window,输出21。而箭头函数的this总是绑定在定义生效时所在的作用域,因此输出42。

3、模板字符串

模板字符串解决了使用+号拼接字符串造成的不便利。模板字符串用(`)反引号标识,可以作普通字符串,也可以定义多行字符串,或者在字符串在嵌入变量。

// 普通字符串

`In JavaScript '\n' is a line-feed.`

 

// 多行字符串

`In JavaScript this is

 not legal.`

 

// 字符串中嵌入变量

let name = "Bob",

    time = "today";

`Hello ${name}, how are you ${time}?`

 

使用模板字符串表示多行字符串,所以的空格和缩进都会被保留在输出中。

使用${}包裹一个变量或表达式。

4、变量的解构赋值

(1)数组的解构赋值

[基本用法]

let [a, b, c] = [1, 2, 3];

 

上面代码表示。可以从数组中提取值,按照对应位置,给变量赋值。

本质上,这种写法属于“模式匹配”,只要等号两边模式相同,左边的变量就会被赋予对应的值。

如果解构不成功,变量的值就等于undefined。

let [foo] = [];

foo //undefined

let [bar, foo] = [1];

foo //undefined

 

另一种情况是不完全解构,即等号左边的模式只匹配一部分等号右边的数组,这种情况下,解构依然成功

let [x, y] = [1, 2, 3];

x // 1

y // 2

 

[默认值]

解构赋值允许制定默认值。

let [foo = true] = [];

foo // true

 

es6内部使用严格相等运算符(===),判断一个位置是否有值。只有数组成员严格等于undefined,默认值才会生效。

let [x, y = 'b'] = ['a']; // x='a', y='b'

let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

let [x = 'a'] = [null];

x // null

 

null不严格等于undefined,因此默认值不生效

(2)对象的解构赋值

解构不仅可以用于数组,还可以应用于对象。

let { bar, foo } = { foo: "aaa", bar: "bbb" };

foo // "aaa"

bar // "bbb"

 

 等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取值完全没有影响。

let { baz } = { foo: "aaa", bar: "bbb" };

baz // undefined

 

变量没有对应的同名属性,导致取不到值,最后等于undefined

对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

let { foo: baz } = { foo: "aaa", bar: "bbb" };

baz // "aaa"

foo // error: foo is not defined

 

foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo

(3)字符串的解构赋值

const [a, b, c, d, e] = 'hello';

a // "h"

b // "e"

c // "l"

d // "l"

e // "o"

 

还可以对字符串的length属性解构赋值

let { length: len } = 'hello';

len // 5

 

(4)数值和布尔值的解构赋值

解构赋值是,登等号右边是数值或布尔值,则会先转为对象。

 let { toString: s } = 123;

s === Number.prototype.toString // true

 

let { toString: s } = true;

s === Boolean.prototype.toString // true

 

(5)函数参数的解构赋值

函数的参数也可使用解构赋值。

function add([x, y]) {

    return x + y;

}

add([1, 2]); // 3

 

(6)用途

变量的解构赋值的用途:

·交换变量的值

let x = 1;

let y = 2;

[x, y] = [y, x];

 

·从函数返回多个值

函数只能返回一个值,如果要返回多个值,只能把他们放在数组或对象里返回。有了解构赋值,取出这些值非常方便。

// 返回一个数组

function example() {

    return [1, 2, 3];

}

let [a, b, c] = example();

 

// 返回一个对象

function example() {

    return {

        foo: 1,

        bar: 2

    };

}

let { foo, bar } = example();

 

·函数参数的定义

解构赋值可以方便地将一组参数与变量名对应。

// 参数是一组有次序的值

function f([x, y, z]) { ... }

f([1, 2, 3]);

 

// 参数是一组无次序的值

function f({ x, y, z }) { ... }

f({ z: 3, y: 2, x: 1 });

 

·提取数据

解构赋值对提取JSON对象中的数据尤其有用。

let jsonData = {

    id: 42,

    status: "OK",

    data: [867, 5309]

};

 

let { id, status, data: number } = jsonData;

console.log(id, status, number);

// 42, "OK", [867, 5309]

 

·函数参数的默认值

指定参数的默认值,就避免了在函数内部再写var foo = config.foo || ‘default foo’。

jQuery.ajax = function (url, {

    async = true,

    beforeSend = function () {},

    cache = true,

    complete = function () {},

    crossDomain = false,

    global = true,

    // ... more config

}) {

    // ... do stuff

};

 

5、函数参数的默认值

(1)默认用法

ES6之前不能给函数参数指定默认值,只能采用变通的方法。

function log(x, y) {

    y = y || 'World';  

    console.log(x, y);

}

 

log('Hello') // Hello World

log('Hello', 'China') // Hello China

log('Hello', '') // Hello World

 

上面的代码检查函数的参数y有没有赋值,如果没有,则指定默认值为World。但这样的缺点在于,如果参数y赋值了,但对应的布尔值为false,则该赋值不起作用。如上面代码最后一行,y等于空字符,结果还是被改为默认值。

为避免这个问题,通常需先判断参数y是否被复制,如果没有,再等于默认值。

if (typeof y === 'undefined') {

    y = 'World';

}

 

 

Es6允许函数的参数设置默认值,直接写在参数定义的后面。

function log(x, y = 'World') {

    console.log(x, y);

}

 

log('Hello') // Hello World

log('Hello', 'China') // Hello China

log('Hello', '') // Hello

 

 

(2)与解构赋值默认值结合使用

函数参数默认值可以与解构赋值的默认值,结合起来使用。

function foo({ x, y = 5 }) {

    console.log(x, y);

}

 

foo({}) // undefined 5

foo({ x: 1 }) // 1 5

foo({ x: 1, y: 2 }) // 1 2

foo() // TypeError: Cannot read property 'x' of undefined

 

上面的代码只使用了对象的解构赋值默认值,没有使用函数参数的默认值。只当函数foo的参数是一个对象时,变量x和y才会通过解构赋值生成。如果函数调用时没提供参数,变量x和变量y就不会生成,因此报错。提供函数的默认值就可以避免这种情况。

function foo({ x, y = 5 } = {}) {

    console.log(x, y);

}

 

foo() // undefined 5

 

上面的代码指定,没有提供参数,函数的参数默认为一个空对象。

// 写法一

function m1({ x = 0, y = 0 } = {}) {

    return [x, y];

}

 

// 写法二

function m2({ x, y } = { x: 0, y: 0 }) {

    return [x, y];

}

 

上面两张写法都对函数的参数设定了默认值,区别是写法一函数参数的默认值是空对象,但设置了对象解构赋值的默认值;写法二函数参数的默认值是一个有具体属性的对象,但是没有设置对象解构赋值的默认值。

// 函数没有参数的情况

m1() // [0, 0]

m2() // [0, 0]

 

// x 有值,y 无值的情况

m1({ x: 3 }) // [3, 0]

m2({ x: 3 }) // [3, undefined]

 

// x 和 y 都无值的情况

m1({}) // [0, 0];

m2({}) // [undefined, undefined]

 

(3)作用域

设置了参数的默认值,函数进行声明初始化时,参数会形成单独的作用域,初始化结束,则该作用域消失。

var x = 1;

function f(x, y = x) {

    console.log(y);

}

f(2) // 2

 

上面代码,y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里,默认值变量x指向第一个参数x,而不是全局变量x,因此输出的是2。

let x = 1;

function f(y = x) {

    let x = 2;

    console.log(y);

}

f() // 1

 

上面代码,函数f调用时,参数y=x形成一个单独作用域,在这个作用域里,变量x本身无定义,所以指向外层的全局变量x。函数调用时,函数体内部的局部变量x不影响默认值变量x。

var x = 1;

function foo(x = x) {

    // ...

}

foo() // ReferenceError: x is not defined

 

上面代码,参数x=x形成单独作用域,实际执行的是let x = x,由暂时性死区的原因,这行代码会报错“x未定义”。

如过参数的默认值是一个函数,该函数的作用域也遵守这个规则。

let foo = 'outer';

function bar(func = () => foo) {

    let foo = 'inner';

    console.log(func());

}

bar(); // outer

 

上面代码,函数bar的参数func的默认值是一个匿名函数,返回值为变量foo。函数参数形成的单独作用域里,没有定义变量foo,因此foo指向外层的全局变量foo,因此输出outer。

var x = 1;

function foo(x, y = function () { x = 2; }) {

    var x = 3;

    y();

    console.log(x);

}

foo() // 3

x // 1

 

上面代码,函数foo的参数形成一个单独作用域。改作用域里,首先声明变量x,然后声明变量y,y的默认值是一个匿名函数。这个匿名函数内部的变量x,指向同一个作用域的第一个参数x。函数foo内部又声明了一个内部变量x,该变量与第一个参数x由于不是同一个作用域,所以不是同一个变量,因此执行y后,内部变量x和外部全局变量x的值都没变。

var x = 1;

function foo(x, y = function () { x = 2; }) {

    x = 3;

    y();

    console.log(x);

}

foo() // 2

x // 1

 

如果将var x = 3的var去除,函数foo的内部变量x就指向第一个参数x,与匿名函数内部的x是一致的,最后输出2,而外层的全局变量x不受影响。

6、rest参数

引入rest参数(…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象。Rest参数搭配的变量是一个数组,将多余的参数放入数组中。

function add(...values) {

    let sum = 0;

    for (var val of values) {

        sum += val;

    }

    return sum;

}

add(2, 5, 3) // 10

 

add函数是一个求和函数,利用rest参数,可以向该函数传入任意数目的参数。

// arguments变量的写法

function sortNumbers() {

    return Array.prototype.slice.call(arguments).sort();

}

 

// rest参数的写法

const sortNumbers = (...numbers) => numbers.sort();

 

上面是rest参数代替arguments变量的例子。比较后可发现,rest参数的写法更自然更简洁。

arguments对象不是数组,是类似数组的对象,为使用数组方法,需先将其转为数组。Rest参数就不存在这个问题,它自身就是数组。

// arguments变量的写法

function sortNumbers() {

    return Array.prototype.slice.call(arguments).sort();

}

 

// rest参数的写法

const sortNumbers = (...numbers) => numbers.sort();

 

上面是利用rest参数改写数组push方法的例子。

需注意。Rest参数之后不能再有其他参数,即只能是最后一个参数,否则报错。

函数的length属性返回没有默认值的参数的个数,其中不包括rest参数。

(function (a) { }).length  // 1

(function (...a) { }).length  // 0

(function (a, ...b) { }).length  // 1

 

7、对象的扩展

(1)Es6中可以直接写入变量和函数,作为对象的属性和方法,使书写更简洁。

const foo = 'bar';

const baz = { foo };

baz // {foo: "bar"}

 

// 等同于

const baz = { foo: foo };

 

此时,属性名为变量名,属性的值为变量的值。

function f(x, y) {

    return { x, y };

}

 

// 等同于

function f(x, y) {

    return { x: x, y: y

    };

}

 

f(1, 2) // Object {x: 1, y: 2}

 

方法也可以简写。

在一个模块对外提供接口时非常适合使用简洁写法。

const getName = () => person.name;

const getAge = () => person.age;

 

commons的写法:

 

module.exports = {

    getName: getName,

    getAge: getAge,

};

 

Es6 modules的写法:

export default {

    getName,

    getAge

};

 

(2)若自适应字面量方法定义对象(使用大括号),es5中只能像下面这样定义属性:

var obj = {

    foo: true,

    abc: 123

};

 

在es6中,就可以把表达式放在方括号内定义对象:

let propKey = 'foo';

let obj = {

    [propKey]: true,

    ['a' + 'bc']: 123

};

 

8、class

     生成实例对象的传统方法使通过构造函数,与传统的面向对象语言差异很大,es6引入class类这个概念,通过class关键字可以定义类。Es6的class可以看作是一个语法糖,它的绝大部分功能,es5都可以做到,class写法只是让对象的写法更清晰。

es5生成实例对象:

function Person(name, age) {

    this.name = name;

    this.age = age;

}

Person.prototype.getName = function () {

    return this.name;

}

var person = new Person('abc', 21);

 

 

es6用class改写

class Person {

    constructor(name, age) {

        this.name = name;

        this.age = age;

    }

 

    getName() {

        return this.name

    }

}

var person = new Person('abc', 21);

 

constructor是构造方法,this代表实例对象,es5的构造函数Person,对应的就是Person类的构造方法,定义类方法使不需要function关键字,直接把函数定义放进去,另外方法之间也不需要逗号分隔。

typeof Person // "function"

Person === Person.prototype.constructor // true

 

上面表明,类的数据类型就是函数,类本身事项构造函数。

class Person {

    constructor() {

        // ...

    }

    getName() {

        // ...

    }

 

}

 

// 等同于

Person.prototype = {

    constructor() {},

    getName() {}

};

 

类的所有方法都定义在类的prototype属性上。因此,类的新方法可以添加在prototype对象上。而Object.assign方法可以仿版地一次想类添加多个方法。

Object.assign(Person.prototype, {

    getNmae() {},

    getAge() {}

});

 

9、class的继承

      1)Es6中,class可以通过extends关键字实现继承,比起es5的通过修改原型链实现继承方便的多。

 

class Person {

    constructor(name, age) {

        this.name = name;

        this.age = age;

    }

 

    getName() {

        return this.name

    }

}

 

// Student类继承Person类

class Student extends Person {

    constructor(name, age, gender, classes) {

        super(name, age);

        this.gender = gender;

        this.classes = classes;

    }

 

    getGender() {

        return this.gender;

    }

}

 

不用像es5那样需要考虑构造函数继承哈斯原型继承,只需要使用extends关键字并关注一个叫super的方法。

如果不使用super方法,在新建实例时就会报错。

Es5的继承,实质是县创造子类实例对象的this,在将父类的方法添加到this(Parent.apply(this))。    而es6的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类构造函数修改this。

如果子类没有定义constructor(),也会被默认添加。即任何一个子类都有constructor方法。

class Student extends Person {}

 

// 等同于

class Student extends Person {

    constructor(...args) {

        super(...args);

    }

}

 

需要注意,只有调用super以后才能使用this,否则报错。因为上面说过,子类实例的构建,是基于父类实例加工,super方法返回父类实例。

1) super关键字既可以当作函数使用也可以当作对象使用。

·Super作为函数调用时,代表父类的构造函数,子类的构造数必须执行一次super函数。

class A {}

 

class B extends A {

    constructor() {

        super();

    }

}

 

Super虽然代表了父类A的工业早函数,但返回的是子类B的实例,即super内部的this指的是B,因此super在这里相当于A.prototype.constructor.call(this)。

class A {

    constructor() {

        console.log(new.target.name);

    }

}

class B extends A {

    constructor() {

        super();

    }

}

new A() // A

new B() // B

 

new.target指向当前正在执行的函数。从上面的代码可以看出:在super执行时,它指向的是子类B的构造函数,即super内部的this指向B

并且,作为函数是,super只能用在子类的构造函数之中,否则报错。

·第二种情况,super作为对象。在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

class A {

    p() {

        return 2;

    }

}

 

class B extends A {

    constructor() {

        super();

        console.log(super.p()); // 2

    }

}

 

let b = new B();

 

上面代码中,子类B的super.p就是把super当做对象使用,此时super在普通方法中,指向A.prototype,因此super.p()就相当于A.prototype.p()。

注意:定义在父类实例上的方法、属性无法通过super调用。

class A {

    constructor() {

        this.x = 1;

    }

    print() {

        console.log(this.x);

    }

}

 

class B extends A {

    constructor() {

        super();

        this.x = 2;

    }

    m() {

        super.print();

    }

}

 

let b = new B();

b.m() // 2

 

super.print()虽然调用的是A.prototype.print(),但A.prototype.print()内部的this指向子类B,因此输出2,而不是1。实际上执行的是super.print.call(this)。

如果super作为对象,用在静态方法中,super就指向父类,而不是父类的原型对象。

class Parent {

    static myMethod(msg) {

        console.log('static', msg);

    }

 

    myMethod(msg) {

        console.log('instance', msg);

    }

}

 

class Child extends Parent {

    static myMethod(msg) {

        super.myMethod(msg);

    }

 

    myMethod(msg) {

        super.myMethod(msg);

    }

}

 

Child.myMethod(1); // static 1

 

var child = new Child();

child.myMethod(2); // instance 2

 

上面的代码,super在静态方法中指向父类,在普通方法中指向父类的原型对象。

10、promise(项目中ajax.js中使用)

promise的作用与回调方法(callback)一样,都是在某种情况下执行设定好的方法。但promise的多重链式调用能使代码更整洁,避免出现“回调地狱”(回调嵌套太多层)。在es6中,promise成为了原生对象可以直接使用。

 

Ajax请求的传统写法:

 

 

改为promise写法:

 

很显然,promise的写法把异步调用中使用回调函数的场景改为了.then()、.catch()等函数链式调用的方式,基于promise可以把复杂的异步调用方式进行模块化。

Promise的原理分析:

Promise对象共有三个状态,分别是:

·pending(进行中)

·resolved(已完成,又称为fullfilled)

·rejected(已失败)

由异步操作的结果决定当前是什么状态。状态的改变只有两种可能:

·从pending变为fullfilled

·从pending变为rejected

只要这两种情况发生,状态就不会再改变了,因此状态是不能逆向改变的。

构建promise:

 

Promise的构造函数接受一个函数作为参数,该函数的两个参数分别为resolve和reject两个函数。

·Resolve函数将promise对象的状态由pending变为resolved,异步操作成功时调用,并将异步操作的结果,作为参数传递出去。

·reject函数将状态由pending变为rejected,异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

  Promise的实例方法:

Promise对象拥有两个实例方法then()和catch()。

·then()方法接受两个函数作为参数,第一个是状态变为resolved时调用,第二个则是状态变为rejected是调用,第二个函数是可选的。

Promise实例生成以后,可以用then方法指定resolved状态和rejected状态的回调函数,即成功和失败的回调函数。

promise构造函数中通常都是异步的,所以then方法往往都先于resolvereject方法执行。这两个函数作为成功和失败的回调函数,都接受promise对象传出的值作为参数。

 

 

then()方法将返回一个新的promise。

因此then可以链式调用,在新的对象上添加失败或成功的回调。

 

·catch()方法的作用是捕获promise的错误。

与then()方法的rejected回调作业几乎一致。

我们知道,如果 Promise 状态已经变成resolved,再抛出错误是无效的。

 

上面代码中,Promise 在resolve语句后面,再抛出错误,不会被捕获,等于没有抛出。因为 Promise 的状态一旦改变,就永久保持该状态,不会再变了。

 

 

promise对象的错误会一直向后传递,直到被捕获,即错误总会被下一个catch所捕获。then方法指定的回调函数,若抛出错误,也会被下一个catch捕获。catch中也能抛错,则需要后面的catch来捕获。

因此一般来说,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。

这样就能够在下一个catch()中统一处理这些错误。同时catch()也能够捕获then()中抛出的错误,所以建议不使用then()的rejected回调,而是统一使用catch()来处理错误。

跟传统的try/catch代码块不同的是,如果没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。Promise 内部的错误不会影响到 Promise 外部的代码。

11、Module(项目中常用)

ES6实现了模块功能,可以取代CommonJS和AMD规范,称为浏览器和服务器通用的模块解决方案。

模块功能主要由两个命令构成:export和import。

Export用于规定模块的对外接口,import用于输入其他模块提供的功能。

一个模块即一个独立文件。该文件内部的所有变量,在外部是无法获取的。如果需要读取模块内的某个变量,就必须使用export输出该变量。

(1)export

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 };

 

除了输出变量,还可以输出函数或class类

export function multiply(x, y) {

    return x * y;

};

 

(2)import

使用export定义了,模块对外的几口以后,其他js文件就可以通过import加载这个模块。

// main.js

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

 

function setName(element) {

    element.textContent = firstName + ' ' + lastName;

}

 

Import接收一堆大括号,里面指定要从其他模块导入的变量名,变量名必须与被导入模块(profile.js)对外接口的名称相同。

Import输入的变量都是只读的,不允许在加载模块的脚本里改写接口。

From指定模块文件的位置,.js后缀可以省略。

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

(3)export default

使用export default为模块指定默认输出。

// export-default.js

export default function () {

    console.log('foo');

}

 

默认输出一个匿名函数

// import-default.js

import customName from './export-default';

customName(); // 'foo'

 

加载该模块时,import为函数指定名字。

需要注意的是export default对应的import不需要使用大括号{}。而export对应的import需要使用大括号。

 

posted @ 2018-02-24 17:32  zi_chil  阅读(576)  评论(0编辑  收藏  举报