#####ES6模块
ES6模块自动采用严格模式,通过export命令显示指定输出代码,再通过import命令输入。
- export输出命令:
export
语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。
|
|
|
|
- import 命令: 具体提升效果,会提升到整个模块的头部
|
|
模块整体加载
除了指定加载某个输出值,还可以使用整体加载,即用星号(
*
)指定一个对象,所有输出值都加载在这个对象上面。1234567891011121314//circle.jsfunction area(radius){return Math.PI * radius * radius}function circumference(radius){return 2*Math.PI * radius}export{area,circumference}------------------import{area,circumference} form './circle'console.log(area(2))--或者import * as circle from './circle' 用*指定一个对象,可以用重命名的circle.xxx来调用属性或者方法console.log('面积:'+ circle.area(2))export default:为模块指定默认输出,不需要知道对外输出的变量名或者函数名,用import引入的时候重命名就行。
注:一个模块只能有一个默认输出,所以export default只能使用1次
12345678//export-default.jsexport default function(){console.log('fuck')} //默认输出一个函数,其他模块加载时,可以为改匿名函数指定任意名字----------------// import-default.jsimport customName from './export-default'customName() // 'fuck' customName就是上面那个函数名。此时import不需要{}了!!123456非匿名函数也可以用export default来输出,但是在其他模块加载的时候,视同匿名函数加载//fn.jsexport default function fn(){//...}import fn from './fn.js'export 和 import的复合写法
在一个模块中,先输入后输出同一个模块,可以用以下写法export{} from ‘’
1234567891011export {foo,bar} from 'myModule'等同于import {foo,bar} from 'myModule'export {foo,bar}-------------------整体输出:export * from 'myModule'-------------------默认接口写法:export {default} from 'myModule'具名接口改为默认接口:export {foo as default} from 'myModule'等同于 import {foo} from './myModule'export default foo
模块在浏览器中的加载
默认情况下,浏览器是同步加载 JavaScript 脚本,即渲染引擎遇到<script>
标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,还必须加入脚本下载的时间。
如果脚本体积很大,下载和执行的时间就会很长,因此造成浏览器堵塞,用户会感觉到浏览器“卡死”了,没有任何响应。这显然是很不好的体验,所以浏览器允许脚本异步加载,下面就是两种异步加载的语法。
|
|
上面代码中,<script>
标签打开defer
或async
属性,脚本就会异步加载。渲染引擎遇到这一行命令,就会开始下载外部脚本,但不会等它下载和执行,而是直接执行后面的命令。
defer
与async
的区别是:前者要等到整个页面正常渲染结束,才会执行;后者一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer
是“渲染完再执行”,async
是“下载完就执行”。另外,如果有多个defer
脚本,会按照它们在页面出现的顺序加载,而多个async
脚本是不能保证加载顺序的。
浏览器加载ES6模块,也适用
<script>
标签,但是要加入type=”module”属性,加了这个属性等同于加了defer。1<script type="module" src="foo.js"></script>12#也可以添加async<script type="module" src="foo.js" async></script>ES6模块和CommonJS模块的差异
CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。而ES6模块原始值变了,
import
加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
CommonJS 加载的是一个对象(即
module.exports
属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
模块化的进程
各种全局变量
12345678var $topbar = $('#topbar')$topbar.on('click',functuion(){console.log('topbar')})var $banners = $('#banners')$banners.on('click',function(){console.log('banners')})用立即执行函数消除全局变量(或者用{}块级作用域+let)
12345678910111213141516171819!function(){var $topbar = $('#topbar')$topbar.on('click',functuion(){console.log('topbar')})}() //让这些变量变成局部变量,放进一个函数里并且调用,但是函数前要加上一个符号(+,-,!等)!function(){var $banners = $('#banners')$banners.on('click',functuion(){console.log('banners')})}()------------------{let $topbar = $('#topbar')$topbar.on('click',functuion(){console.log('topbar')})} //或者用es6语法:块级作用域,局部变量按照上述方法,作用域与作用域之间是隔开的,如果作用域B想使用作用域A的变量,怎么办?可以使用两个作用域都能访问到的全局变量window,作为桥梁,赋值给window属性并访问
123456789101112!function(){var $topbar = $('#topbar')var user = {name:'yom',age:20}window.user = user //全局变量}()!function(){console.log(window.user)}()如果不能让别的作用域改user,只能读(暴露一个函数,不要暴露整个变量)
123456789101112131415!function(){var $topbar = $('#topbar')var user = {name:'yom',age:20}window.user = {nameGetter:function(){return user.name},ageGetter:fucntion(){return user.age}}}()!function(){console.log(window.user.nameGetter) //yom}()什么时候闭包:只要一个函数使用了它外面的变量,这个函数就是闭包,闭包是指这个函数以及它能访问到的这个变量。闭包是作用域的一种特殊的使用方式。
举例:年龄增长器
1234567891011function olderMaker(){var user = {name:'jack',age:18}return function(){user.age += 1}}var older = olderMaker() //函数older.call() 每call一下,年龄增加一岁require.js(固定使用window.require()与window.define())
首先引入require.js
1<script src="js/require.js"></script>加载完require.js后,加载自己的代码,如果我们的代码主文件是main.js,那么需要用data-main=”./main”来引入(data-main属性的作用是,指定网页程序的主模块。)
1<script src="js/require.js" data-main="js/main"></script>主模块的写法,需要使用AMD规范定义的require()函数。
function里的参数可以是任何命名,但是参数是按照顺序的
123456//main.jsrequire(['./moduleA','moduleB','moduleC'],function(moduleA, moduleB, moduleC){some code here})//require()函数接受两个参数。第一个参数是一个数组,表示所依赖的模块,上例就是['moduleA', 'moduleB', 'moduleC'],即主模块依赖这三个模块;第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,并且参数是按照模块顺序的!!1234567891011121314151617require.config({paths: {"jquery": "lib/jquery.min","underscore": "lib/underscore.min","backbone": "lib/backbone.min"}});//使用require.config()方法,我们可以对模块的加载行为进行自定义。require.config()就写在主模块(main.js)的头部。参数就是一个对象,这个对象的paths属性指定各个模块的加载路径。---------------------or直接改变基目录(baseUrl)。require.config({baseUrl:'js/lib',paths:{"jquery": "jquery.min","underscore": "underscore.min","backbone": "backbone.min"}})假定现在有一个math.js文件,它定义了一个math模块。那么,math.js就要这样写
1234567891011121314// math.jsdefine(function(){var add = function (x,y){return x+y;};return {add: add};})加载方法如下:// main.jsrequire(['math'], function (math){alert(math.add(1,1));});如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。
define([‘myLib’], function(myLib){
function foo(){
myLib.doSomething();
}
return {
foo : foo
};
});
当require()函数加载上面这个模块的时候,就会先加载myLib.js文件。
举例
1234567891011121314151617181920212223242526272829303132333435363738394041index.htmlrequire.jsmain.js......topbar.js......banners.js......slides.js.......plugin.jsindex.html:<script src="./require.js" data-main="./main"><script/> //data-main="" 路径plugin.js:-----------define(function(){console.log('this is plugin')function aaa(){console.log('bbb')}return aaa})banners.js:----------define(functuon(){console.log('banners')})topbar.js:------------define([‘./plugin’],function(plugin){console.log('topbar')console.log(aaa)})slides.js:----------define([‘./plugin’],function(plugin){console.log('slides')aaa()})main.js:-----------require(['./slides','./banners','./topbar'],function(){console.log('main is runned')})加载顺序:1. 先加在没有依赖的模块js
2.再加载子模块依赖的plugin模块
3.最后加载main.js主文件
common.js
暴露:exports.xxx={} / module.exports={}
,接收:let 变量=require('./xxx')
|
|
注:如果ModuleA.js中只想暴露name,不像暴露xxx,不能写成这样
|
|
AMD(异步模块定义)
require.js
ES Modules
|
|