ECMAScript6

也就是ECMAScript2015

变量声明let

let变量不可重复声明

let a = 1
let a = 2 // Duplicate declaration 

块级作用域

// 在ES6之前,JS只有全局作用域和函数作用域
{
    var a = 'a变量';
}
console.log(a); // 可以正常输出


// ES6加入了块级作用域,如果想声明一个只在块作用域中有效的变量,需要使用let关键字
{
    let b = "变量b";
}
//console.log(b); // ReferenceError: b is not defined,let定义的变量的作用域位于块级作用域中

不存在变量提升

// 使用var定义变量,存在变量提升
// 以下代码不会报错
console.log(a)
var a = 100

// ES6 的let不存在变量提升
console.log(b) // ReferenceError: Cannot access 'b' before initialization
let b = 100

常量声明 const

值不能被修改的量称为常量

不可被修改

// 同样,在ES6之前,也没有常量(恒量)
const c = '变量c';
//c = '变量cc';
//console.log(c); // TypeError: Assignment to constant variable.

对数组和对象元素的修改,不算对常量的修改

const d = [];
d.push("路飞"); // 正确
d.push("索隆"); // 正确
d.push("山治"); // 正确
// d = []; // 不正确

const变量不可重复声明

const c = "变量c";
//const c = '新的变量c'; // SyntaxError: Identifier 'c' has already been declared  不能重复声明同一个值

在声明时就要被初始化

const c; // SyntaxError: Missing initializer in const declaration
c = 100;

约定:常量一般使用大写

const AMOUNT = 100;

块级作用于

同let一致

Destructuring 变量解构赋值

数组解构

// 数组的解构
function breakfast() {
    return ["🥛", "🍞", "🥓"];
}

// ES5 将breakfast中的元素一一赋值给一个变量
var temp = breakfast();
var milk = temp[0];
var bread = temp[1];
var bacon = temp[2];
console.log(milk, bread, bacon);

// ES6可以使用
let [milkN, breadN, baconN] = breakfast();
console.log(milk, bread, bacon);

// 忽略某些解构值
let [, , baconN] = breakfast();

对象解构

// 对象的解构
function sports() {
    return {football: '⚽', basketball: '🏀', pingPong: '🏓'};
}

// 将sports.football 赋值给变量footballN ...
let {football: footballN, basketball: basketballN, pingPong: pingPongN} = sports();
console.log(footballN, basketballN, pingPongN);

// 对象连续解构
let person = {
    name: "张三",
    age: 12,
    hobby: {
        name: "打篮球",
        start: new Date()
    }
}

let {hobby: {name}} = person
console.log(name); // 打篮球
console.log(hobby); // undefined

// 连续解构赋值且重命名
let {hobby: {name: myname}} = person
console.log(myname)

函数解构参数

// 函数的解构参数
// 给与解构参数默认值,否则不传会报错
function study(name, time, {location, classes} = {}) {
    console.log(name, time, location, classes); // 解构参数,可以直接获取对象中的值
}

study('李四', '明天'); // 李四 明天 undefined undefined
study('张三', '后天', {location: 'home', classes: 'Math'}); // 张三 后天 home Math

字符串 String

字符串包含

let str = '我是字符串A';
console.log(str.startsWith('我')); // true
console.log(str.endsWith('A')); // true
console.log(str.includes('字符')); // true

模板字符串

可有换行,会替换变量

// 模板字符串
// ES6提供了模板字符串

let dessert = '🍞'
drink = '🍺';
let breakfastMsg = `今天的早餐是${dessert},
    并且喝个${drink}`; // 可以换行
console.log(breakfastMsg);

带标签的模板字符串

// Tagged Template 带标签的模板
let money = 20;
let dessert = '🍞'
drink = '🍺';
let breakfastMsg2 = kitchen`今天的早餐是${dessert},并且喝个${drink}, 总共花费了${money}`;

// 这里kitchen函数是用于生成模板字符串的函数
// strings+values就是目标字符串,values是插值
// 通常用于字符串预处理,比如传入了一个html标签,去除标签样式,让其变为纯文本
function kitchen(strings,
                 ...
                     values
) {
    // console.log(strings); // [ '今天的早餐是', ',\n并且喝个', '' ]
    // console.log(values);  //[ '🍞', '🍺' ]
    let result = '';
    result += strings[0];
    result += values[0];
    result += strings[1];
    result += "一杯" + values[1];
    result += strings[2];
    result += values[2] + '元';
    return result;
}

