2019最新前端面试题整理(持续更新中)

1.写出下面程序的运行结果:
console.log([] == ![])	// true
console.log([] == [])	// false
console.log([] == {})	// false
console.log(new String('a') == new String('a')) // false
console.log(isNaN(NaN))	// true
console.log(isNaN('32131dsafdas'))	// true
console.log(NaN === NaN)	// false
console.log(NaN === undefined)	// false
console.log(undefined === undefined)	// true
console.log(typeof NaN)	 // number

console.log({} + [])	// 0
console.log([] + {})	// "[object Object]"
console.log({} + {})	// "[object Object][object Object]"
console.log([]+ [])	// ""
console.log({} + 1)	// 1
console.log([] + 1)	// "1"

隐式类型转换参考:https://blog.csdn.net/m0_37793545/article/details/87862847html

2.写出下面程序的运行结果:
var name = 'world';
  (function () {
    if (typeof name === 'undefined') {
      var name = 'jack'
      console.log('hi' + name)
    } else {
      console.log('hello' + name)
    }
  })()
  
  // hijack
3.写出下面程序的运行结果:
var test = (function (a) {
    this.a = a;
    return function (b) {
      return this.a + b
    }
  }(function (a, b) {
    return a
  }(1, 2)))
  test(4)
  
  // 5
4.写出下面程序的运行结果:
var x = 1,
    y = z = 0

  function add(n) {
    return n = n + 1
  }
  y = add(x)

  function add(n) {
    return n = n + 3
  }
  z = add(x)
  console.log(x, y, z)
  
  // 1 4 4
5.写出下面程序的运行结果:
function bar() {
    return foo;
    foo = 10;

    function foo() {}
    var foo = 11
  }
  alert(typeof bar())

  // function
  (2)
  var a = 1
  function a() {}
  console.log(a) // 1
  (3)
  var b = 1
  if (true) {
  	console.log(b)	//	1
  	var b = 2
  	var c = 3
  	console.log(c)	//	3
  }
  console.log(b)	//	2
  console.log(c)	//	3
6.写出下面程序的运行结果:
var x = 3;
  var foo = {
    x: 2,
    baz: {
      x: 1,
      bar: function () {
        return this.x
      }
    }
  }
  var go = foo.baz.bar
  go()
  foo.baz.bar()
  
  // 3
  // 1
7.写出下面程序的运行结果:
console.log([1, 2] + [2, 1])
  
  // 1,2,2,1
8.判断一串字符串是否为回文
var test = '12344321'
  var test1 = test.split('').reverse().join('')
  console.log(test === test1)
9.数组去重,数组排序
//去重:
  (1)Array.from(new Set([1,2,3,5,2,1,22,'1']))
  (2)
  var array = [1, 2, 1, 1, '1']; 
  function unique(array) {
    var res = array.filter(function(item, index, array){
      return array.indexOf(item) === index;
    })
    return res;
  }
  console.log(unique(array));
  (3)
  function unique(arr) {
    arr = arr.sort()
    let res = []
    for (let i = 0; i < arr.length; i++) {
      if (arr[i] !== arr[i-1]) {
          res.push(arr[i])
      }
    }
    return res
  }

更多去重方式:http://www.javashuo.com/article/p-pmixedhh-h.html
数组排序总结:http://www.javashuo.com/article/p-azrzzgup-dz.html前端

10.js继承的实现方式
// (1) 借助构造函数实现继承
  function Parent() {
    this.name = 'parent'
  }
  function Child() {
    Parent.call(this)
    this.age = 18
  }
  // 这种方式只能实现部分继承,即父类的构造方法中的属性,子类能够继承,其缺点是,父类原型上的属性或方法,子类没法继承。
  // (2)借助原型链实现继承
  function Parent() {
    this.name = 'parent'
    this.play = [1, 2, 3]
  }
  function Child() {
    this.age = 18
  }
  Child.prototype = new Parent()
  // 这种继承方式的缺点是用子类Child实例化两个对象后,var s1 = new Child(); var s2 = new Child(); s1.play.push(4); console.log(s2.play); 也会打印出[1,2,3,4],其缘由是两个对象的__proto__指向了同一个原型对象。
  // (3)组合方式(继承的完美实现)
  function Parent() {
    this.name = 'parent'	
  }
  function Child() {
    //	将父对象的构造函数绑定在子对象上
    Parent.call(this)
    this.age = 18
  }
  //  将Parent的prototype拷贝给Child
  Child.prototype = Object.create(Parent.prototype)
  //  修正Child的prototype对象指向的构造函数
  Child.prototype.constructor = Child
