[js基础]ECMAScript2015(ES6)精要知识点(下)

模块化

模块就是在单个文件中声明的JavaScript代码。我们可以用JS代码直接从其他文件中导入函数、变量和类

在NodeJS之前,由于没有过于复杂的开发场景,前端是不存在模块化的,后端才有模块化。

NodeJS诞生之后,它使用CommonJS的模块化规范。从此,js模块化开始快速发展

CommonJS

多用module.exports定义当前模块对外输出的接口(不推荐直接用exports),用require加载模块。

// name.js
module.exports.name = 'abc'
module.exports.sayName = function() {console.log('name')}

或者

// name.js
var name = 'abc';
function sayName() {console.log('name')}
module.exports = {
  name: name,
  sayName: sayName
}

引用:[普通模块name.js]

var nameObj = require('./name.js')
console.log(nameObj.name)
nameObj.sayName(); // name

如果引用系统模块[会去node_modules里找]

var http = require('http');
http.createService(...).listen(3000);

特点:用同步的方式加载模块。

缺点:在服务端,模块文件都存放在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载

AMD

AMD是一种异步模块化标准。

RequireJS是AMD最流行的实现。(CommonJS与requireJS无关,只是用了require语句

场景:项目中我们会将JS组件放到不同的文件里,并通过script标签引入。当组件间存在依赖关系的时候,被依赖的组件需要放到前面。否则的话会出现XXX is undefined或者XXXX is not a function之类的错误。比如一个jquery的插件显然是依赖jquery核心库的,所以jquery核心库文件必须先引入。当组件间依赖复杂时,使用RequireJs可以从一个根开始检查依赖,根据这些依赖关系自动的帮助我们插入script标签。

原理:所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

简单使用:

引入requireJS。指定工程JS模块入口。

// index.html
<script src="lib/require.js" data-main="js/scripts/main.js"></script>

情景一: 入口文件main.js里用RequireJs映射Jquery并使用

require.config(
    {
        // baseUrl——用于加载模块的根路径
        baseUrl:'./js',
        // paths——用于映射不存在根路径下面的模块路径
        paths: {
            'Jquery': lib/jquery'
        }
    }
);
require(['Jquery'],function ($) {
    // jquery操作
     $(document).on('click','#contentBtn',function(){
        $('#messagebox').html('You have access Jquery by using require()');
     });
});

情景二:app.js需要依赖tools.js模块

// tool.js
define(function(){
    return{
        decs : 'abcd',
    };
})
//  app.js
require(['./tool'],function(data){
    console.log(data.desc)
})

define函数接收[moduleName][requireModeule][callback]三个参数。

完整写法诸如:

define('./app',['./tool'],function(data){
   .....
})

第一个参数是定义模块名,第二个参数是传入定义模块所需要的依赖,第三个函数则是定义模块的主函数,主函数和require的回调函数一样,同样是在依赖加载完以后再调用执行。

不建议传入第一个参数,即自定义模块名,因为如果哪一天我将这个文件转移到其他目录下,那我就得在这这里再修改一次模块名。官方其实也不推荐,用官方的说法是:让优化工具去自动生成这些模块名吧!

情景三:将require.config配置在入口文件,以供全局使用

// main.js
define(function(){
    require.config({
        baseUrl:'./js',
        paths: {
            'Jquery': lib/jquery'
        }
    });
}); 
// app.js
require(['./main.js'],function(){
    require(['Jquery'],function ($) {
         $(document).on('click','#btn1',function(){
            $('#messagebox').html('anc');
            require(['./tool'],function(data){
                console.log(data.desc)
            });
         });
    });
});

require.config额外配置:

shims——虽然目前已经有一部分流行的函数库(比如 jQuery)符合 AMD 规范,但还有很多库并不符合。shim 就是为了加载这些非 AMD 规范的 js,并解决其载入顺序的。

require.config({
baseUrl: './js',
paths: {
    'Jquery': lib/jquery'
}
shim: {
    'backbone': {
        deps: ['underscore', 'Jquery'],
        exports: 'Backbone'
    },
    'underscore': {
        exports: '_'
    }
},
});

我们想通过 RequireJS 来使用 backbone,那么你就需要在配置中把它定义为一个 shim。同时通过 deps 配置其依赖关系,可以保证 underscore、jquery 先被加载。

CMD

CMD是另一种js模块化方案,它与AMD很类似,不同点在于:AMD推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。CMD最流行的实现模式便是sea.js

AMD模式:

define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { 
    // 等于在最前面声明并初始化了要用到的所有模块
    a.do()
    if (false) {
      // 即便没用到某个模块 b,但 b 还是提前加载了。**这就CMD要优化的地方**
      b.do()
    } 
});