console.log(breakfastMsg2); // 今天的早餐是🍞,并且喝个一杯🍺, 总共花费了20元

对象

对象简化写法:属性赋值简化

let name = "北京大学";
let change = function () {
    console.log("我们可以改变你");
}

// ES5,定义一个school对象
let school = {
    name: name,
    change: change,
}

// ES6,如果要赋值的变量与对象的key相同,可以省略
let school2 = {
    name,
    change
}

// 以上代码作用相同

对象简化写法:方法声明简化

let school = {
    name: "北京大学",
    change: function() {
      console.log("我们可以改变你");
    },
}

let school2 = {
  name: "北京大学",
  change() {
    console.log("我们可以改变你");
  }
}

// 以上两种写法等价

对象属性值的赋值

obj.height = 175;
obj['love girl'] = '莉莉';

Object对象

Object.is 判断值是否相同

这里包括了数据类型的判断,类似于 ===,但是===有些问题

// ES5中的==和===存在着问题,==会自动转换数据类型;===情况下NaN不等于NaN,+0等于-0
// ES6中的Object.is() 判断两个值是否相同。即只要两个值是一样的,它们就应该相等
Object.is(NaN, NaN)     //true
Object.is(+0, -0)       //false

Object.assign 属性复制

// 将primary对象中的所有属性复制到test1中
var test1 = {test: 'test'}; // 必须初始化,不可以是undefined或者null
var primary = {name: '李四'};
Object.(test1, primary);
console.log(test1); // { test: 'test', name: '李四' }
test1.name = "张三";
console.log(test1, primary); // { test: 'test', name: '张三' } { name: '李四' } // 原来的值不受影响

Object.setPrototypeOf 更改对象原型

// Object.setPrototypeOf
// 对象在创建后,支持使用此方法改变对象的PrototypeOf

let p1 = {
    get() {
        return 'A';
    }
}
let p2 = {
    get() {
        return 'B';
    }
}
// 把p1作为原型创建对象
let p3 = Object.create(p1);
// 或者使用__proto__指定原型
let p4 = {
    __proto__: p1
}
console.log(p1.get()); // A
console.log(Object.getPrototypeOf(p3) == p1); // true
// 更换原型
Object.setPrototypeOf(p3, p2);
// 或者
// p3.__proto__ = p2;
console.log(p3.get()); // B
console.log(Object.getPrototypeOf(p3) == p2); // true
console.log(Object.getPrototypeOf(p4) == p1); // true

函数

获取函数名称

// 获取函数的名称
function test1() {
}

console.log(test1.name); // test1

let test2 = function () {
}
console.log(test2.name); // test2

let test3 = function test4() {
}
console.log(test3.name); // test4

箭头函数 Arrow Function

类似Java的lambda表达式

// Arraw Function 箭头函数

// 单个参数,直接返回参数的函数
let demo1 = param1 =>
param1;
/*
function demo1(param1) {
    return param1;
}
*/

// 多个参数,返回简单进行参数处理的函数
let demo2 = (param1, param2) =>
param1 + param2;
/*
function demo2(param1, param2) {
    return param1 + param2;
}
*/

// 多个参数,返回复杂的参数处理函数
let demo3 = (param1, param2) =>
{
    return param1 + param2;
}
/*
function demo2(param1, param2) {
    return param1 + param2;
}
*/

函数形参初始值

// ES 可以给函数参数设置默认值

function breakfast(food = '🍞', drink = '🥛') {
    console.log(`我今天吃的 ${food}, 喝的 ${drink}`)
}

breakfast();

rest参数(可变参数)

// ES5 获取实参的方式
function date(){
    console.log(arguments);
}

date("a", "b", "c"); // [Arguments] { '0': 'a', '1': 'b', '2': 'c' } // 是一个Arguments对象

// ES6 rest参数
// 就是可变参数
function date2(a, ... args) {
    console.log(args);
}

date2("a", "b", "c"); // ['b', 'c' ] // 是一个参数数组

Spread扩展运算符

let fruits = ['🍎', '🍌', '🍋'],
    foods = ['🥪', '🍞',...fruits]; // 将fruits展开到foods

console.log(fruits);
console.log(...fruits); // 展开操作符
console.log(foods);  // 将fruits加入到foods中