11.JavaScript事件循环机制相关问题
// 请写出如下代码执行的顺序

setTimeout(function () {
    console.log(1);
});

new Promise(function(resolve,reject){
    console.log(2)
    resolve(3)
}).then(function(val){
    console.log(val);
})
console.log(4);

// 2 4 3 1
在同步代码执行完成后才回去检查是否有异步任务完成,并执行对应的回调,而微任务又会在宏任务以前执行。
同步代码=>异步代码(微任务=>宏任务)
宏任务: setTimeout
微任务: Promise.then
12.伪数组转数组
var a={length:2,0:'aaa',1:'bbb'};  
// ES6:
Array.from(a)
// ES5:
Array.prototype.slice.call(a);//  ["aaa", "bbb"]   

var a={length:2};  
Array.prototype.slice.call(a);//  [undefined, undefined]
13.写出下面程序的运行结果:
function Foo() {
    getName = function() {
      alert(1)
    }
    return this
  }
  Foo.getName = function() {
    alert(2)
  }
  Foo.prototype.getName = function() {
    alert(3)
  }
  var getName = function() {
    alert(4)
  }
  function getName() {
    alert(5)
  }
  //请写出如下输出结果:
  Foo.getName() // 2
  getName() // 4
  Foo().getName() // 1
  getName() // 1
  new Foo.getName() // 2
  new Foo().getName() // 3
  new new Foo().getName() // 3
  // 详细解释 https://www.jb51.net/article/79461.htm
14. 函数防抖与函数节流

(1) 函数防抖vue

function debounce(func, delay) {
      var timeout
      return function(e) {
        console.log('清除', timeout, e.target.value)
        clearTimeout(timeout)
        var context = this,
          args = arguments
        console.log('新的', timeout, e.target.value)
        timeout = setTimeout(function() {
          console.log('----')
          func.apply(context, args)
        }, delay)
      }
    }

    var validate = debounce(function(e) {
      console.log('change', e.target.value, new Date() - 0)
    }, 380)
    // 绑定监听
    document.querySelector('input').addEventListener('input', validate)

(2) 函数节流node

// 函数节流
	    function throttle(fn, threshhold) {
      var timeout
      var start = new Date()
      var threshhold = threshhold || 160
      return function() {
        var context = this,
          args = arguments,
          curr = new Date() - 0

        clearTimeout(timeout) //老是干掉事件回调
        if (curr - start >= threshhold) {
          console.log('now', curr, curr - start) //注意这里相减的结果,都差很少是160左右
          fn.apply(context, args) //只执行一部分方法,这些方法是在某个时间段内执行一次
          start = curr
        } else {
          //让方法在脱离事件后也能执行一次
          timeout = setTimeout(function() {
            fn.apply(context, args)
          }, threshhold)
        }
      }
    }
    var mousemove = throttle(function(e) {
      console.log(e.pageX, e.pageY)
    })

    // 绑定监听
    document.querySelector('#panel').addEventListener('mousemove', mousemove)
15. 什么是hash函数

hash(散列、杂凑)函数,是将任意长度的数据映射到有限长度的域上。直观解释起来,就是对一串数据m进行杂糅,输出另外一段固定长度的数据h,做为这段数据的特征(指纹)。也就是说,不管数据块m有多大,其输出值h为固定长度。jquery

hashmap,hash值(key)存在的目的是加速键值对的查找nginx

详解:https://www.zhihu.com/question/26762707web

16. Vue 组件 data 为何必须是函数

每一个实例能够维护一份被返回对象的独立的拷贝,不然;将致使多个实例共享一个对象,其中一个组件改变data属性值,其它实例也会受到影响。算法

