模块化Modules
函数变量 与 函数表达式Function Variables and Expressions
函数作为变量Function as Variable
js 中 函数其实是可以存储在变量里的
1 2 3 4 5 <function f() { console.log('called');}const f1 = f;f1();called
函数声明,其实是给函数变量赋值
1 2 3 4 <function f() { console.log('called');}console.log(f);[Function: f]
函数表达式Function Expressions
函数声明
1 2 3 4 function f() { console.log('f')}f();
函数表达式
1 2 3 4 const f1 = function f() { console.log('f')}f1();
作用域
函数声明
上下均可使用
1 2 3 4 5 f();function f() { console.log('f')}f();
函数表达式
f1
作为一个变量,只有在复制后才能使用。之前会出错
1!2 3 4 5 f1();const f1 = function f() { console.log('f')}f1();
二次扫描时,不会扫到 const f1 =
右侧的
f()
在执行完表达式后,名字就废了,之后也不能用
1!2 3 4 5!f();const f1 = function f() { console.log('f')}f();
匿名函数表达式
因为函数表达式上的函数的名字 无法在调用后使用,
所以可以选择不起名
更改前
1 2 3 4 const f1 = function f() { console.log('f')}f1();
更改后
1 2 3 4 const f = function() { console.log('f')}f();
函数表达式 立刻调用
使用前
1 2 3 4 function f() { console.log('f');}f();
使用后
1 2 3 (function f() { console.log('f');})();
函数作为返回值Function as Return Value
函数嵌套Nested Functions
可以在函数内继续声明函数
1 2 3 4 5 6 7 8 9 10 11 12 13 // 1function outer() { // 2 function inner() { // 3 } // 4 inner(); // 5}// 6outer();// 7
执行顺序
1 6 outer 2 4 inner 3 5 7
函数作为返回值Function as Return Value
函数也可以作为返回值传递回来
1 2 3 4 5 6 7 8 9 10 < < <function f1() { console.log('f1 called'); function f2() { console.log('f2 called'); } return f2;}const f = f1();console.log('after return');f();f1 calledafter return f2 called
使用匿名函数表达式,简化
1 2 3*4*5*6 7 8 9 function f1() { console.log('f1 called'); return function () { console.log('f2 called'); }}const f = f1();console.log('after return');f();
闭包Closure
案例分析
1 2 3 4 5 6 7 8 9 < < <let _count = 0;function count() { _count++; return _count;}console.log(count());console.log(count());_count = 8;console.log(count());129
上面代码里 _count
可以被外部修改,从而导致 count()
产生紊乱
函数 与 作用域
只有 不使用关键字 声明的变量,是开在文件上的,函数调用后,在外部仍然可以使用。
使用 关键字 声明的变量/函数,均会受到 所在函数的限制。函数调用后,在外部无法使用。
1 2 3 4 5 6 7 8 9 <10!11!12!13!function f() { v1 = 1; var v2 = 2; let v3 = 3; const v4 = 4; function v5() { } } console.log(v1);1console.log(v2);console.log(v3);console.log(v4);console.log(v5);
访问封锁
使用函数,可以将,一些资源封锁在函数内部,从而外部无法访问
1 2 3 4 5 6 7 8!9!10!11!function counter() { let _count = 0; function count() { _count++; return _count; }}console.log(count());console.log(count());_count = 8;console.log(count());
访问外暴
将希望外部访问的东西,返回出去
1 2 3*4 5 6 7 8+9 <10 <11!function counter() { let _count = 0; return function() { _count++; return _count; }}const count = counter();console.log(count());1console.log(count());2_count = 8;
捕捉 与 环境Capture 与 Environment
变量捕捉
如果一个函数A 返回了一个函数B,此时。B 会 捕捉 A 中的局部变量 跟着 B 在一起。
此时 虽然 A 函数已经结束了,但 A 里的局部变量 没有死掉
1 2 3 4 5 6 7 8 9 function f() { let a = 0; let b = 0; return function() { b++; return b; }}const g = f();


多次调用
多次调用,会捕捉多次变量,且每个被捕捉的变量,相互独立
1 2 3 4 5 6 7 8 9 10 11 <12 <13 <14 function f() { let a = 0; let b = 0; return function() { b++; return b; }}const g1 = f();const g2 = f();console.log(g1());1console.log(g2());1console.log(g1());2