// 应用在函数传参:
function test() {
    console.log(arguments);
}
let arr = ["a", "b", "c"];
test(...arr); // [Arguments] { '0': 'a', '1': 'b', '2': 'c' }

新数据类型:symbol

symbol代表独一无二的值。

函数Symbol()创建symbol

// 创建Symbol
let s1 = Symbol();
console.log(s1, typeof s1); //Symbol() symbol

根据字符串创建symbol

// 可根据字符串创建symbol,字符串类似一个symbol的备注
let s2 = Symbol("study");
let s3 = Symbol("study");

// 通过Symbol方法创建的任何symbol,比较都为false
console.log(s2 == s3) // false

Symbol.for创建

// 但是可以使用symbol.for方法创建相同的symbol
let s4 = Symbol.for("study");
let s5 = Symbol.for("study");
console.log(s4 === s5); // true

不可与任何数据类型运算

// Symbol数据类型不能与其他任何数据类型进行运算
let result = s1 + 100; // 错误
let result = s1 > 100; // 错误
let result = s1 + s2; // 错误

使用场景:用作唯一方法/属性名

let game = {
    name: "俄罗斯方块",
    up() {
    },
    down() {
    }
}

// 向game添加两个方法,是methods的up和down
game.up = function () {
}
game.down = function () {
}

// 上述做法,在复杂的对象中有可能出现方法被覆盖等问题
// 所以需要借助symbol来添加唯一方法

let methods = {
    up: Symbol(),
    down: Symbol(),
};

// 这样game新增的up和down方法key是symbol类型,不会发生冲突了
game[methods.up] = function () {
    console.log("symbol 向上");
}
game[methods.down] = function () {
    console.log("symbol 向下");
}

// 调用方法
game[methods.up]();
game[methods.down]();

或者使用如下方式:

let youxi = {
    name: "俄罗斯方块",
    [Symbol.for("up")]: function () {
        console.log("向上");
    },
    [Symbol.for("down")]: function () {
        console.log("向下");
    }
}

youxi[Symbol.for("up")]();
youxi[Symbol.for("down")]();

Symbol对象的内置值

Symbol对象有一些内置值,代表对象的一些内置方法,比如在调用obj instanceof Person时,会去调用类型内部的static [Symbol.hasInstance](param) {}方法去判断对象是否属于某一类型。

我们可以通过Symbol的内置值,对类型的一些功能进行重写。

参考:Symbol - JavaScript | MDN (mozilla.org)

Symbol.hasInstance 控制类型检测

instanceof对象类型判断重写:

class Person {
    static [Symbol.hasInstance](param) {
        console.log(`Person 与 ${param} 类型进行从属判断`);
        return true; // true表示属于该类型
    }
}

console.log("hello" instanceof Person);
// 输出结果
// Person 与 hello 类型进行从属判断
// true

Symbol.isConcatSpreadable是否可以被展开

const arr1 = ["a", "b", "c"];
const arr2 = ["d", "e", "f"];
console.log(arr1.concat(arr2)); // [ 'a', 'b', 'c', 'd', 'e', 'f' ]

const arr3 = ["a", "b", "c"];
const arr4 = ["d", "e", "f"];
arr4[Symbol.isConcatSpreadable] = false; // 数组是否可以被展开,如果为false,说明数组是一个整体
console.log(arr3.concat(arr4));
// 结果
// [
//   'a',
//   'b',
//   'c',
//   [ 'd', 'e', 'f', [Symbol(Symbol.isConcatSpreadable)]: false ]
// ]

迭代器

迭代器是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作。下面是原生带有Iterator接口的数据:

  • Array

  • Arguments

  • Set

  • Map

  • String

  • TypedArray

  • NodeList

Iterator接口

// 生成迭代器

var foods = ['🍅', '🍚'];

function* chef() {
    for (var i = 0; i < foods.length; i++) {
        yield foods[i];
    }
}

let lisi = chef();
console.log(lisi.next());
console.log(lisi.next());
console.log(lisi.next());

for of循环

const ps = ["毛利兰", "江户川柯南", "灰原哀", "赤井秀一"];
for (const p of ps) {
    console.log(p); // 毛利兰 ... 
}

自定义类型支持Iterator