17. jQuery.extend()和jQuery.fn.extend()的区别
jQuery.extend()为扩展jQuery类自己.为类添加新的方法。如 $.wang()
jquery.fn.extend(object);给jQuery对象添加方法。如 $('#test').wang()
18. Vue中的虚拟Dom diff 算法
(1)所谓的virtual dom,也就是虚拟节点。它经过JS的Object对象模拟DOM中的节点,而后再经过特定的render方法将其渲染成真实的DOM节点
dom diff 则是经过JS层面的计算,返回一个patch对象,即补丁对象,在经过特定的操做解析patch对象,完成页面的从新渲染
(2)DOM DIFF
比较两棵DOM树的差别是Virtual DOM算法最核心的部分.简单的说就是新旧虚拟dom 的比较,若是有差别就以新的为准,而后再插入的真实的dom中,从新渲染。
比较只会在同层级进行, 不会跨层级比较。新旧虚拟dom比较的时候,是先同层比较,同层比较完看看时候有儿子,有则须要继续比较下去,直到没有儿子。

比较后会出现四种状况:
一、此节点是否被移除 -> 添加新的节点 
二、属性是否被改变 -> 旧属性改成新属性
三、文本内容被改变-> 旧内容改成新内容
四、节点要被整个替换 -> 结构彻底不相同 移除整个替换

详解:http://www.javashuo.com/article/p-rsuyzvts-kc.htmljson

19.常见Http状态码
// 100  Continue  继续,通常在发送post请求时,已发送了http header以后服务端将返回此信息,表示确认,以后发送具体参数信息
// 200  OK   正常返回信息
// 201  Created  请求成功而且服务器建立了新的资源
// 202  Accepted  服务器已接受请求,但还没有处理
// 301  Moved Permanently  请求的网页已永久移动到新位置。
// 302 Found  临时性重定向。
// 303 See Other  临时性重定向,且老是使用 GET 请求新的 URI。
// 304  Not Modified  自从上次请求后,请求的网页未修改过。

// 400 Bad Request  服务器没法理解请求的格式,客户端不该当尝试再次使用相同的内容发起请求。
// 401 Unauthorized  请求未受权。
// 403 Forbidden  禁止访问。
// 404 Not Found  找不到如何与 URI 相匹配的资源。

// 500 Internal Server Error  最多见的服务器端错误。
// 503 Service Unavailable 服务器端暂时没法处理请求(多是过载或维护)。
20. 什么状况出现跨域,如何解决?

同源策略是浏览器的一个安全功能,不一样源的客户端脚本在没有明确受权的状况下,不能读写对方资源。若地址里面的协议、域名和端口号均相同则属于同源。segmentfault

jsonp跨域、nginx反向代理、node.js中间件代理跨域、后端设置http header、后端在服务器上设置cors。

21. 重绘和回流

重绘:当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并从新绘制它,这个过程称为重绘。

回流:当Render Tree(DOM)中部分或所有元素的尺寸、结构、或某些属性发生改变时,浏览器从新渲染部分或所有文档的过程称为回流。
回流要比重绘消耗性能开支更大。
回流必将引发重绘,重绘不必定会引发回流。

详解:http://www.javashuo.com/article/p-npxyfplr-cw.html

22. 浏览器从加载到渲染的过程,好比输入一个网址到显示页面的过程。

加载过程:

  • 浏览器根据 DNS 服务器解析获得域名的 IP 地址
  • 向这个 IP 的机器发送 HTTP 请求 服务器收到、处理并返回 HTTP 请求
  • 浏览器获得返回内容

渲染过程:

  • 根据 HTML 结构生成 DOM 树
  • 根据 CSS 生成 CSSOM
  • 将 DOM 和 CSSOM 整合造成 RenderTree
  • 根据 RenderTree 开始渲染和展现
  • 遇到<script>时,会执行并阻塞渲染
23. 浏览器缓存机制

强制缓存:catch-control,expires,catch-control优先级高。

协商缓存:Last-Modified / If-Modified-Since和Etag / If-None-Match,其中Etag / If-None-Match的优先级比Last-Modified / If-Modified-Since高。

详解:https://juejin.im/entry/5ad86c16f265da505a77dca4
http://www.javashuo.com/article/p-qbijdnlt-bx.html

24. Vue双向绑定原理