CMD模式:

define(function(require, exports, module) {
    var a = require('./a'); //在需要时申明
    a.doSomething();
    if (false) {
        var b = require('./b');
        b.doSomething();
    }
});

完整写法:

// index.html
<script type="text/javascript" src="js/libs/sea.js"></script>
<script type="text/javascript">
    seajs.use('./js/main.js')
</script>
// main.js
define(function(require,exports,module){
    let module1 = require('./module1.js')
    console.log(module1.getName()) // module1
 
    let module4 = require('./module4.js') //module2 module4 module3
    module4.getVal()
 
})
// modoule1
define(function(require,exports,module){
    let name = 'module1';
    function getName(){
        return name;
    }
    //暴露模块 
    module.exports = {getName}
})
// module4
define(function(require,exports,module){
    let val = 'module4'
    function getVal(){
        console.log(val)
    }
 
    // 引入module2 同步
    let module2 = require('./module2.js');
    // 执行module2
    module2()
 
    // 异步引入module3
    require.async('./module3.js',function(module3){
        //执行module3
        module3.module3.getData()
    });
 
    // 暴露模块
    module.exports = {getVal}
 }
// module2.js
define(function(require,exports,module){
    let msg = 'module2';
    function getMsg(){
        console.log(msg)
    }
    //暴露模块
    module.exports = getMsg;
})
// module3.js

define(function(require,exports,module){
    let data = 'module3'
    function getData(){
        console.log(data)
    }
 
    // 暴露模块
    exports.module3 = {getData}
})

因为在module4.js中引入了module3.js是异步的,所以先打印出module4,再打印出module3.

UMD

UMD是AMD和CommonJS的一个糅合。AMD是浏览器优先,异步加载;CommonJS是服务器优先,同步加载。

既然要通用,怎么办呢?那就先判断是否支持node.js的模块,存在就使用node.js;再判断是否支持AMD(define是否存在),存在则使用AMD的方式加载。这就是所谓的UMD。

((root, factory) => {
  if (typeof define === 'function' && define.amd) {
    //AMD
    define(['jquery'], factory);
  } else if (typeof exports === 'object') {
    //CommonJS
    var $ = requie('jquery');
    module.exports = factory($);
  } else {
    //都不是,浏览器全局定义
    root.testModule = factory(root.jQuery);
  }
})(this, ($) => {
  //do something...  这里是真正的函数体
});

ES6Module

ES2015在JavaScript标准中引入官方模块功能。

// a.js
const numberFn = r=> r * r;
const nmber = 5;
export {numberFn,number}

或者

// a.js
export const numberFn = r=> r * r;
export const nmber = 5;

使用:

import {number as count, numberFn} from './a.js' 
console.log(numberFn(count))

或者将整个模块当做一个变量来导入

import * as nn from './a.js' 
console.log(nn.numberFn(nn.number))

假设模块中只有一个成员被导出。可以使用export default关键字

// b.js
export default class Book{
    constructor(title) {
        this.title = title
    }
    printTitle()  {
        console.log(this.title)
    }
}
import Book from './b.js'
const aBook = new Book('aaa')
aBook.printTitle(); // aaa

导入export default导出模块。不需要将类名包含在花括号中(import {book} from './b.js' X)。只有模块有多个成员被导出才用花括号。

TypeScrip

TypeScript是一个开源的、渐进式包含类型的JavaScript超集。

作用是让开发者增强js的能力并使应用的规模扩展变得更容易。

主要功能之一是为JavaScript变量提供类型支持,在js中提供类型支持以实现静态检查。
有了TS可以使用一些js中没有的面向对象的概念,比如接口和私有属性(对开发数据结构和排序算法非常有用)

最终ts会变编译成简单的js代码。

安装:

npm i -g typescript

创建ts文件:

// a.ts
const tag = 'abc';
tag = 10;
let tag1 = 'ddd'
tag1 = 20

VScode编译器支持在编写代码时进行ts错误检查。

编译ts文件:

tsc a

终端输出了警告的错误信息(不会阻止编译器生成js代码)

Ts特性-类型推断:

// 常见的类型判断
let age: number = 20;
let isDead: boolean = true;
let name: string = 'abc';

ts允许我们给变量设置一个类型。但是上面的写法太啰嗦了。ts有一个类型推断机制,ts会根据为变量赋的值自动给该变量设置一个类型。上面可以写为:

let age = 20;
let  isDead = true;
let name = 'abc';

在上面的代码中,ts知道age就是一个数,isDead就是一个布尔值,name就是字符串,不需要显式设置变量类型。

// 当声明了一个变量但是没有设置初始值,建议为其设置一个类型
let ttt: string;

如果没有设置类型,那么它的类型会被设置为any,即接受任何值。

Ts特性-接口:

在ts中,有两个接口概念

概念一:给变量设置一个类型.它是对应各对象必须包含的属性和方法描述。

interface Person {
    name: string;
    age: number;
}
function printName(person:Person){console.log(person.name)}

概念二:与面向对象编程相关

interface Comparable {
    compareTo(b): number;    
}
class myClass implements Comparable {
    age: number;
    compareTo(b): number {
        if (this.age === b.age) retrun 0;
        return this.age > b.age ? 1 : -1
    }
}

comparable接口告诉myclass类,他需要思想一个叫做compareTo的方法,并且该方法接受一个参数,并返回数字结果。在该方法内部,我们可以实现对比逻辑。

Ts特性-泛型:

我们定义一个变量不确定类型的时候有两种解决方式:

使用any

使用any定义时存在的问题:虽然 以 知道传入值的类型但是无法获取函数返回值的类型;另外也失去了ts类型保护的优势

使用泛型

泛型指的是在定义函数/接口/类型时,不预先指定具体的类型,而是在使用的时候在指定类型限制的一种特性

在函数中使用泛型:

function test <T> (b:T):T{
  console.log(b);
  return b;
}
test<number>(111);// 返回值是number类型的 111
test<string | boolean>('hahaha')//返回值是string类型的 hahaha
test<string | boolean>(true);//返回值是布尔类型的 true

使用方式类似于函数传参,传什么数据类型,T就表示什么数据类型, 使用表示,T也可以换成任意字符串。

在接口中使用泛型:

// 注意,这里写法是定义的方法哦
interface Search {
  <T,Y>(name:T,age:Y):T
}

let fn:Search = function <T, Y>(name: T, id:Y):T {
  console.log(name, id)
  return name;
}
fn('li',11);//编译器会自动识别传入的参数,将传入的参数的类型认为是泛型指定的类型

在类中使用泛型:

class Animal<T> {
 name:T;
 constructor(name: T){
  this.name = name;
 }
 action<T>(say:T) {
   console.log(say)
 }
}
let cat = new Animal('cat');
cat.action('mimi')
————————————————
版权声明:本文为CSDN博主「xianghong_yang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44761091/article/details/124017662

附录-在浏览器中运行ES2015模块

可以看到部分浏览器依然不支持ES6。

想要在浏览器里有更好的兼容性体验,可以使用流行的代码打包工具,如Browserify或Webpack。生成编译成ES5代码的JS文件。

ES6有很好的向后兼容性。ES6是JS语言的超集,所有符合ES5规范的特性都可以在往后的浏览器上使用。

想要开发一套在浏览器和Nodejs环境中可以通用使用的,我们需要将代码转译成UMD通用模块定义。

附录-让TS检查在js文件里运行

①在计算机全局安装TypeScript

npm i -g typescript

②在jis文件的第一行添加一句// @ts-check

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值