const banji = {
    name: "一年级一班",
    students: [
        '张三',
        '李四',
        '王五',
        '赵六',
    ],
    [Symbol.iterator]() {
        let _this = this;
        let index = 0;
        return {
            next: function () {
                if (index < _this.students.length) {
                    return {value: _this.students[index++], done: false};
                } else {
                    return {value: undefined, done: true};
                }
            }
        }
    }
}

// 使用for of循环遍历
for (const bj of banji) {
    console.log(bj);
}

生成器

声明和调用

// 1: 生成器是一个函数
// 2: 生成器返回迭代器
// 3: function关键字后面紧挨着或者留一个空格后,必须有一个星号(*)
// 4: 函数体里面会用到关键字yield,来依次返回想要迭代的值

function* createIterator() {
    yield 1;
    yield 2;
    yield 3;
}

let iterator = createIterator();
console.log(iterator.next());//{value: 1, done: false}
console.log(iterator.next());//{value: 2, done: false}
console.log(iterator.next());//{value: 3, done: false}
console.log(iterator.next());//{ value: undefined, done: true }

参数传递

yield关键字可以接收next的返回值,yield将会在next方法调用之前阻塞。

// 使用 * 定义一个生成器函数
function * gen(args) {
    console.log(`gen整体传参:${args}`);
    console.log("第一部分");

    let oneParam = yield "两只老虎";
    console.log(oneParam); // BBB
    console.log("第二部分");
    let twoParam = yield "跑的快";
    console.log(twoParam); // CCC

    console.log("第三部分");
    let threeParam = yield "一直没有眼睛";
    console.log(threeParam); // DDD
}

let g = gen("hello");
g.next("AAA");
g.next("BBB");
g.next("CCC");
g.next("DDD");

上面代码的输出结果为:

gen整体传参:hello
第一部分
BBB
第二部分
CCC
第三部分
DDD

实例:解决回调地狱

// 生成器是新的异步解决方案
// 1s 后输出 1
// 2s 后输出 2
// 3s 后输出 3

// ES5的代码,会出现回调地狱
setTimeout(function () {
    console.log(1);
    setTimeout(function () {
        console.log(2);
        setTimeout(function () {
            console.log(3);
        }, 3 * 1000);
    }, 2 * 1000);
}, 1000);

// 使用ES6,生成器代码
// 首先定义三个任务
function one() {
    setTimeout(() => console.log(1), 1000);
}

function two() {
    setTimeout(() => console.log(2), 2 * 1000);
}

function three() {
    setTimeout(() => console.log(3), 3 * 1000);
}

// 然后使用生成器一次调用
function* gen() {
    yield one();
    yield two();
    yield three();
}

let g = gen();
g.next()
g.next()
g.next()

实例2:实际场景模拟

假设有如下场景,需要

  1. 获取当前登录用户信息

  2. 根据用户id查询订单列表

  3. 取出订单列表第一条订单购买的第一个商品id,并请求查询商品详情


// 获取当前登录的用户信息
function getUser(userid) {
    // 使用setTimeout模拟http接口的请求
    let user;
    setTimeout(() => {
        console.log("获取登录用户数据成功!http status : ok");
        user = {
            userid: userid,
            name: "宫野明美",
        }
        iterator.next(user);
    }, 1000);
    // 将值传给下一个iterator
    return user;
}

// 获取用户的订单列表信息
function getOrders(userid) {
    let orders;
    setTimeout(() => {
        console.log(`获取用户${userid}的订单列表成功!http status : ok`);
        orders = [
            {orderid: 102, totalAmount: 100.02, time: new Date(), goodsId: [901, 903, 878]},
            {orderid: 103, totalAmount: 20.4, time: new Date(), goodsId: [999, 675]},
        ];
        iterator.next(orders);
    }, 1000);
    return orders;
}

// 获取某个指定产品的信息
function getGoods(goodsId) {
    let goods;
    setTimeout(() => {
        console.log(`请求${goodsId}商品详细信息成功`);
        goods = {goodsid: goodsId, goodsName: "洗衣粉"};
        iterator.next(goods);
    }, 1000);
    return goods;
}

function * getUserFirstOrderFirstGoods(userid) {
    // 获取登录用户
    let user = yield getUser(userid);

    // 获取订单数据
    let orders = yield getOrders(user.userid);

    // 从订单取出第一个商品id再查询商品详情
    let goods = yield getGoods(orders[0].goodsId[0]);

    // 获取产品数据成功
    console.log(goods);
}

// iterator 是一个全局变量
let iterator = getUserFirstOrderFirstGoods();
iterator.next();

