本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1、模块化发展历程
| 模块化方案 | 全称 | 适用范围 |
|---|---|---|
| CJS | CommonJS | Node 端 |
| AMD | Async Module Definition | 浏览器 |
| CMD | Common Module Definition | 浏览器 |
| UMD | Universal Module Definition | Node 端和浏览器 |
| ESM | ES Module | Node 端和浏览器 |
可从AMD、CMD、CommonJS、UMD、ES Module 这几个角度考虑。
模块化主要是用来抽离公共代码,隔离作用域,避免变量冲突等。
AMD: 使用requireJS 来编写模块化,特点:依赖必须提前声明好。
define('./index.js',function(code){
// code 就是index.js 返回的内容
})
CMD: 使用seaJS 来编写模块化,特点:支持动态引入依赖文件。
define(function(require, exports, module) {
var indexCode = require('./index.js');
})
UMD:兼容AMD、CommonJS 模块化语法。
其中 AMD 、CMD 、 UMD 都已经属于有点过去式的模块化方案了,在新的业务里,结合各种编译工具,可以直接用最新的 ESM 方案来实现模块化。
CommonJS: nodejs 中自带的模块化。原本是服务端的模块化标准(设计之初也叫 ServerJS ),是为 JavaScript 设计的用于浏览器之外的一个模块化方案, Node 默认支持了该规范,在 Node 12 之前也只支持 CJS ,但从 Node 12 开始,已经同时支持 ES Module 的使用
var fs = require('fs');
ES Modules: ES6 引入的模块化,支持import 来引入另一个 js 。
ESM 使用 export default (默认导出)和 export (命名导出)这两个语法导出模块,和 CJS 一样, ESM 也可以导出任意合法的 JavaScript 类型,例如:字符串、布尔值、对象、数组、函数等等。
使用 import ... from ... 导入模块,在导入的时候,如果文件扩展名是 .js 则可以省略文件名后缀,否则需要把扩展名也完整写出来。
import a from 'a';
2、什么是防抖和节流?有什么区别?如何实现?
防抖
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。
解题思路 : 每次触发事件时都取消之前的延时调用方法
function debounce(func, delay) {
let timer = null;// 创建一个标记用来存放定时器的返回值
return function(...args) {
timer && clearTimeout(timer) // 清除定时器(存在时)
timer = setTimeout(() => { // 创建一个新的定时器, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
func.apply(this, args)
}, delay)
}
},
function sayHi() {
console.log('防抖成功');
}
var inp = document.getElementById('inp');
inp.addEventListener('input', debounce(sayHi)); // 防抖
节流
高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率。
解题思路 : 每次触发事件时都判断当前是否有等待执行的延时函数
function throttle(fn) {
let runTime = true;
return function () {
if (!runTime) return;
runTime = false;
setTimeout(() => {
fn.apply(this, arguments);
runTime = true;
}, 500);
};
}
function sayHi(e) {
console.log(e.target.innerWidth, e.target.innerHeight);
}
window.addEventListener('resize', throttle(sayHi));
3、meta viewport
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
width:控制 viewport 的大小,可以指定的一个值,如 600,或者特殊的值,如 device-width 为设备的宽度(单位为缩放为 100% 时的 CSS 的像素)。
height:和 width 相对应,指定高度。
initial-scale:初始缩放比例,也即是当页面第一次 load 的时候缩放比例。
maximum-scale:允许用户缩放到的最大比例。
minimum-scale:允许用户缩放到的最小比例。
user-scalable:用户是否可以手动缩放
4、Cookie、sessionStorage、localStorage的区别
共同点:都是保存在浏览器端,并且是同源的
- Cookie:cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下,存储的大小很小只有4K左右。 (tip:可以在浏览器和服务器端来回传递,存储容量小,只有大约4K左右,sessionStorage 和 localStorage 虽然也有存储大小的限制,但比 cookie 大得多,可以达到 5M 或更大。)
- sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持,localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。(key:本身就是一个回话过程,关闭浏览器后消失,session为一个回话,当页面不同即使是同一页面打开两次,也被视为同一次回话)
- localStorage:localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。(key:同源窗口都会共享,并且不会失效,不管窗口或者浏览器关闭与否都会始终生效)
备注:(做私有定制)
- 保存用户登录状态。例如将用户id存储于一个cookie内,这样当用户下次访问该页面时就不需要重新登录了,现在很多论坛和社区都提供这样的功能。 cookie还可以设置过期时间,当超过时间期限后,cookie就会自动消失。因此,系统往往可以提示用户保持登录状态的时间:常见选项有一周、一个月、三个月等。
- 跟踪用户行为。例如一个天气预报网站,能够根据用户选择的地区显示当地的天气情况。如果每次都需要选择所在地是烦琐的,当利用了 cookie后就会显得很人性化了,系统能够记住上一次访问的地区,当下次再打开该页面时,它就会自动显示上次用户所在地区的天气情况。因为一切都是在后 台完成,所以这样的页面就像为某个用户所定制的一样,使用起来非常方便
- 定制页面。如果网站提供了换肤或更换布局的功能,那么可以使用cookie来记录用户的选项,例如:背景色、分辨率等。当用户下次访问时,仍然可以保存上一次访问的界面风格。
5、数组扁平化
二维数组时,可以简单使用flat()方法
let arr = [[1,2],[3,4],[5,6]];
let arr = [[1,2],[3,4],[5,6]].flat();
console.log(arr); // [1,2,3,4,5,6]
如果是多维数组,则flat()将达不到效果,需要给flat()传一个参数
var newArray = arr.flat([depth])
参数:depth,可选,指定要提取嵌套数组的结构深度,默认值为 1。
let arr = [[1,2],[3,4],[5,6]].flat(Infinity);
console.log(arr); // [1,2,3,4,5,6]
迭代实现 (ES6扩展运算符…)
const arr = [1,2,[3,4,5,[6,7],8],9,10,[11,[12,13]]];
const flatten = (arr) => {
while(arr.some(item=>Array.isArray(item))){
arr=[].concat(…arr);
}
return arr;
}
console.log(flatten(arr)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
递归实现
const arr = [1,2,[3,4,5,[6,7],8],9,10,[11,[12,13]]];
const flatten = (arr) => {
let result = [];
arr.forEach((item, i, arr) => {
if (Array.isArray(item)) {
result = result.concat(flatten(item));
} else {
result.push(arr[i])
}
})
return result;
};
console.log(flatten(arr));// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
concat + apply实现
const arr = [1,2,[3,4,5,[6,7],8],9,10,[11,[12,13]]];
const flatten = (arr) => {
while (arr.some(item => Array.isArray(item))){
arr = [].concat.apply([], arr);
}
return arr;
}
console.log(flatten(arr));// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
7、while和if的区别
while语句属于循环语句,如果条件为true,则会继续执行,直到false为止,即会进行多次判断(除非一开始条件就是错的)。
if语句属于条件判断语句,如果条件是true,则继续执行,为false则跳出语句不执行,只会进行单次判断。
while与if语句的最大的相同点是都有至少一步的判断。
最大的不同点是:if语句运行完毕后,接着运行下面的语句。而while中的执行语句运行完毕后,还要进行继续判断条件是否符合循环条件,根据判断的条件,返回执行语句或继续运行下面的程序。
8、computed和watch的区别?
计算属性computed :
- 支持缓存,只有依赖数据发生改变,才会重新进行计算
- 不支持异步,当
computed内有异步操作时无效,无法监听数据的变化 computed属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者 父组件传递的props中的数据通过计算得到的值- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用
computed - 如果
computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中 的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
侦听属性watch:
- 不支持缓存,数据变,直接会触发相应的操作;
watch支持异步;- 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
- 当一个属性发生变化时,需要执行对应的操作;一对多;
- 监听数据必须是
data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数。immediate:组件加载立即触发回调函数执行;deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到。
注:当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。这是watch和computed最大的区别。