外部变量
不在外部函数外部的变量,不会被捕捉,且保持单一
1 2 3 4 5 6 7 8 9 10 <11 <12 <13 a = 0;function f() { return function() { a++; return a; }}const g1 = f();const g2 = f();console.log(g1());1console.log(g2());2console.log(g1());3
闭包
针对这一堆现象的组合,我们称对应的外部函数为 闭包 Closure
闭包可以用来达到封装的效果,将部分资源,以环境的身份被保护起来,存活的同时,不能被外界访问
1 2 3 4 5 6 7 8 9 10 <11 <12 <function counter() { let _count = 0; return function() { _count++; return _count; }}const countChicken = counter();const countBeef = counter();console.log(countChicken());1console.log(countBeef());1console.log(countChicken());2
闭包模块化Modules using Closure
案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <function counter() { let count = 0; function tick() { count++; } function current() { return count; } return { tick, current };} let c1 = counter();c1.tick();c1.tick();console.log(c1.current());2
步骤
在外部函数里面 开变量 以及 声明函数
返回一个 object,把希望曝光的函数 曝光出去
效果
封装
保护部分资源 不被外界直接访问。
CommonJS 模块化Modules using CommonJS
案例
counter.js
1 2 3 4 5 6 7 8 9 10 11 console.log('executing in counter.js')let count = 0;function tick() { count++;}function current() { return count;} exports.tick = tick;exports.current = current;
main.js
1 2 3 4 let counter = require('./counter.js');counter.tick();counter.tick();console.log(counter.current());
exports
类似 return 的 object
添加的属性 会曝光出去
require
导入对应文件,调用后返回文件内的 exports
文件执行
require 时,会导致 被 require 的文件 的执行
require 实质
如果多个文件 分别 require 同一个文件...
案例
counter.js
1 2 3 4 5 6 7 8 9 10 11 console.log('executing in counter.js')let count = 0;function tick() { count++;}function current() { return count;} exports.tick = tick;exports.current = current;
file1.js
1 2 3 4 console.log('executing in file1.js');let counter = require('./counter.js');counter.tick();console.log(counter.current());
file2.js
1 2 3 4 console.log('executing in file2.js');let counter = require('./counter.js');counter.tick();console.log(counter.current());
main.js
1 2 require('./file1.js');require('./file2.js');
运行结果
executing in file1.js executing in counter.js 1 executing in file2.js 2
缓存
当 require 一个文件时,文件内的代码会被执行
返回的 exports object 会被存储下来
下次再 require 一个文件时,将直接返回 上次返回的同一个 exports object(同一个地址)
配图
单一性
第一次 require 时,创造了 exports 对象,并创建了环境(包含 count 变量)
第二次 require 时,直接返回 上次的 exports 对象,所以共用一个环境
所以 第一个文件里 tick 的结果,在第二个文件里 tick 时 会联动
exports 其它类型
有些代码会使用 exports 导出一个不是对象的东西
counter.js
1 2 3 4 5 let count = 0;module.exports = function () { count++; return count;}
main.js
1 2 3 const counter = require('./counter.js');console.log(counter());console.log(counter());
结论
这种方式,我们无法断定 require 返回的 一定是对象
require 省略写法
main.js
1 2 3 4 let counter = require('./counter.js');counter.tick();counter.tick();console.log(counter.current());
可以改为
main.js
1*2 3 4 let counter = require('./counter');counter.tick();counter.tick();console.log(counter.current());
require 目录
如果导入的目录是 文件夹,将自动导入 文件夹内的 index.js
文件
domain/index.js
1 2 exports.name = 'zzax.io';exports.port = 9917;
main.js
1 2 const domain = require('./domain');console.log(domain);
应用案例
游戏当前的等级
level.js
1 2 3 4 5 exports.current = 1; exports.upgrade = function () { exports.current++;}
main.js
1 2 3 4 const level = require('./level');const game = require('./game');console.log(level.current);game.play();
game.js
1 2 3 4 5 const level = require("./level");exports.play = function() { level.upgrade(); console.log(level.current);};
js 中的文件,某种意义上来讲,可以充当一个单例对象
ES6 Modules 模块化Modules using ES6 Modules
ECMAScript 6
案例
使用前
counter.js
1 2 3 4 5 6 7 8 9 10 11 console.log('executing in counter.js')let count = 0;function tick() { count++;}function current() { return count;} exports.tick = tick;exports.current = current;
main.js
1 2 3 4 let counter = require('./counter.js');counter.tick();counter.tick();console.log(counter.current());
使用后
counter.js
1 2 3 4 5 6 7 8 9 10*11*console.log('executing in counter.js')let count = 0;function tick() { count++;}function current() { return count;} export { tick };export { current };
main.js
1*2 3 4 import * as counter from "./counter";counter.tick();counter.tick();console.log(counter.current());
import 和 export
跟 require 和 exports 的功能类似
只不过变成了语法级别支持
先声明,再 export
printer.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const width = 20;function headline1(text) { console.log('='.repeat(width)); console.log(text); console.log('='.repeat(width));}function headline2(text) { console.log(); console.log(text); console.log('-'.repeat(width));} export { headline1 }export { headline2 }// or // export { headline1, headline2 }
export 时 声明
printer.js
1 2 3 4 5 6 7 8 9 10 11 const width = 20;export function headline1(text) { console.log('='.repeat(width)); console.log(text); console.log('='.repeat(width));}export function headline2(text) { console.log(); console.log(text); console.log('-'.repeat(width));}
import 模块内的资源
printer.js
1 2 3 import { headline1, headline2 } from './printer';headline1('Import and Export');headline2('Import');
import 整个模块
main.js
1 2 3 import * as Printer from './printer';Printer.headline1('Import and Export');Printer.headline2('Import');
export import 时 改名
export 时 改名
printer.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const width = 20;function headline1(text) { console.log('='.repeat(width)); console.log(text); console.log('='.repeat(width));}function headline2(text) { console.log(); console.log(text); console.log('-'.repeat(width));} export { headline1 as printHeadline1 }export { headline2 as printHeadline2 }
main.js
1 2 3 import { printHeadline1, printHeadline2 } from './printer';printHeadline1('Import and Export');printHeadline2('Import');
import 时 改名
printer.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const width = 20;function headline1(text) { console.log('='.repeat(width)); console.log(text); console.log('='.repeat(width));}function headline2(text) { console.log(); console.log(text); console.log('-'.repeat(width));} export { headline1 }export { headline2 }
main.js
1 2 3 4 5 6 import { headline1 as printHeadline1, headline2 as printHeadline2 } from './printer';printHeadline1('Import and Export');printHeadline2('Import');
export import default
效果
addOperator.js
1 2 3 export default function (left, right) { return left + right;}
main.js
1 2 import addOperator from './addOperator';console.log(addOperator(3, 5));
直接 export default
addOperator.js
1 2 3 export default function (left, right) { return left + right;}
先声明, 再 export default
addOperator.js
1 2 3 4 function addOperator (left, right) { return left + right;}export { addOperator as default }
import default
main.js
1 2 import addOperator from './addOperator';console.log(addOperator(3, 5));
import default 改名
main.js
1 2 import { default as add } from './addOperator';console.log(add(3, 5));
default 与 一般情况混搭
addOperator.js
1 2 3 4 export default function addOperator (left, right) { return left + right;}export const sign = '+';
main.js
1 2 3 4 5 6 7 import { default as add, sign as addSign } from './addOperator'; calculate(add, 3, 5, addSign); function calculate(operator, left, right, operatorSign) { console.log(`${left} ${operatorSign} ${right} = ${operator(left, right)}`);}
NPM 包管理Node Package Manager
是什么
js 的 包管理器
NPM 官网
安装 node 的时候 就带着一个 npm
初始化
需要在项目文件夹下,增加一个 package.json 文件,并且符合一些标准
指令添加
执行这个语句,会在回答一些问题后,自动生成
>npm init
手动添加
可以手动用 vscode 等工具添加这个文件
可以没有任何属性,但至少得有一对大括号
1{}
添加 依赖
资源库
NPM 官网
添加
在项目根目录上运行
>npm install chalk
两部分
文件夹上的更改
package.json 也会有变动
1 2+3+4+5 { "dependencies": { "chalk": "^3.0.0" }}
导入 依赖的资源
1 2 const chalk = require('chalk');console.log(chalk.bgBlue.white(' ZZAX '));
package.json
用于记录软件运行所需要的信息
在将来代码合作的时候 node_modules 是不会共享的
需要使用软件的人,只需要你的源代码,以及 package.json 声明的依赖
可以通过 package.json 的依赖 重新下载软件。
根据 package.json 安装 依赖
准备好一个新的文件夹
把 package.json 文件放到里面
运行
>npm install
install 指令
安装指定软件
>npm install <名字>
会安装到 node_modules 并在 package.json 中记录
save 选项
你可能会看到 --save 这个选项
>npm install --save chalk
在旧版 npm 中,install 只会 会安装到 node_modules,不会在 package.json 中记录
增加 --save 会达到两边都改
新版 npm 中 无需添加 --save
根据 package.json 恢复
>npm install
根据 package.json 的 dependencies,恢复 node_modules 文件夹