响应结果:

获取登录用户数据成功!http status : ok
获取用户undefined的订单列表成功!http status : ok
请求901商品详细信息成功
{ goodsid: 901, goodsName: '洗衣粉' }

awaitsync

await是生成器的语法糖:

var fs = require('fs');

var readFile = function (fileName){
  return new Promise(function (resolve, reject){
    fs.readFile(fileName, function(error, data){
      if (error) reject(error);
      resolve(data);
    });
  });
};

var gen = function* (){
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

上面的代码与下面是等价的:

var asyncReadFile = async function (){
  var f1 = await readFile('/etc/fstab');
  var f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

Promise

Promise是JS中进行异步编程的新解决方案,Promise可以封装一个异步操作,并获取成功/失败的结果值。通过Promise构造函数,创建一个Promise:

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

Promise中含有两个重要的属性,PromiseStatePromiseResult。当Promise执行异步任务时

  • 如果经过一段时间异步任务执行成功,需要异步任务调用resolve(value),此时

    • PromiseState将会从pending变为resolved

    • PromiseResult的值为value,一般是成功的值

  • 如果经过一段时间异步任务执行失败,需要异步任务调用reject(err)或者直接抛出异常throw err,此时

    • PromiseState将会从pending变为rejected

    • PromiseResult的值为err,一般是失败的原因

此外,promise还提供了then以及catch方法,当状态发生变化时,会调用then传入的回调:

  • then方法的调用需要传入两个回调函数(可以只传一个回调函数)

    let p = promise.then(function (value) { // 状态变为resolved的时候的回调,参数为异步任务调用resolve方法的参数
      
    }, function (reason) { // 状态变为reject的回调, 参数为异步任务调用reject方法的参数
      
    });
    // 并且then方法的返回值是一个新的promise对象
  • 有些异步任务仅仅需要处理失败的情况,Promise也提供了catch方法,单独用作失败回调

    promise.catch(function(reason){
      
    }); 
  • 可以指定多个then,这些then指定的回调都会被执行:

    promise.then(function (value) {
      console.log("1");
    }).then(function (value) {
      console.log("2");
    });
    // 结果: 
    // 1
    // 2 
  • then方法返回的promise对象是由如下三种情况决定的

    • 如果then的函数参数在处理中抛出异常,then返回状态为resolved的新的promise

      let promise = new Promise(function (resolve, reject) {
        setTimeout(() => {
          resolve("success");
        }, 2000)
      });
      
      let p = promise.then(function (value) {
        throw "出错了";
      });
      
      console.log(p === promise); // false,说明是新的promise
      console.log(p); // Promise {<rejected>}
    • 如果then的函数参数返回的值是非promise的任意值,那么then返回状态为resolved的新promise对象

      let promise = new Promise(function (resolve, reject) {
        setTimeout(() => {
          resolve("success");
        }, 2000)
      });
      
      let p = promise.then(function (value) {
        console.log(value);
      });
      
      console.log(p === promise); // false,说明是新的promise
      console.log(p); // Promise {<pending>}
    • 如果then的函数参数返回的值是一个新的promise对象,那么then返回这个新的promise对象(这种方式可以通过链式调用完成嵌套操作)

      let promise = new Promise(function (resolve, reject) {
        setTimeout(() => {
          resolve("success");
        }, 2000)
      });
      
      promise.then(function (value) {
        console.log(value); // success
        return new Promise(function (resolve, reject) {
          resolve("1");
        });
      }).then(value => {
        console.log(value); // 1
      });

实例:文件读取

const fs = require("fs");

const p = new Promise(function (resolve, reject) {
    fs.readFile("./a.txt", function (err, data) {
        if (err) {
            reject(err); // 处理失败调用reject函数,将会把Promise对象置为失败状态
        } else {
            resolve(data); // 处理成功调用resolve函数,将数据传输过去,并会将Promise对象置为成功状态
        }
    });
});

// 使用then方法,指定promise对象成功或者失败后的处理方式,也就是resolve和reject函数
p.then(function (value) {
    console.log("处理成功, 值为 ", value.toString());
}, function (reason) {
    console.log("处理失败, 错误为 ", reason);
});

实例:封装AJAX

const xmlhttprequest = require("xmlhttprequest")
let p = new Promise(function (resolve, reject) {
    let xhr = new xmlhttprequest.XMLHttpRequest();
    xhr.open("GET", "https://baidu.com");
    xhr.send();
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) { // 进入最后一个阶段,响应体已经返回
            if (xhr.status >= 200 && xhr.status < 300) {
                resolve(xhr.responseText);
            } else {
                reject(xhr.status);
            }
        }
    };
});