详解:http://www.javashuo.com/article/p-sdwfvzai-eb.html

25. 路由导航钩子(导航守卫)

首页能够控制导航跳转,beforeEach,afterEach等,通常用于页面title的修改。一些须要登陆才能调整页面的重定向功能。

beforeEach主要有3个参数to,from,next:

  1. to:route即将进入的目标路由对象
  2. from:route当前导航正要离开的路由
  3. next:function必定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。能够控制网页的跳转。

afterEach和beforeResolve(全局解析守卫)没有next参数

路由独享的守卫:beforeEnter,参数同全局前置守卫
组件内的守卫:beforeRouteEnter(组件实例未建立,不能使用this),beforeRouteUpdate,beforeRouteLeave

详解:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

26. Vue的路由模式,有什么区别?

hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;
特色:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动做,对服务端安全无用,hash不会重加载页面。
hash 模式下,仅 hash 符号以前的内容会被包含在请求中,如 http://www.xxx.com,所以对于后端来讲,即便没有作到对路由的全覆盖,也不会返回 404 错误。

history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()能够对浏览器历史记录栈进行修改,以及popState事件的监听到状态变动。
history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端若是缺乏对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还须要后台配置支持……因此呢,你要在服务端增长一个覆盖全部状况的候选资源:若是 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”

27. 安全问题,如 XSS 和 CSRF
Xss
  • 跨站脚本攻击,是一种网站应用程序的安全漏洞攻击,是代码注入的一种。常见方式是将恶意代码注入合法代码里隐藏起来,再诱发恶意代码,从而进行各类各样的非法活动。

防范:全部用户输入的都是不可信的,输入过滤和转义,重要cookie设置为HttpOnly

CSRF
  • 跨站请求伪造,也称 XSRF,是一种挟制用户在当前已登陆的Web应用程序上执行非本意的操做的攻击方法。与 XSS 相比,XSS利用的是用户对指定网站的信任,CSRF利用的是网站对用户网页浏览器的信任。

防范:

  1. 验证Http Referer字段,判断请求来源是否合法,Referer记录了该 HTTP 请求的来源地址
  2. 在请求地址中添加 token 并验证
  3. 在 HTTP 头中自定义属性并验证

详解:https://www.jianshu.com/p/64a413ada155

28. Css实现文本溢出显示省略号
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
29. js实现相似于add(1)(2)(3)调用方式的方法

简单方法:

var add = function(a){
    return function(b){
        return function(c){
            return a+b+c;
        };
    };
};

可是这样的代码在add(1)(2)或者add(1)(2)(3)(4)时便不对了。
正确实现:

function add () {
    var args = Array.prototype.slice.call(arguments);
 
    var fn = function () {
        var arg_fn = Array.prototype.slice.call(arguments);
        return add.apply(null, args.concat(arg_fn));
    }
    
 	// 重写valueOf方法,是为了处理隐式类型转换
    fn.valueOf = function () {
        return args.reduce(function(a, b) {
            return a + b;
        })
    }
 
    return fn;
}

详解:https://www.cnblogs.com/coco1s/p/6509141.html
https://blog.csdn.net/m0_37793545/article/details/87862847

30. 定义一个log方法,让它能够代理console.log的方法(支持传入n个参数)
function log(){ 
 console.log.apply(console, arguments); 
}
31. 当new Foo()时发生了什么
  1. 建立了一个新对象
  2. 将新建立的空对象的隐式原型指向其构造函数的显示原型。
  3. 将this指向这个新对象
  4. 若是无返回值或者返回一个非对象值,则将新对象返回;若是返回值是一个新对象的话那么直接直接返回该对象。
手写一个new实现
function create() {
	// 建立一个空的对象
    var obj = new Object(),
	// 得到构造函数,arguments中去除第一个参数
    Con = [].shift.call(arguments);
	// 连接到原型,obj 能够访问到构造函数原型中的属性
    obj.__proto__ = Con.prototype;
	// 绑定 this 实现继承,obj 能够访问到构造函数中的属性
    var ret = Con.apply(obj, arguments);
	// 优先返回构造函数返回的对象
	return ret instanceof Object ? ret : obj;
};

