obj={
a:1,
b:2
}
obj2={
a:1,
b:2
}
obj3={
a:1,
b:'2'
}
可以转换为字符串来判断
JSON.stringify(obj)==JSON.stringify(obj2);//true
JSON.stringify(obj)==JSON.stringify(obj3);//false
var arr = [1,2,3,4,5,6,7,8,9,10];
arr.sort(function(){
return Math.random() - 0.5;
})
console.log(arr);
call
和 apply
都是为了解决改变 this
的指向。作用都是相同的,只是传参的方式不同。call
可以接收一个参数列表,apply
只接受一个参数数组let a = {
value: 1
}
function getValue(name, age) {
console.log(name)
console.log(age)
console.log(this.value)
}
getValue.call(a, 'yck', '24')
getValue.apply(a, ['yck', '24'])
bind
和其他两个方法作用也是一致的,只是该方法会返回一个函数。并且我们可以通过bind
实现柯里化
CDN
缓存更方便cookie
带宽前端性能优化:
总的来说性能优化这个领域的很多内容都很碎片化,这一章节我们将来学习这些碎片化的内容。
计算图片大小
对于一张
100 * 100
像素的图片来说,图像上有10000
个像素点,如果每个像素的值是RGBA
存储的话,那么也就是说每个像素有4
个通道,每个通道1
个字节(8
位 =1
个字节),所以该图片大小大概为39KB
(10000 * 1 * 4 / 1024
)。
CSS
去代替。CDN
加载,可以计算出适配屏幕的宽度,然后去请求相应裁剪好的图片。base64
格式WebP
格式的浏览器尽量使用 WebP
格式。因为 WebP
格式具有更好的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量,缺点就是兼容性并不好PNG
,其实对于大部分图标这类图片,完全可以使用 SVG
代替JPEG
DNS
解析也是需要时间的,可以通过预解析的方式来预先获得域名所对应的IP
。
考虑一个场景,滚动事件中会发起网络请求,但是我们并不希望用户在滚动过程中一直发起请求,而是隔一段时间发起一次,对于这种情况我们就可以使用节流。
理解了节流的用途,我们就来实现下这个函数
// func是用户传入需要防抖的函数
// wait是等待时间
const throttle = (func, wait = 50) => {
// 上一次执行该函数的时间
let lastTime = 0
return function(...args) {
// 当前时间
let now = +new Date()
// 将当前时间和上一次执行函数时间对比
// 如果差值大于设置的等待时间就执行函数
if (now - lastTime > wait) {
lastTime = now
func.apply(this, args)
}
}
}
setInterval(
throttle(() => {
console.log(1)
}, 500),
1
)
考虑一个场景,有一个按钮点击会触发网络请求,但是我们并不希望每次点击都发起网络请求,而是当用户点击按钮一段时间后没有再次点击的情况才去发起网络请求,对于这种情况我们就可以使用防抖。
理解了防抖的用途,我们就来实现下这个函数
// func是用户传入需要防抖的函数
// wait是等待时间
const debounce = (func, wait = 50) => {
// 缓存一个定时器id
let timer = 0
// 这里返回的函数是每次用户实际调用的防抖函数
// 如果已经设定过定时器了就清空上一次的定时器
// 开始一个新的定时器,延迟执行用户传入的方法
return function(...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}
fetch
,强制浏览器请求资源,并且不会阻塞 onload
事件,可以使用以下代码开启预加载
预加载可以一定程度上降低首屏的加载时间,因为可以将一些不影响首屏但重要的文件延后加载,唯一缺点就是兼容性不好。
可以通过预渲染将下载的文件预先在后台渲染,可以使用以下代码开启预渲染
预渲染虽然可以提高页面的加载速度,但是要确保该页面大概率会被用户在之后打开,否则就是白白浪费资源去渲染。
懒执行就是将某些逻辑延迟到使用时再计算。该技术可以用于首屏优化,对于某些耗时逻辑并不需要在首屏就使用的,就可以使用懒执行。懒执行需要唤醒,一般可以通过定时器或者事件的调用来唤醒。
src
属性为一张占位图,将真实的图片资源放入一个自定义属性中,当进入自定义区域时,就将自定义属性替换为 src
属性,这样图片就会去下载资源,实现了图片懒加载。
CDN
的原理是尽可能的在各个地方分布机房缓存数据,这样即使我们的根服务器远在国外,在国内的用户也可以通过国内的机房迅速加载资源。
因此,我们可以将静态资源尽量使用
CDN
加载,由于浏览器对于单个域名有并发请求上限,可以考虑使用多个CDN
域名。并且对于CDN
加载静态资源需要注意CDN
域名要与主站不同,否则每次请求都会带上主站的Cookie
,平白消耗流量
https://www.jianshu.com/p/a88f9d8282b3
https://www.cnblogs.com/yzhihao/p/9385467.html
JSONview 方法,让json数据格式化显示 JSONview 方法,让json数据格式化显示
https://www.jianshu.com/p/36434eb32144
math.random
返回介于 0(包含) ~ 1(不包含) 之间的一个随机数
在本例中,我们将取得介于 1 到 10 之间的一个随机数:
Math.floor((Math.random()*10)+1);
在本例中,我们将取得介于 1 到 100 之间的一个随机数:
Math.floor((Math.random()*100)+1);
随机数Math.random()公式
1. 0-x之间的随机数:
Math.round(Math.random()*x);
2. x至y之间的随机数
Math.round(Math.random()*(y-x)+x);
3. 1-x之间的随机数:
Math.ceil(Math.random()*x);
new 操作符具体干了什么?
阻止事件冒泡和默认行为
阻止事件冒泡:stopPropagation(),cancelBuble=true;
阻止默认行为:preventDefault,returnValue=false
Ajax 是什么? 如何创建一个Ajax?
1、创建XMLHttpRequest对象
2,创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
3,设置响应HTTP请求状态变化的函数.
4,发送HTTP请求.
5,获取异步调用返回的数据.
6,使用JavaScript和DOM实现局部刷新.
JavaScript 对象生命周期的理解?
async函数是什么,有什么作用?
async
函数可以理解为内置自动执行器的Generator
函数语法糖,它配合ES6
的Promise
近乎完美的实现了异步编程解决方案
addEventListener()
是符合W3C规范的标准方法; attachEvent()
是IE低版本的非标准方法addEventListener()
支持事件冒泡和事件捕获; - 而attachEvent()
只支持事件冒泡addEventListener()
的第一个参数中,事件类型不需要添加on
; attachEvent()
需要添加'on'
addEventListener()
会按照事件绑定的顺序依次执行,attachEvent()
会按照事件绑定的顺序倒序执行你有哪些性能优化的方法?
答:
(1) 减少http请求次数:CSS Sprites, JS、CSS源码压缩、图片大小控制合适;网页Gzip,CDN托管,data缓存 ,图片服务器。
(2) 前端模板 JS+数据,减少由于HTML标签导致的带宽浪费,前端用变量保存AJAX请求结果,每次操作本地变量,不用请求,减少请求次数
(3) 用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能。
(4) 当需要设置的样式很多时设置className而不是直接操作style。
(5) 少用全局变量、缓存DOM节点查找的结果。减少IO读取操作。
(6) 避免使用CSS Expression(css表达式)又称Dynamic properties(动态属性)。
(7) 图片预加载,将样式表放在顶部,将脚本放在底部 加上时间戳。
那些操作会造成内存泄漏?
JavaScript 内存泄露指对象在不需要使用它时仍然存在,导致占用的内存不能使用或回收
未使用 var 声明的全局变量
闭包函数(Closures)
循环引用(两个对象相互引用)
控制台日志(console.log)
移除存在绑定事件的DOM元素(IE)
ES6对String字符串类型做的常用升级优化
1.ES6
新增了字符串模板,在拼接大段字符串时,用`${}`取代以往的字符串相加的形式.使得字符串拼接看起来更加直观,更加优雅
2.ES6
在String
原型上新增了includes()
方法,用于取代传统的只能用indexOf
查找包含字符的方法(indexOf
返回-1
表示没查到不如includes
方法返回false
更明确,语义更清晰), 此外还新增了startsWith()
, endsWith(),
padStart()
,padEnd()
,repeat()
等方法,可方便的用于查找,补全字符串
ES6对Array数组类型做的常用升级优化
1.数组解构赋值。
解构赋值:
只要判定了等号两边的模式相同(解构),左边的变量就会被赋予对应的值(赋值),以前一次给一个变量赋值,解构赋值可以一次性给多个变量进行赋值。
2.扩展运算符
3.ES6
在Array
原型上新增了find()
方法,用于取代传统的只能用indexOf
查找包含数组项目的方法,
.此外还新增了copyWithin()
,includes()
, fill()
,flat()
等方法,可方便的用于字符串的查找,补全,转换等
举一些ES6对Object类型做的常用升级优化?(重要)
1.对象属性变量式声明。ES6
可以直接以变量形式声明对象属性或者方法,。比传统的键值对形式声明更加简洁,更加方便,语义更加清晰
let [apple, orange] = ['red appe', 'yellow orange'];
let myFruits = {apple, orange}; // let myFruits = {apple: 'red appe', orange: 'yellow orange'};
2.对象的解构赋值。 ES6
对象也可以像数组解构赋值那样,进行变量的解构赋值
let {apple, orange} = {apple: 'red appe', orange: 'yellow orange'};
3.ES6
在Object
原型上新增了is()
方法,做两个目标对象的相等比较,用来完善'==='
方法。'==='
方法中NaN === NaN //false
其实是不合理的,Object.is
修复了这个小bug
。(Object.is(NaN, NaN) // true)
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
4.ES6
在Object
原型上新增了assign()
方法,用于对象新增属性或者多个对象合并
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
谈一谈你了解ECMAScript6的新特性?
let a = 1;
const PI = 3.141592654;
var [a, b, c] = [1, 2, 3];
var sum =
${a + b};
Array.from($('li'));
[1, 2].push(...[3, 4, 5]);
Object.is(NaN, NaN);
let uid = Symbol('uid');
let set = new Set([1, 2, 2, 3]);
for(let val of arr){};
var promise = new Promise(func);
function* foo(x){yield x; return x*x;}
class Foo {}
export default func;
列举一下JavaScript数组和对象有哪些原生方法?
数组:
对象:
JavaScript 由以下三部分组成:
ECMAScript(核心):JavaScript 语言基础
DOM(文档对象模型):规定了访问HTML和XML的接口
BOM(浏览器对象模型):提供了浏览器窗口之间进行交互的对象和方法
JS的基本数据类型和引用数据类型,类型判断用哪些方法?
基本数据类型:undefined、null、boolean、number、string、symbol
引用数据类型:object、array、function
类型判断typeof
instanceof 用于实例和构造函数的对应。
JavaScript有几种类型的值?,你能画一下他们的内存图吗?
介绍JS有哪些内置对象?
数据封装类对象:Object、Array、Boolean、Number、String
其他对象:Function、Arguments、Math、Date、RegExp、Error
ES6新增对象:Symbol、Map、Set、Promises、Proxy、Reflect
重绘和回流(重排)的区别和关系?
解释JavaScript中的作用域与变量声明提升?
JavaScript作用域:
在Java、C等语言中,作用域为for语句、if语句或{}内的一块区域,称为作用域;
而在 JavaScript 中,作用域为function(){}内的区域,称为函数作用域。
Javascript作用链域?
全局函数无法查看局部函数的内部细节,但局部函数可以查看其上层的函数细节,直至全局细节
如果当前作用域没有找到属性或方法,会向上层作用域查找,直至全局函数,这种形式就是作用域链
JavaScript变量声明提升:
在JavaScript中,函数声明与变量声明经常被JavaScript引擎隐式地提升到当前作用域的顶部。
声明语句中的赋值部分并不会被提升,只有名称被提升
函数声明的优先级高于变量,如果变量名跟函数名相同且未赋值,则函数声明会覆盖变量声明
如果函数有多个同名参数,那么最后一个参数(即使没有定义)会覆盖前面的同名参数
var、let 及 const 区别?
var 存在提升,我们能在声明之前使用。let、const 因为暂时性死区的原因,不能在声明前使用
var 在全局作用域下声明变量会导致变量挂载在 window 上,其他两者不会
let 和 const 作用基本一致,但是后者声明的变量不能再次赋值
var定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。
let定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。
const用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。
块级作用域 {}
ES5 中作用域有:全局作用域、函数作用域。没有块作用域的概念。
ES6 中新增了块级作用域。块作用域由 { } 包括,if语句和 for语句里面的{ }也属于块作用域。
介绍JavaScript的原型,原型链?有什么特点?
原型:
JavaScript的所有对象中都包含了一个 [proto] 内部属性,这个属性所对应的就是该对象的原型
JavaScript的函数对象,除了原型 [proto] 之外,还预置了 prototype 属性
当函数对象作为构造函数创建实例时,该 prototype 属性值将被作为实例对象的原型 [proto]。
原型链:
当一个对象调用的属性/方法自身不存在时,就会去自己 [proto] 关联的前辈 prototype 对象上去找
如果没找到,就会去该 prototype 原型 [proto] 关联的前辈 prototype 去找。依次类推,直到找到属性/方法或 undefined 为止。从而形成了所谓的“原型链”
原型特点:
JavaScript对象是通过引用来传递的,当修改原型时,与之相关的对象也会继承这一改变
谈谈this对象的理解
this 总是指向函数的直接调用者
如果有 new 关键字,this 指向 new 出来的实例对象
在事件中,this指向触发这个事件的对象
IE下 attachEvent 中的this总是指向全局对象Window
eval是做什么的?
eval的功能是把对应的字符串解析成JS代码并运行
应该避免使用eval,不安全,非常耗性能(先解析成js语句,再执行)
由JSON字符串转换为JSON对象的时候可以用 eval(’(’+ str +’)’);
事件的代理/委托
事件委托是指将事件绑定目标元素的到父元素上,利用冒泡机制触发该事件
优点:
可以减少事件注册,节省大量内存占用
可以将事件应用于动态添加的子元素上
缺点:
使用不当会造成事件在不应该触发时触发
什么是函数节流?介绍一下应用场景和原理?
函数节流用于 onresize, onscroll 等短时间内会多次触发的事件
函数节流的原理:使用定时器做时间节流。
当触发一个事件时,先用 setTimout 让这个事件延迟一小段时间再执行。
如果在这个时间间隔内又触发了事件,就 clearTimeout 原来的定时器,
再 setTimeout 一个新的定时器重复以上流程。
Javascript如何实现继承?
构造继承
原型继承
实例继承
拷贝继承
原型prototype机制或apply和call方法去实现较简单,建议使用构造函数与原型混合方式
javascript创建对象的几种方式?
对象字面量的方式
用function来模拟无参的构造函数
用function来模拟参构造函数来实现(用this关键字定义构造的上下文属性)
用工厂方式来创建(内置对象)
用原型方式来创建
用混合方式来创建
谈谈This对象的理解
this总是指向函数的直接调用者(而非间接调用者)
如果有new关键字,this指向new出来的那个对象
在事件中,this指向触发这个事件的对象,特殊的是,IE中的attachEvent中的this总是指向全局对象Window
null,undefined 的区别?
null是一个表示"无"的对象,null 表示一个对象被定义了,值为“空值”,转为数值时为0;undefined 表示不存在这个值,undefined :是一个表示"无"的原始值或者说表示"缺少值",就是此处应该有一个值,但是还没有定义。当尝试读取时会返回 undefined,转为数值时为NaN。
undefined:
(1)变量被声明了,但没有赋值时,就等于undefined。
(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。
(3)对象没有赋值的属性,该属性的值为undefined。
(4)函数没有返回值时,默认返回undefined。
null:
(1) 作为函数的参数,表示该函数的参数不是对象。
(2) 作为对象原型链的终点。
(3)在验证null时,一定要使用 === ,因为 == 无法分别 null 和 undefined
说说你对闭包的理解
闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,利用闭包可以突破作用链域
使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。在js中,函数即闭包,只有函数才会产生作用域的概念
闭包有三个特性:
1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收
javascript 代码中的"use strict";是什么意思 ? 使用它区别是什么?
use strict是一种ECMAscript 5 添加的(严格)运行模式,这种模式使得 Javascript 在更严格的条件下运行,使JS编码更加规范化的模式,消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为
documen.write和 innerHTML的区别
document.write只能重绘整个页面
innerHTML可以重绘页面的一部分
判断一个变量是否为数组
Instanceof
isArray
Object.prototype.toString
对象合并(数组合并)
var obj = Object.assign(a,b)
babel是什么,有什么作用?
babel
是一个 ES6
转码器,可以将 ES6
代码转为 ES5
代码,以便兼容那些还没支持ES6
的平台
let有什么用,有了var为什么还要用let?
ES5
里面没有块级作用域是很不合理的。没有块级作用域回来带很多难以理解的问题,比如for
循环var
变量泄露,变量覆盖等问题。let
声明的变量拥有自己的块级作用域,且修复了var
声明变量带来的变量提升问题。
箭头函数
1.箭头函数内的this指向的是函数定义时所在的对象,而不是函数执行时所在的对象。
this
总是指向上一层的this
,如果上一层还是箭头函数,则继续向上指,直到指向到有自己this
的函数为止,并作为自己的this
2.箭头函数不能用作构造函数,因为它没有自己的this
,无法实例化
3.也是因为箭头函数没有自己的this,所以箭头函数 内也不存在arguments
对象。(可以用扩展运算符代替)
Set是什么,有什么作用?
Set
是ES6
引入的一种类似Array
的新的数据结构,Set
实例的成员类似于数组item
成员,区别是Set
实例的成员都是唯一,不重复的。这个特性可以轻松地实现数组去重
Map是什么,有什么作用?
Map
是ES6
引入的一种类似Object
的新的数据结构,Map
可以理解为是Object
的超集,打破了以传统键值对形式定义对象,对象的key
不再局限于字符串,也可以是Object
。可以更加全面的描述对象的属性
Promise是什么,有什么作用?
Promise
是ES6
引入的一个新的对象,他的主要作用是用来解决JS异步机制里,回调机制产生的“回调地狱”。它并不是什么突破性的API
,只是封装了异步回调形式,使得异步回调可以写的更加优雅,可读性更高,而且可以链式调用
for…in 和for…of有什么区别?
如果看到问题十六,那么就很好回答。问题十六提到了ES6统一了遍历标准,制定了可遍历对象,那么用什么方法去遍历呢?答案就是用for...of
。ES6规定,有所部署了载了Iterator
接口的对象(可遍历对象)都可以通过for...of
去遍历,而for..in
仅仅可以遍历对象
DOM操作——怎样添加、移除、移动、复制、创建和查找节点?
(1)创建新节点
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素 createTextNode() //创建一个文本节点
(2)添加、移除、替换、插入
appendChild() removeChild() replaceChild() insertBefore() //在已有的子节点前插入一个新的子节点
(3)查找
getElementsByTagName() //通过标签名称
getElementsByName() // 通过元素的Name属性的值(IE容错能力较强,会得到一个数组,其中包括id等于name值的)
getElementById() //通过元素Id,唯一性
JSON 的了解?
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它是基于JavaScript的一个子集。数据格式简单, 易于读写, 占用带宽小。
格式:采用键值对,例如:{'age':'12', 'name':'back'}
数组去重es5,es6
深拷贝浅拷贝
深拷贝两种方法:JSON.parse(JSON.stringify(obj)),第二种可以使用for...in加递归
浅拷贝:引用类型之间进行赋值操作,只是拷贝了引用类型的引用地址而非值,一个改变另一个也改变。
cookie 和session 的区别:
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
考虑到减轻服务器性能方面,应当使用COOKIE。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
描述 cookies、sessionStorage 和 localStorage 的区别?
与服务器交互:
cookie 是网站为了标示用户身份而储存在用户本地终端上的数据(通常经过加密)
cookie 始终会在同源 http 请求头中携带(即使不需要),在浏览器和服务器间来回传递
sessionStorage 和 localStorage 不会自动把数据发给服务器,仅在本地保存
存储大小:
cookie 数据根据不同浏览器限制,大小一般不能超过 4k
sessionStorage 和 localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
有期时间:
localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据
sessionStorage 数据在当前浏览器窗口关闭后自动删除
cookie 设置的cookie过期时间之前一直有效,与浏览器是否关闭无关
类的定义和继承的几种方式
类的定义:
方式一:用构造函数模拟类(传统写法)
方式二:用 class 声明(ES6的写法)
继承方式:
方式一:借助构造函数//这种方式,虽然改变了 this 的指向,但是,Child1 无法继承 Parent1 的原型。也就是说,如果我给 Parent1 的原型增加一个方法:这个方法是无法被 Child1 继承的。如下:
方法二:通过原型链实现继承//缺点是:如果修改 child1实例的name属性,child2实例中的name属性也会跟着改变。造成这种缺点的原因是:child1和child2共用原型。
方式三:组合的方式:构造函数 + 原型链//这种方式,能解决之前两种方式的问题:既可以继承父类原型的内容,也不会造成原型里属性的修改。这种方式的缺点是:让父亲Parent的构造方法执行了两次。
什么是同源策略?
同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。所以a.com下的js脚本采用ajax读取b.com里面的文件数据是会报错的。
• 不受同源策略限制的:
1、页面中的链接,重定向以及表单提交是不会受到同源策略限制的。
2、跨域资源的引入是可以的。但是js不能读写加载的内容。如嵌入到页面中的,,,
涉及面试题:为什么要使用模块化?都有哪几种方式可以实现模块化,各有什么特点?
使用一个技术肯定是有原因的,那么使用模块化可以给我们带来以下好处
立即执行函数
在早期,使用立即执行函数实现模块化是常见的手段,通过函数作用域解决了命名冲突、污染全局作用域的问题
(function(globalVariable){
globalVariable.test = function() {}
// ... 声明各种变量、函数都不会污染全局作用域
})(globalVariable)
AMD 和 CMD
鉴于目前这两种实现方式已经很少见到,所以不再对具体特性细聊,只需要了解这两者是如何使用的。
// AMD
define(['./a', './b'], function(a, b) {
// 加载模块完毕可以使用
a.do()
b.do()
})
// CMD
define(function(require, exports, module) {
// 加载模块
// 可以把 require 写在函数体的任意地方实现延迟加载
var a = require('./a')
a.doSomething()
})
CommonJS
CommonJS
最早是Node
在使用,目前也仍然广泛使用,比如在Webpack
中你就能见到它,当然目前在Node
中的模块管理已经和CommonJS
有一些区别了
// a.js
module.exports = {
a: 1
}
// or
exports.a = 1
// b.js
var module = require('./a.js')
module.a // -> log 1
ar module = require('./a.js')
module.a
// 这里其实就是包装了一层立即执行函数,这样就不会污染全局变量了,
// 重要的是 module 这里,module 是 Node 独有的一个变量
module.exports = {
a: 1
}
// module 基本实现
var module = {
id: 'xxxx', // 我总得知道怎么去找到他吧
exports: {} // exports 就是个空对象
}
// 这个是为什么 exports 和 module.exports 用法相似的原因
var exports = module.exports
var load = function (module) {
// 导出的东西
var a = 1
module.exports = a
return module.exports
};
// 然后当我 require 的时候去找到独特的
// id,然后将要使用的东西用立即执行函数包装下,over
另外虽然
exports
和module.exports
用法相似,但是不能对exports
直接赋值。因为var exports = module.exports
这句代码表明了exports
和module.exports
享有相同地址,通过改变对象的属性值会对两者都起效,但是如果直接对exports
赋值就会导致两者不再指向同一个内存地址,修改并不会对module.exports
起效
ES Module
ES Module
是原生实现的模块化方案,与CommonJS
有以下几个区别
CommonJS
支持动态导入,也就是 require(${path}/xx.js)
,后者目前不支持,但是已有提案CommonJS
是同步导入,因为用于服务端,文件都在本地,同步导入即使卡住主线程影响也不大。而后者是异步导入,因为用于浏览器,需要下载文件,如果也采用同步导入会对渲染有很大影响CommonJS
在导出时都是值拷贝,就算导出的值变了,导入的值也不会改变,所以如果想更新值,必须重新导入一次。但是 ES Module
采用实时绑定的方式,导入导出的值都指向同一个内存地址,所以导入值会跟随导出值变化ES Module
会编译成 require/exports
来执行的// 引入模块 API
import XXX from './a.js'
import { XXX } from './a.js'
// 导出模块 API
export function a() {}
export default function() {}