p.then(function (response) {
    console.log(response)
}, function (status) {
    console.error(status);
});

Set

// 无序不可重复集合 Set
// 注意构造函数Set()的参数传入的是iterable接口,
// string本身也是一个iterable接口,所以下面的元素数量是5
let food = new Set('🍎🍌🍅🥓🥔');
console.log(food.size); // 5
// 添加元素
food.add('🍉');
console.log(food, food.size); // 不相同元素会添加
food.add('🍎');
console.log(food, food.size); // 相同元素不会添加 6

// 删除元素
food.delete('🍉');
console.log(food, food.size); // 删除西瓜,5

// 判断元素是否存在于集合中
console.log(food.has('🍉')); // false

// 遍历元素
food.forEach(f = > {
    console.log(f);
})

// 清空set
food.clear();
console.log(food, food.size); // 0

Map

// js对象默认就是一个map,为什么还需要map? 
// 因为js对象的key只能是string类型,不可以是复杂的对象

// 新建Map
let food = new Map();
let fruit = {}, cook = function () {
}, dessert = '甜点';

// 向map中添加元素
food.set(fruit, '🍋');
food.set(cook, '🔪');

// 获取map的长度
console.log(food.size);

// 获取指定元素
console.log(food.get(fruit));

// 删除某个元素
food.delete(fruit);

// 判断元素是否存在
console.log(food.has(fruit));

// 遍历map
console.log("-------");
food.forEach((key, value) => {
    console.log(key, value);
})
;

// 清空map
food.clear();

面向对象

ES6 的面向对象就是 ES5 的语法糖,这些功能都可以在 ES5 中实现。

class

class Chef {
    constructor(food) {
        this.food = food; // 声明属性
        this.dish = [];
    }

    cook() {
        console.log(this.food);
    }

    // getter and setter
    get menu() {
        return this.dish;
    }

    set menu(dish) {
        this.dish.push(dish);
    }

    // static method 不需要创建对象就可以调用
    static cook(food) {
        console.log('我炒了' + food)
    }
}
let lisi = new Chef('🍅');
lisi.menu = '🍕';
lisi.menu = '🍞';
lisi.cook();
console.log(lisi.menu);

// 调用静态方法
Chef.cook('🍅');

继承

// 类也可以继承
class Person {
    constructor(name, birthday) {
        // 定义属性
        this.name = name;
        this.birthday = birthday;
    }

    info() {
        return `姓名:${this.name}, 出生日期: ${this.birthday}`;
    }
}

// Person的子类
class Student extends Person {
    constructor(name, birthday) {
        super(name, birthday);
    }

    // 重写方法
    info() {
        return super.info() + " 是一个学生";
    }
}

let zhangsan = new Student('张三', '1995年9月2日');
console.log(zhangsan.info());

封装

class Phone {
  get price() {
    return "haha";
  }
  
  set price(num) {
    console.log("haha");
  }
}

// 调用set方法
let p = new Phone();
console.log(p.price);
// 调用get方法
p.price = 100;

模块化

在ES5的时候,第三方提供的模块化规范有:

模块化规范实现

CommonJS

NodeJSBrowserify

AMD

requireJS

CMD

seaJS

exportimport

创建模块m1.js

export let name = "张三";
export function hello() {
    console.log("hello");
}

创建模块m2.js,并引用m1导出的方法和变量:

import * as m1 from './m1';

console.log(m1.name);
m1.hello();

统一暴露

let name = "张三";
function hello() {
    console.log("hello");
}
export {name, hello};

默认暴露

export default {
  name : "张三",
  function hello() {
    console.log("hello");
  }
}

通用别名导入方式

import * as m1 from "./m1.js";

解构赋值形式

import {name, hello} from "./m1.js";
// 下面就不用使用别名了
hello();

结构部分别名

// 如果不同模块有同名方法,可以使用as别名
import {name, hello as hello1} from "./m1.js";
hello1();

导入默认暴露的模块

import {default as m1} from "./m1.js";

导入的简便模式:针对默认暴露

import m1 from "./m1.js";

最后更新于