详解:《JS高程》 6.2.2 《你不知道的Javascript》2.2.4

32. Css样式的百分比都相对于谁?
  • 相对于父级宽度的:
    max-width、min-width、width、left、right、text-indent、padding、margin、grid-template-columns、grid-auto-columns、column-gap 等;

  • 相对于父级高度的:

    max-height、min-height、height、top、bottom、grid-template-rows、grid-auto-rows、row-gap 等;

  • 相对于主轴长度的:

    flex-basis 等;

  • 相对于继承字号的:

    font-size 等;

  • 相对于自身字号的:

    line-height 等;

  • 相对于自身宽高的:

    border-radius、background-size、border-image-width、transform: translate()、transform-origin、zoom、clip-path 等;

  • 相对于行高的:

    vertical-align 等;

  • 特殊算法的:
    background-position (方向长度 / 该方向除背景图以外部分总长度)、border-image-slice (相对于图片尺寸)、filter 系列函数等;

若是自身设置 position: absolute,“父级”指:Boring:破坏文档流的div高度设为百分比是相对谁而言的?

若是 position: fixed,“父级”指视口(父级不存在 transform 为非 none 值的状况下)。

来源:知乎
连接:https://www.zhihu.com/question/36079531/answer/65809167

33. 什么是闭包? 堆栈溢出? 内存泄漏? 哪些操做会形成内存泄漏?怎么样防止内存泄漏?

闭包:就是可以读取其余函数内部变量的函数。
堆栈溢出:就是不顾堆栈中分配的局部数据块大小,向该数据块写入了过多的数据,致使数据越界,结果覆盖了别的数据。常常会在递归中发生。
内存泄露是指:用动态存储分配函数内存空间,在使用完毕后未释放,致使一直占据该内存单元。直到程序结束。指任何对象在您再也不拥有或须要它以后仍然存在。

形成内存泄漏:
setTimeout 的第一个参数使用字符串而非函数的话,会引起内存泄漏。
闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)
防止内存泄露:
一、不要动态绑定事件;
二、不要在动态添加,或者会被动态移除的dom上绑事件,用事件冒泡在父容器监听事件;
三、若是要违反上面的原则,必须提供destroy方法,保证移除dom后事件也被移除,这点能够参考Backbone的源代码,作的比较好;
四、单例化,少建立dom,少绑事件。

34. 移动端上什么是点击穿透?如何解决?

点击穿透现象有3种:
点击穿透问题:点击蒙层(mask)上的关闭按钮,蒙层消失后发现触发了按钮下面元素的click事件跨页面点击穿透问题:若是按钮下面刚好是一个有href属性的a标签,那么页面就会发生跳转另外一种跨页面点击穿透问题:此次没有mask了,直接点击页内按钮跳转至新页,而后发现新页面中对应位置元素的click事件被触发了

解决方案:
一、只用touch
最简单的解决方案,完美解决点击穿透问题
把页面内全部click所有换成touch事件( touchstart 、’touchend’、’tap’)

二、只用click
下下策,由于会带来300ms延迟,页面内任何一个自定义交互都将增长300毫秒延迟

三、tap后延迟350ms再隐藏mask
改动最小,缺点是隐藏mask变慢了,350ms仍是能感受到慢的

四、pointer-events
比较麻烦且有缺陷, 不建议使用mask隐藏后,给按钮下面元素添上 pointer-events: none; 样式,让click穿过去,350ms后去掉这个样式,恢复响应缺陷是mask消失后的的350ms内,用户能够看到按钮下面的元素点着没反应,若是用户手速很快的话必定会发现

35. TCP协议中的三次握手,四次挥手

TCP是主机对主机层的传输控制协议,提供可靠的链接服务,采用三次握手确认创建一个链接,四次挥手断开链接

位码即tcp标志位,有6种标示:SYN(synchronous创建联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)

Sequence number(顺序号码) Acknowledge number(确认号码)

三次握手:

第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器并进入SYN_SEND状态,主机B由SYN=1知道,A要求创建联机;

第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包并进入SYN_RECV状态

第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则链接创建成功,客户端和服务器进入ESTABLISHED状态。

完成三次握手,主机A与主机B开始传送数据。

三次握手

