Open YvetteLau opened 5 years ago
照抄阮大神的书
//lib.js 一个commonJS模块
var counter = 3
function incCounter() {
counter++
}
module.exports = {
counter : counter,
incCounter : incCounter,
}
//main.js 在这个函数里加载这个模块
var mod = require ('./lib')
console.log(mod.counter)
mod.incCounter()
console.log(mod.counter)
3
3
上面的代码说明,lib.js模块加载后,它的内部变化就影响不到输出的mod.counter 了。 这是因为mod.counter是一个原始类型,会被缓存。除非写成一个函数,否则得不到内部变动后的值。
//lib.js
var counter = 3
function incCounter() {
counter++
}
module.exports = {
get counter(){ //输出的counter属性实际上是个取值器函数。
return counter
},
incCounter: incCounter
}
main.js
var mod = require ('./lib')
console.log(mod.counter)
mod.incCounter()
console.log(mod.counter)//现在再执行就能正确读取内部变量counter的变动了。
3
4
ES6模块的运行机制与CommonJS不一样。JS引擎对脚本静态分析的时候,遇到模块加载命令import就会生成一个只读引用。等到脚本真正执行的时候,再根据这个只读引用到被加载的模块中取值。因此,ES6模块是动态引用,并且不会缓存值,模块里的变量绑定其所在的模块。
// lib.js
export let counter = 3
export function incCounter() {
counter++
}
//main.js import { counter, incCounter } from './lib' console.log(counter) incCounter() console.log(counter)
3 4
上面的代码说明,ES6模块输入的变量counter是活的,完全反应其所在模块lib.js内部的变化。
再如:
//m1.js export var foo = 'bar' setTimeout(()=>foo='baz',500) //m2.js import {foo} from './m1.js' console.log(foo) setTimeout(()=>console.log(foo),500)
bar baz
上面的代码表明,ES6模块不会缓存运行结果,而是动态地去被加载的模块取值,并且变量总是绑定其所在的模块。
由于ES6输入的模块变量只是一个“符号连接”,所以这个变量是只读的,对它重新赋值会报错。
//lib.js export let obj = {}
//main.js import {obj} from './lib' obj.prop=123 //OK obj = {} //TypeError
main.js 从 lib.js 输入变量obj,可以对obj添加属性,但是重新赋值就会报错。因为变量obj指向的地址是只读的,不能重新赋值,这就好比main.js创造了一个名为obj的const变量。
//mod.js function C(){ this.sum = 0 this.add = function(){ this.sum += 1 } this.show = function(){ console.log(this.sum) } } export let c = new C()
//x.js import {c} from './mod' c.add()
//y.js import {c} from './mod' c.show()
//main.js import './x' import './y'
1
这就证明了x.js和y.js加载都是C的同一实例
摘抄自:
阮一峰-ES6标准入门-第六章-Module的加载实现
ES6是只读的 主要阐述CommonJS吧 之前面试遇到过这个题
CommonJS: 对于变量属于复制 存在缓存 此模块的变量可以被其他模块所修改
// b.js
let count = 1
let plusCount = () => {
count = 99
}
setTimeout(() => {
console.log('lala', count)
}, 4000)
module.exports = {
count,
plusCount
}
// a.js
let mod = require('./b.js')
console.log('a.js-1', mod.count)
mod.plusCount()
console.log('a.js-2', mod.count)
mod.count = 3
console.log('a.js-3', mod.count)
node a.js
a.js-1 1
a.js-2 1
a.js-3 3
lala 99 // 四秒后
复杂数据类型属于浅复制
// b.js
let count = 1
let obj = [{val: 1}]
let plusCount = () => {
count = 99
}
let plusObj = () => {
obj[0].val = 999
}
setTimeout(() => {
console.log('lala', obj)
}, 4000)
module.exports = {
count,
plusCount,
plusObj,
obj
}
// a.js
let mod = require('./b.js')
console.log('a.js-1', mod.obj)
mod.plusObj()
console.log('a.js-2', mod.obj)
node a.js
a.js-1 [{val: 99}]
a.js-2 [{val: 99}]
lala [{val: 99}]
CommonJS
模块是运行时加载,ES6模块是编译时输出接口。CommonJS
加载的是一个对象,该对象只有在脚本运行完才会生成。CommonJS
模块输出的是一个值的拷贝,ES6模块输出的是值的引用。- `CommonJS` 输出的是一个值的拷贝(注意基本数据类型/复杂数据类型)
- ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
CommonJS 模块输出的是值的拷贝。
模块输出的值是基本数据类型,模块内部的变化就影响不到这个值。
//name.js
let name = 'William';
setTimeout(() => { name = 'Yvette'; }, 300);
module.exports = name;
//index.js
const name = require('./name');
console.log(name); //William
//name.js 模块加载后,它的内部变化就影响不到 name
//name 是一个基本数据类型。将其复制出一份之后,二者之间互不影响。
setTimeout(() => console.log(name), 500); //William
模块输出的值是复杂数据类型
//name.js
let name = 'William';
setTimeout(() => { name = 'Yvette'; }, 300);
module.exports = { name };
//index.js
const { name } = require('./name');
console.log(name); //William
//name 是一个原始类型的值,会被缓存。
setTimeout(() => console.log(name), 500); //William
模块输出的是对象:
//name.js
let name = 'William';
let hobbies = ['coding'];
setTimeout(() => {
name = 'Yvette';
hobbies.push('reading');
}, 300);
module.exports = { name, hobbies };
//index.js
const { name, hobbies } = require('./name');
console.log(name); //William
console.log(hobbies); //['coding']
/*
* name 的值没有受到影响,因为 {name: name} 属性值 name 存的是个字符串
* 300ms后 name 变量重新赋值,但是不会影响 {name: name}
*
* hobbies 的值会被影响,因为 {hobbies: hobbies} 属性值 hobbies 中存的是
* 数组的堆内存地址,因此当 hobbies 对象的值被改变时,存在栈内存中的地址并
没有发生变化,因此 hoobies 对象值的改变会影响 {hobbies: hobbies}
* xx = { name, hobbies } 也因此改变 (复杂数据类型,拷贝的栈内存中存的地址)
*/
setTimeout(() => {
console.log(name);//William
console.log(hobbies);//['coding', 'reading']
}, 500);
ES6 模块的运行机制与 CommonJS
不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令 import
,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
//name.js
let name = 'William';
setTimeout(() => { name = 'Yvette'; hobbies.push('writing'); }, 300);
export { name };
export var hobbies = ['coding'];
//index.js
import { name, hobbies } from './name';
console.log(name, hobbies); //William ["coding"]
//name 和 hobbie 都会被模块内部的变化所影响
setTimeout(() => {
console.log(name, hobbies); //Yvette ["coding", "writing"]
}, 500); //Yvette
ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。因此上面的例子也很容易理解。
那么 export default
导出是什么情况呢?
//name.js
let name = 'William';
let hobbies = ['coding']
setTimeout(() => { name = 'Yvette'; hobbies.push('writing'); }, 300);
export default { name, hobbies };
//index.js
import info from './name';
console.log(info.name, info.hobbies); //William ["coding"]
//name 不会被模块内部的变化所影响
//hobbie 会被模块内部的变化所影响
setTimeout(() => {
console.log(info.name, info.hobbies); //William ["coding", "writing"]
}, 500); //Yvette
一起看一下为什么。
export default
可以理解为将变量赋值给 default
,最后导出 default
(仅是方便理解,不代表最终的实现,如果对这块感兴趣,可以阅读 webpack 编译出来的代码)。
基础类型变量 name
, 赋值给 default
之后,只读引用与 default
关联,此时原变量 name
的任何修改都与 default
无关。
复杂数据类型变量 hobbies
,赋值给 default
之后,只读引用与 default
关联,default
和 hobbies
中存储的是同一个对象的堆内存地址,当这个对象的值发生改变时,此时 default
的值也会发生变化。
CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。 CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
CommonJs模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化不会影响到这个值
// common.js
var count = 1;
var printCount = () =>{
return ++count;
}
module.exports = {
printCount: printCount,
count: count
};
// index.js
let v = require('./common');
console.log(v.count); // 1
console.log(v.printCount()); // 2
console.log(v.count); // 1
你可以看到明明common.js里面改变了count,但是输出的结果还是原来的。这是因为count是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部变动的值。将common.js里面的module.exports 改写成
module.exports = {
printCount: printCount,
get count(){
return count
}
};
这样子的输出结果是 1,2,2 而在ES6当中,写法是这样的,是利用export 和import导入的
// es6.js
export let count = 1;
export function printCount() {
++count;
}
// main1.js
import { count, printCount } from './es6';
console.log(count)
console.log(printCount());
console.log(count)
ES6模块是动态引用,并且不会缓存,模块里面的便令绑定其所在的模块,而是动态地去加载值,并且不能重新复制
另外还想说一个export default
let count = 1;
function printCount() {
++count;
}
export default { count, printCount}
// main3.js
import res form './main3.js'
console.log(res.count)
commonJS是require()方法 相当于值的拷贝
会有属性缓存 一旦输出 内部的基本类型值的改变不会对输出发生影响 引用类型会
import 是声明时暴漏的接口 只有在使用时才会去根据这个接口取值 没有属性缓存
对复杂数据类型来说 export default值 也会受到改变 而基本数据类型不会
照抄阮大神的书
- CommonJS模块输出的是一个值的复制,ES6模块输出的是值的引用
- CommonJS模块是运行时加载,ES6模块是编译时输出接口 第二个差异是因为CommonJS加载的是一个对象,即module.export属性,该对象只有在脚本运行结束时才会生成。而ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。 下面重点解释第一个差异。 CommonJS模块输出的是值的复制,一旦输出这个值,模块内部的变化就影响不到这个值。
//lib.js 一个commonJS模块 var counter = 3 function incCounter() { counter++ } module.exports = { counter : counter, incCounter : incCounter, }
//main.js 在这个函数里加载这个模块 var mod = require ('./lib') console.log(mod.counter) mod.incCounter() console.log(mod.counter)
3 3
上面的代码说明,lib.js模块加载后,它的内部变化就影响不到输出的mod.counter 了。 这是因为mod.counter是一个原始类型,会被缓存。除非写成一个函数,否则得不到内部变动后的值。
//lib.js var counter = 3 function incCounter() { counter++ } module.exports = { get counter(){ //输出的counter属性实际上是个取值器函数。 return counter }, incCounter: incCounter }
main.js var mod = require ('./lib') console.log(mod.counter) mod.incCounter() console.log(mod.counter)//现在再执行就能正确读取内部变量counter的变动了。
3 4
ES6模块的运行机制与CommonJS不一样。JS引擎对脚本静态分析的时候,遇到模块加载命令import就会生成一个只读引用。等到脚本真正执行的时候,再根据这个只读引用到被加载的模块中取值。因此,ES6模块是动态引用,并且不会缓存值,模块里的变量绑定其所在的模块。
// lib.js export let counter = 3 export function incCounter() { counter++ } //main.js import { counter, incCounter } from './lib' console.log(counter) incCounter() console.log(counter) 3 4
上面的代码说明,ES6模块输入的变量counter是活的,完全反应其所在模块lib.js内部的变化。 再如:
//m1.js export var foo = 'bar' setTimeout(()=>foo='baz',500) //m2.js import {foo} from './m1.js' console.log(foo) setTimeout(()=>console.log(foo),500) bar baz
上面的代码表明,ES6模块不会缓存运行结果,而是动态地去被加载的模块取值,并且变量总是绑定其所在的模块。 由于ES6输入的模块变量只是一个“符号连接”,所以这个变量是只读的,对它重新赋值会报错。
//lib.js export let obj = {} //main.js import {obj} from './lib' obj.prop=123 //OK obj = {} //TypeError
main.js 从 lib.js 输入变量obj,可以对obj添加属性,但是重新赋值就会报错。因为变量obj指向的地址是只读的,不能重新赋值,这就好比main.js创造了一个名为obj的const变量。
//mod.js function C(){ this.sum = 0 this.add = function(){ this.sum += 1 } this.show = function(){ console.log(this.sum) } } export let c = new C()
//x.js import {c} from './mod' c.add()
//y.js import {c} from './mod' c.show()
//main.js import './x' import './y' 1
这就证明了x.js和y.js加载都是C的同一实例
摘抄自: 阮一峰-ES6标准入门-第六章-Module的加载实现
还是峰哥牛批
ES6模块是引用,重新赋值会编译报错,不能修改其变量的指针指向,但可以改变内部属性的值; CommonJS模块是拷贝(浅拷贝),可以重新赋值,可以修改指针指向;