四次挥手

中断链接能够是Client端发起,也能够是Server端发起

假设Client端发起中断链接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",可是若是你还有数据没有发送完成,则没必要急着关闭Socket,能够继续发送数据。因此你先发送ACK,“告诉Client端,你的请求我收到了,可是我还没准备好,请继续你等个人消息”。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端肯定数据已发送完成,则向Client端发送FIN报文,“告诉Client端,好了,我这边数据发完了,准备好关闭链接了”。Client端收到FIN报文后,"就知道能够关闭链接了,可是他仍是不相信网络,怕Server端不知道要关闭,因此发送ACK后进入TIME_WAIT状态,若是Server端没有收到ACK则能够重传。“,Server端收到ACK后,“就知道能够断开链接了”。Client端等待了2MSL后依然没有收到回复,则证实Server端已正常关闭,那好,我Client端也能够关闭链接了。Ok,TCP链接就这样关闭了!
四次挥手
相关问题:
1.为何创建链接协议是三次握手,而关闭链接倒是四次握手呢?

这是由于服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它能够把ACK和SYN(ACK起应答做用,而SYN起同步做用)放在一个报文里来发送。但关闭链接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你全部的数据都所有发送给对方了,因此你能够未必会立刻会关闭SOCKET,也即你可能还须要发送一些数据给对方以后,再发送FIN报文给对方来表示你赞成如今能够关闭链接了,因此它这里的ACK报文和FIN报文多数状况下都是分开发送的.

2.为何TIME_WAIT状态还须要等2MSL后才能返回到CLOSED状态?

这是由于虽然双方都赞成关闭链接了,并且握手的4个报文也都协调和发送完毕,按理能够直接回到CLOSED状态(就比如从SYN_SEND状态到ESTABLISH状态那样);可是由于咱们必需要假想网络是不可靠的,你没法保证你最后发送的ACK报文会必定被对方收到,所以对方处于LAST_ACK状态下的SOCKET可能会由于超时未收到ACK报文,而重发FIN报文,因此这个TIME_WAIT状态的做用就是用来重发可能丢失的ACK报文。

详解解释:http://www.cnblogs.com/cy568searchx/p/3711670.html

36. 写出下面程序的运行结果
// 坑人的连续赋值问题
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x);  // undefinde
console.log(b);  //  {n:1,x:{n:2}}

详解:https://www.zhihu.com/question/41220520

37. 写出下面程序的运行结果
function f1(){
	var n=999
	nAdd=function(){
		n+=1
		console.log(n)
	}
	function f2(){
		console.log(n)
	}
	return f2
}

f1()()  //  999
nAdd()  //  1000
nAdd()  //  1001
38. require和import的区别
遵循的规范不一样
  1. require/exports是CommonJS的一部分
  2. import/export是ES6新规范
书写形式不同

require/exports 的用法只有如下三种简单的写法:

const fs = require('fs')
exports.fs = fs
module.exports = fs

而 import/export 的写法就多种多样:

import fs from 'fs'
import {default as fs} from 'fs'
import * as fs from 'fs'
import {readFile} from 'fs'
import {readFile as read} from 'fs'
import fs, {readFile} from 'fs'

export default fs
export const fs
export function readFile
export {readFile, read}
export * from 'fs'
本质上的区别
  1. require的引用至关于拷贝,原js内再发生变化不会影响到拷贝的值
  2. import的引用是引用了地址

详解:https://www.zhihu.com/question/56820346

39. 前端模块化CommonJS,AMD,CMD,ES6
  1. commonJS用同步的方式加载模块。node
  2. AMD规范采用异步方式加载模块,模块的加载不影响它后面语句的运行。全部依赖这个模块的语句,都定义在一个回调函数中,等到加载完成以后,这个回调函数才会运行。特色:前置依赖,提早执行。require.js
  3. CMD与AMD很相似,CMD推崇依赖就近、延迟执行。此规范实际上是在sea.js推广过程当中产生的。sea.js
  4. ES6的模块不是对象,import命令会被 JavaScript 引擎静态分析,在编译时就引入模块代码,而不是在代码运行时加载,因此没法实现条件加载。也正由于这个,使得静态分析成为可能。import,export。