手写发布订阅

class EventListener {listeners = {};on(name, fn) {(this.listeners[name] || (this.listeners[name] = [])).push(fn)}once(name, fn) {let tem = (...args) => {this.removeListener(name, fn)fn(...args)}fn.fn = temthis.on(name, tem)}removeListener(name, fn) {if (this.listeners[name]) {this.listeners[name] = this.listeners[name].filter(listener => (listener != fn && listener != fn.fn))}}removeAllListeners(name) {if (name && this.listeners[name]) delete this.listeners[name]this.listeners = {}}emit(name, ...args) {if (this.listeners[name]) {this.listeners[name].forEach(fn => fn.call(this, ...args))}}
}

前端进阶面试题详细解答

回流与重绘的概念及触发条件

(1)回流

当渲染树中部分或者全部元素的尺寸、结构或者属性发生变化时,浏览器会重新渲染部分或者全部文档的过程就称为回流

下面这些操作会导致回流:

  • 页面的首次渲染
  • 浏览器的窗口大小发生变化
  • 元素的内容发生变化
  • 元素的尺寸或者位置发生变化
  • 元素的字体大小发生变化
  • 激活CSS伪类
  • 查询某些属性或者调用某些方法
  • 添加或者删除可见的DOM元素

在触发回流(重排)的时候,由于浏览器渲染页面是基于流式布局的,所以当触发回流时,会导致周围的DOM元素重新排列,它的影响范围有两种:

  • 全局范围:从根节点开始,对整个渲染树进行重新布局
  • 局部范围:对渲染树的某部分或者一个渲染对象进行重新布局

(2)重绘

当页面中某些元素的样式发生变化,但是不会影响其在文档流中的位置时,浏览器就会对元素进行重新绘制,这个过程就是重绘

下面这些操作会导致回流:

  • color、background 相关属性:background-color、background-image 等
  • outline 相关属性:outline-color、outline-width 、text-decoration
  • border-radius、visibility、box-shadow

注意: 当触发回流时,一定会触发重绘,但是重绘不一定会引发回流。

display、float、position的关系

(1)首先判断display属性是否为none,如果为none,则position和float属性的值不影响元素最后的表现。

(2)然后判断position的值是否为absolute或者fixed,如果是,则float属性失效,并且display的值应该被设置为table或者block,具体转换需要看初始转换值。

(3)如果position的值不为absolute或者fixed,则判断float属性的值是否为none,如果不是,则display的值则按上面的规则转换。注意,如果position的值为relative并且float属性的值存在,则relative相对于浮动后的最终位置定位。

(4)如果float的值为none,则判断元素是否为根元素,如果是根元素则display属性按照上面的规则转换,如果不是,则保持指定的display属性值不变。

总的来说,可以把它看作是一个类似优先级的机制,"position:absolute"和"position:fixed"优先级最高,有它存在的时候,浮动不起作用,'display’的值也需要调整;其次,元素的’float’特性的值不是"none"的时候或者它是根元素的时候,调整’display’的值;最后,非根元素,并且非浮动元素,并且非绝对定位的元素,'display’特性值同设置值。

懒加载的实现原理

图片的加载是由src引起的,当对src赋值时,浏览器就会请求图片资源。根据这个原理,我们使用HTML5 的data-xxx属性来储存图片的路径,在需要加载图片的时候,将data-xxx中图片的路径赋值给src,这样就实现了图片的按需加载,即懒加载。

注意:data-xxx 中的xxx可以自定义,这里我们使用data-src来定义。

懒加载的实现重点在于确定用户需要加载哪张图片,在浏览器中,可视区域内的资源就是用户需要的资源。所以当图片出现在可视区域时,获取图片的真实地址并赋值给图片即可。

使用原生JavaScript实现懒加载:

知识点:

(1)window.innerHeight 是浏览器可视区的高度

(2)document.body.scrollTop || document.documentElement.scrollTop 是浏览器滚动的过的距离

(3)imgs.offsetTop 是元素顶部距离文档顶部的高度(包括滚动条的距离)

(4)图片加载条件:img.offsetTop < window.innerHeight + document.body.scrollTop;

图示: 代码实现:

<div class="container"><img src="loading.gif"  data-src="pic.png"><img src="loading.gif"  data-src="pic.png"><img src="loading.gif"  data-src="pic.png"><img src="loading.gif"  data-src="pic.png"><img src="loading.gif"  data-src="pic.png"><img src="loading.gif"  data-src="pic.png">
</div>
<script>
var imgs = document.querySelectorAll('img');
function lozyLoad(){var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;var winHeight= window.innerHeight;for(var i=0;i < imgs.length;i++){if(imgs[i].offsetTop < scrollTop + winHeight ){imgs[i].src = imgs[i].getAttribute('data-src');}}}window.onscroll = lozyLoad();
</script>

settimeout 模拟实现 setinterval(带清除定时器的版本)

题目描述:setinterval 用来实现循环定时调用 可能会存在一定的问题 能用 settimeout 解决吗

实现代码如下:

function mySettimeout(fn, t) {let timer = null;function interval() {fn();timer = setTimeout(interval, t);}interval();return {cancel:()=>{clearTimeout(timer)}}
}
// let a=mySettimeout(()=>{//   console.log(111);
// },1000)
// let b=mySettimeout(() => {//   console.log(222)
// }, 1000)

扩展:我们能反过来使用 setinterval 模拟实现 settimeout 吗?

const mySetTimeout = (fn, time) => {const timer = setInterval(() => {clearInterval(timer);fn();}, time);
};
// mySetTimeout(()=>{//   console.log(1);
// },1000)

扩展思考:为什么要用 settimeout 模拟实现 setinterval?setinterval 的缺陷是什么?

答案请自行百度哈 这个其实面试官问的也挺多的 小编这里就不展开了

说一下HTTP 3.0

HTTP/3基于UDP协议实现了类似于TCP的多路复用数据流、传输可靠性等功能,这套功能被称为QUIC协议。

  1. 流量控制、传输可靠性功能:QUIC在UDP的基础上增加了一层来保证数据传输可靠性,它提供了数据包重传、拥塞控制、以及其他一些TCP中的特性。

  2. 集成TLS加密功能:目前QUIC使用TLS1.3,减少了握手所花费的RTT数。

  3. 多路复用:同一物理连接上可以有多个独立的逻辑数据流,实现了数据流的单独传输,解决了TCP的队头阻塞问题。

  4. 快速握手:由于基于UDP,可以实现使用0 ~ 1个RTT来建立连接。

为什么需要浏览器缓存?

对于浏览器的缓存,主要针对的是前端的静态资源,最好的效果就是,在发起请求之后,拉取相应的静态资源,并保存在本地。如果服务器的静态资源没有更新,那么在下次请求的时候,就直接从本地读取即可,如果服务器的静态资源已经更新,那么我们再次请求的时候,就到服务器拉取新的资源,并保存在本地。这样就大大的减少了请求的次数,提高了网站的性能。这就要用到浏览器的缓存策略了。

所谓的浏览器缓存指的是浏览器将用户请求过的静态资源,存储到电脑本地磁盘中,当浏览器再次访问时,就可以直接从本地加载,不需要再去服务端请求了。

使用浏览器缓存,有以下优点:

  • 减少了服务器的负担,提高了网站的性能
  • 加快了客户端网页的加载速度
  • 减少了多余网络数据传输

代码输出结果

var myObject = {foo: "bar",func: function() {var self = this;console.log(this.foo);  console.log(self.foo);  (function() {console.log(this.foo);  console.log(self.foo);  }());}
};
myObject.func();

输出结果:bar bar undefined bar

解析:

  1. 首先func是由myObject调用的,this指向myObject。又因为var self = this;所以self指向myObject。
  2. 这个立即执行匿名函数表达式是由window调用的,this指向window 。立即执行匿名函数的作用域处于myObject.func的作用域中,在这个作用域找不到self变量,沿着作用域链向上查找self变量,找到了指向 myObject对象的self。

title与h1的区别、b与strong的区别、i与em的区别?

  • strong标签有语义,是起到加重语气的效果,而b标签是没有的,b标签只是一个简单加粗标签。b标签之间的字符都设为粗体,strong标签加强字符的语气都是通过粗体来实现的,而搜索引擎更侧重strong标签。
  • title属性没有明确意义只表示是个标题,H1则表示层次明确的标题,对页面信息的抓取有很大的影响
  • i内容展示为斜体,em表示强调的文本

vuex

vuex是一个专为vue.js应用程序开发的状态管理器,它采用集中式存储管理应用的所有组件的状态,并且以相
应的规则保证状态以一种可以预测的方式发生变化。state: vuex使用单一状态树,用一个对象就包含来全部的应用层级状态mutation: 更改vuex中state的状态的唯一方法就是提交mutationaction: action提交的是mutation,而不是直接变更状态,action可以包含任意异步操作getter: 相当于vue中的computed计算属性

JS闭包,你了解多少?

应该有面试官问过你:

  1. 什么是闭包?
  2. 闭包有哪些实际运用场景?
  3. 闭包是如何产生的?
  4. 闭包产生的变量如何被回收?

这些问题其实都可以被看作是同一个问题,那就是面试官在问你:你对JS闭包了解多少?

来总结一下我听到过的答案,尽量完全复原候选人面试的时候说的原话。

答案1: 就是一个function里面return了一个子函数,子函数访问了外面那个函数的变量。

答案2: for循环里面可以用闭包来解决问题。

for(var i = 0; i < 10; i++){setTimeout(()=>console.log(i),0)
}
// 控制台输出10遍10.
for(var i = 0; i < 10; i++){(function(a){setTimeout(()=>console.log(a),0)})(i)
}// 控制台输出0-9

答案3: 当前作用域产产生了对父作用域的引用。

答案4: 不知道。是跟浏览器的垃圾回收机制有关吗?

开杠了。请问,小伙伴的答案和以上的内容有多少相似程度?

其实,拿着这些问题好好想想,你就会发现这些问题都只是为了最终那一个问题。

闭包的底层实现原理

1. JS执行上下文

我们都知道,我们手写的js代码是要经过浏览器V8进行预编译后才能真正的被执行。例如变量提升、函数提升。举个栗子。

// 栗子:
var d = 'abc';
function a(){console.log("函数a");
};
console.log(a);   // ƒ a(){ console.log("函数a"); }
a();              // '函数a'
var a = "变量a";
console.log(a);   // '变量a'
a();              // a is not a function
var c = 123;// 输出结果及顺序:
// ƒ a(){ console.log("函数a"); }
// '函数a'
// '变量a'
// a is not a function// 栗子预编后相当于:
function a(){console.log("函数a");
};
var d;
console.log(a);  // ƒ a(){ console.log("函数a"); }
a();              // '函数a'a = "变量a";     // 此时变量a赋值,函数声明被覆盖console.log(a); // "变量a"
a();         // a is not a function

那么问题来了。 请问是谁来执行预编译操作的?那这个谁又是在哪里进行预编译的?

是的,你的疑惑没有错。js代码运行需要一个运行环境,那这个环境就是执行上下文。 是的,js运行前的预编译也是在这个环境中进行。

js执行上下文分三种:

  • 全局执行上下文: 代码开始执行时首先进入的环境。
  • 函数执行上下文:函数调用时,会开始执行函数中的代码。
  • eval执行上下文:不建议使用,可忽略。

那么,执行上下文的周期,分为两个阶段:

  • 创建阶段

    • 创建词法环境
    • 生成变量对象(VO),建立作用域链作用域链作用域链(重要的事说三遍)
    • 确认this指向,并绑定this
  • 执行阶段。这个阶段进行变量赋值,函数引用及执行代码。

你现在猜猜看,预编译是发生在什么时候?

噢,我忘记说了,其实与编译还有另一个称呼:执行期上下文

预编译发生在函数执行之前。预编译四部曲为:

  1. 创建AO对象
  2. 找形参和变量声明,将变量和形参作为AO属性名,值为undefined
  3. 将实参和形参相统一
  4. 在函数体里找到函数声明,值赋予函数体。最后程序输出变量值的时候,就是从AO对象中拿。

所以,预编译真正的结果是:

var AO = {a = function a(){console.log("函数a");};d = 'abc'
}

我们重新来。

1. 什么叫变量对象?

变量对象是 js 代码在进入执行上下文时,js 引擎在内存中建立的一个对象,用来存放当前执行环境中的变量。

2. 变量对象(VO)的创建过程

变量对象的创建,是在执行上下文创建阶段,依次经过以下三个过程:

  • 创建 arguments 对象。

    对于函数执行环境,首先查询是否有传入的实参,如果有,则会将参数名是实参值组成的键值对放入arguments 对象中。否则,将参数名和 undefined组成的键值对放入 arguments 对象中。

//举个栗子
function bar(a, b, c) {console.log(arguments);  // [1, 2]console.log(arguments[2]); // undefined
}
bar(1,2)
  • 当遇到同名的函数时,后面的会覆盖前面的。
console.log(a); // function a() {console.log('Is a ?') }
function a() {console.log('Is a');
}
function a() {console.log('Is a ?')
}/**ps: 在执行第一行代码之前,函数声明已经创建完成.后面的对之前的声明进行了覆盖。**/
  • 检查当前环境中的变量声明并赋值为undefined。当遇到同名的函数声明,为了避免函数被赋值为 undefined ,会忽略此声明
console.log(a); // function a() {console.log('Is a ?') }
console.log(b); // undefined
function a() {console.log('Is a ');
}
function a() {console.log('Is a ?');
}
var b = 'Is b';
var a = 10086;/**这段代码执行一下,你会发现 a 打印结果仍旧是一个函数,而 b 则是 undefined。**/

根据以上三个步骤,对于变量提升也就知道是怎么回事了。

3. 变量对象变为活动对象

执行上下文的第二个阶段,称为执行阶段,在此时,会进行变量赋值,函数引用并执行其他代码,此时,变量对象变为活动对象。

我们还是举上面的例子:

console.log(a); // function a() {console.log('fjdsfs') }
console.log(b); // undefined
function a() {console.log('Is a');
}
function a() {console.log('Is a?');
}
var b = 'Is b';
console.log(b); // 'Is b'
var a = 10086;
console.log(a);  // 10086
var b = 'Is b?';
console.log(b); // 'Is b?'

在上面的代码中,代码真正开始执行是从第一行 console.log() 开始的,自这之前,执行上下文是这样的:

// 创建过程
EC= {VO: {}; // 创建变量对象scopeChain: {}; // 作用域链
}
VO = {argument: {...}; // 当前为全局上下文,所以这个属性值是空的a: <a reference> // 函数 a  的引用地址  b: undefiend  // 见上文创建变量对象的第三步}
词法作用域(Lexical scope

这里想说明,我们在函数执行上下文中有变量,在全局执行上下文中有变量。JavaScript的一个复杂之处在于它如何查找变量,如果在函数执行上下文中找不到变量,它将在调用上下文中寻找它,如果在它的调用上下文中没有找到,就一直往上一级,直到它在全局执行上下文中查找为止。(如果最后找不到,它就是 undefined)。

再来举个栗子:

 1: let top = 0; // 2: function createWarp() {3:   function add(a, b) {4:     let ret = a + b5:     return ret6:   }7:   return add8: }9: let sum = createWarp()
10: let result = sum(top, 8)
11: console.log('result:',result)

分析过程如下:

  • 在全局上下文中声明变量top 并赋值为0.
  • 2 - 8行。在全局执行上下文中声明了一个名为 createWarp 的变量,并为其分配了一个函数定义。其中第3-7行描述了其函数定义,并将函数定义存储到那个变量(createWarp)中。
  • 第9行。我们在全局执行上下文中声明了一个名为 sum 的新变量,暂时,值为 undefined
  • 第9行。遇到(),表明需要执行或调用一个函数。那么查找全局执行上下文的内存并查找名为 createWarp 的变量。 明显,已经在步骤2中创建完毕。接着,调用它。
  • 调用函数时,回到第2行。创建一个新的createWarp执行上下文。我们可以在 createWarp 的执行上下文中创建自有变量。js 引擎createWarp 的上下文添加到调用堆栈(call stack)。因为这个函数没有参数,直接跳到它的主体部分.
  • 3 - 6 行。我们有一个新的函数声明,createWarp执行上下文中创建一个变量 addadd 只存在于 createWarp 执行上下文中, 其函数定义存储在名为 add 的自有变量中。
  • 第7行,我们返回变量 add 的内容。js引擎查找一个名为 add 的变量并找到它. 第4行和第5行括号之间的内容构成该函数定义。
  • createWarp 调用完毕,createWarp 执行上下文将被销毁。add 变量也跟着被销毁。add 函数定义仍然存在,因为它返回并赋值给了 sum 变量。 (ps: 这才是闭包产生的变量存于内存当中的真相
  • 接下来就是简单的执行过程,不再赘述。。
  • ……
  • 代码执行完毕,全局执行上下文被销毁。sum 和 result 也跟着被销毁。

小结一下

现在,如果再让你回答什么是闭包,你能答出多少?

其实,大家说的都对。不管是函数返回一个函数,还是产生了外部作用域的引用,都是有道理的。

所以,什么是闭包?

  • 解释一下作用域链是如何产生的。
  • 解释一下js执行上下文的创建、执行过程。
  • 解释一下闭包所产生的变量放在哪了。
  • 最后请把以上3点结合起来说给面试官听。

代码输出结果

(function(){var x = y = 1;
})();
var z;console.log(y); // 1
console.log(z); // undefined
console.log(x); // Uncaught ReferenceError: x is not defined

这段代码的关键在于:var x = y = 1; 实际上这里是从右往左执行的,首先执行y = 1, 因为y没有使用var声明,所以它是一个全局变量,然后第二步是将y赋值给x,讲一个全局变量赋值给了一个局部变量,最终,x是一个局部变量,y是一个全局变量,所以打印x是报错。

instanceof

作用:判断对象的具体类型。可以区别 arrayobjectnullobject 等。

语法A instanceof B

如何判断的?: 如果B函数的显式原型对象在A对象的原型链上,返回true,否则返回false

注意:如果检测原始值,则始终返回 false

实现:

function myinstanceof(left, right) {// 基本数据类型都返回 false,注意 typeof 函数 返回"function"if((typeof left !== "object" && typeof left !== "function") || left === null) return false;let leftPro = left.__proto__;  // 取左边的(隐式)原型 __proto__// left.__proto__ 等价于 Object.getPrototypeOf(left)while(true) {// 判断是否到原型链顶端if(leftPro === null) return false;// 判断右边的显式原型 prototype 对象是否在左边的原型链上if(leftPro === right.prototype) return true;// 原型链查找leftPro = leftPro.__proto__;}
}

代码输出结果

function Person(name) {this.name = name
}
var p2 = new Person('king');
console.log(p2.__proto__) //Person.prototype
console.log(p2.__proto__.__proto__) //Object.prototype
console.log(p2.__proto__.__proto__.__proto__) // null
console.log(p2.__proto__.__proto__.__proto__.__proto__)//null后面没有了,报错
console.log(p2.__proto__.__proto__.__proto__.__proto__.__proto__)//null后面没有了,报错
console.log(p2.constructor)//Person
console.log(p2.prototype)//undefined p2是实例,没有prototype属性
console.log(Person.constructor)//Function 一个空函数
console.log(Person.prototype)//打印出Person.prototype这个对象里所有的方法和属性
console.log(Person.prototype.constructor)//Person
console.log(Person.prototype.__proto__)// Object.prototype
console.log(Person.__proto__) //Function.prototype
console.log(Function.prototype.__proto__)//Object.prototype
console.log(Function.__proto__)//Function.prototype
console.log(Object.__proto__)//Function.prototype
console.log(Object.prototype.__proto__)//null

这道义题目考察原型、原型链的基础,记住就可以了。

常见的图片格式及使用场景

(1)BMP,是无损的、既支持索引色也支持直接色的点阵图。这种图片格式几乎没有对数据进行压缩,所以BMP格式的图片通常是较大的文件。

(2)GIF是无损的、采用索引色的点阵图。采用LZW压缩算法进行编码。文件小,是GIF格式的优点,同时,GIF格式还具有支持动画以及透明的优点。但是GIF格式仅支持8bit的索引色,所以GIF格式适用于对色彩要求不高同时需要文件体积较小的场景。

(3)JPEG是有损的、采用直接色的点阵图。JPEG的图片的优点是采用了直接色,得益于更丰富的色彩,JPEG非常适合用来存储照片,与GIF相比,JPEG不适合用来存储企业Logo、线框类的图。因为有损压缩会导致图片模糊,而直接色的选用,又会导致图片文件较GIF更大。

(4)PNG-8是无损的、使用索引色的点阵图。PNG是一种比较新的图片格式,PNG-8是非常好的GIF格式替代者,在可能的情况下,应该尽可能的使用PNG-8而不是GIF,因为在相同的图片效果下,PNG-8具有更小的文件体积。除此之外,PNG-8还支持透明度的调节,而GIF并不支持。除非需要动画的支持,否则没有理由使用GIF而不是PNG-8。

(5)PNG-24是无损的、使用直接色的点阵图。PNG-24的优点在于它压缩了图片的数据,使得同样效果的图片,PNG-24格式的文件大小要比BMP小得多。当然,PNG24的图片还是要比JPEG、GIF、PNG-8大得多。

(6)SVG是无损的矢量图。SVG是矢量图意味着SVG图片由直线和曲线以及绘制它们的方法组成。当放大SVG图片时,看到的还是线和曲线,而不会出现像素点。这意味着SVG图片在放大时,不会失真,所以它非常适合用来绘制Logo、Icon等。

(7)WebP是谷歌开发的一种新图片格式,WebP是同时支持有损和无损压缩的、使用直接色的点阵图。从名字就可以看出来它是为Web而生的,什么叫为Web而生呢?就是说相同质量的图片,WebP具有更小的文件体积。现在网站上充满了大量的图片,如果能够降低每一个图片的文件大小,那么将大大减少浏览器和服务器之间的数据传输量,进而降低访问延迟,提升访问体验。目前只有Chrome浏览器和Opera浏览器支持WebP格式,兼容性不太好。

  • 在无损压缩的情况下,相同质量的WebP图片,文件大小要比PNG小26%;
  • 在有损压缩的情况下,具有相同图片精度的WebP图片,文件大小要比JPEG小25%~34%;
  • WebP图片格式支持图片透明度,一个无损压缩的WebP图片,如果要支持透明度只需要22%的格外文件大小。

代码输出结果

var a, b
(function () {console.log(a);console.log(b);var a = (b = 3);console.log(a);console.log(b);
})()
console.log(a);
console.log(b);

输出结果:

undefined
undefined
3
3
undefined
3

这个题目和上面题目考察的知识点类似,b赋值为3,b此时是一个全局变量,而将3赋值给a,a是一个局部变量,所以最后打印的时候,a仍旧是undefined。

JSONP

JSONP 核心原理:script 标签不受同源策略约束,所以可以用来进行跨域请求,优点是兼容性好,但是只能用于 GET 请求;

const jsonp = ({ url, params, callbackName }) => {const generateUrl = () => {let dataSrc = ''for (let key in params) {if (params.hasOwnProperty(key)) {dataSrc += `${key}=${params[key]}&`}}dataSrc += `callback=${callbackName}`return `${url}?${dataSrc}`}return new Promise((resolve, reject) => {const scriptEle = document.createElement('script')scriptEle.src = generateUrl()document.body.appendChild(scriptEle)window[callbackName] = data => {resolve(data)document.removeChild(scriptEle)}})
}

Canvas和SVG的区别

(1)SVG: SVG可缩放矢量图形(Scalable Vector Graphics)是基于可扩展标记语言XML描述的2D图形的语言,SVG基于XML就意味着SVG DOM中的每个元素都是可用的,可以为某个元素附加Javascript事件处理器。在 SVG 中,每个被绘制的图形均被视为对象。如果 SVG 对象的属性发生变化,那么浏览器能够自动重现图形。

其特点如下:

  • 不依赖分辨率
  • 支持事件处理器
  • 最适合带有大型渲染区域的应用程序(比如谷歌地图)
  • 复杂度高会减慢渲染速度(任何过度使用 DOM 的应用都不快)
  • 不适合游戏应用

(2)Canvas: Canvas是画布,通过Javascript来绘制2D图形,是逐像素进行渲染的。其位置发生改变,就会重新进行绘制。

其特点如下:

  • 依赖分辨率
  • 不支持事件处理器
  • 弱的文本渲染能力
  • 能够以 .png 或 .jpg 格式保存结果图像
  • 最适合图像密集型的游戏,其中的许多对象会被频繁重绘

注:矢量图,也称为面向对象的图像或绘图图像,在数学上定义为一系列由线连接的点。矢量文件中的图形元素称为对象。每个对象都是一个自成一体的实体,它具有颜色、形状、轮廓、大小和屏幕位置等属性。

数组扁平化

题目描述:实现一个方法使多维数组变成一维数组

最常见的递归版本如下:

function flatter(arr) {if (!arr.length) return;return arr.reduce((pre, cur) =>Array.isArray(cur) ? [...pre, ...flatter(cur)] : [...pre, cur],[]);
}
// console.log(flatter([1, 2, [1, [2, 3, [4, 5, [6]]]]]));

扩展思考:能用迭代的思路去实现吗?

实现代码如下:

function flatter(arr) {if (!arr.length) return;while (arr.some((item) => Array.isArray(item))) {arr = [].concat(...arr);}return arr;
}
// console.log(flatter([1, 2, [1, [2, 3, [4, 5, [6]]]]]));

前端社招高频面试题(一)(附答案)相关推荐

  1. 2021-02-252021 互联网大厂年度总结1000+道高频面试题(附答案解析)冲刺2021

    2021 互联网大厂年度总结1000+道高频面试题(附答案解析)冲刺2021 1.String类能不能被继承?为什么? 不能 因为string类是被final修饰的类,final修饰过的类不能被继承. ...

  2. 京东2021年5月社招软件测试面试题【有答案】

    京东2021年5月社招软件测试面试题[有答案] 1. http常见状态码有哪些? 2. HTTP协议特点有哪些? 3. 给你一个网站,你如何测试? 4. 一条软件缺陷记录都包含了哪些内容?如何提交高质 ...

  3. 2017河北单招计算机试题,2017河北单招语文模拟试题一(附答案).docx

    2017河北单招语文模拟试题一(附答案) 2017河北单招语文模拟试题一(附答案)1.扩写下面一段话,使之情景更丰富,至少使用一种修辞手法.(不少于60个字)家乡的小河,奔流在村头,我的乡情就溶在那清 ...

  4. 20道前端高频面试题(附答案)

    setTimeout 模拟 setInterval 描述:使用setTimeout模拟实现setInterval的功能. 实现: const mySetInterval(fn, time) {let ...

  5. js打印到控制台_前端JS,8个问题测试你真的懂吗(高频面试题,附答案)

    为了保证的可读性,本文采用意译而非直译. JavaScript 是一种有趣的语言,我们都喜欢它,因为它的性质.浏览器是JavaScript的主要运行的地方,两者在我们的服务中协同工作.JS有一些概念, ...

  6. 软件测试面试,一定要准备的7个高频面试题(附答案,建议收藏)

    收集了2022年最新的面试题后,负责就业的黑马讲师们整理出了7个高频出现的面试题,一起来看看. 高频问题1:请自我介绍下? 高频问题2:请介绍下最近做过的项目? 高频问题3:请介绍下你印象深刻的bug ...

  7. 2022前端必会的面试题(附答案)

    对React SSR的理解 服务端渲染是数据与模版组成的html,即 HTML = 数据 + 模版.将组件或页面通过服务器生成html字符串,再发送到浏览器,最后将静态标记"混合" ...

  8. 美团前端二面必会面试题(附答案)

    判断数组的方式有哪些 通过Object.prototype.toString.call()做判断 Object.prototype.toString.call(obj).slice(8,-1) === ...

  9. 社招阿里java岗到底有多难?分享阿里社招面试真题(附答案),以及P7需要掌握的知识点总结!

    前言 本来是抱着想试试淘系技术部的心态,没想到一路走完了流程,这里前辈水平的确很高,面试就感觉出来了,想把过程中的面经分享给大家: 一面 线程池了解吗,几种线程池创建方式 ThreadPoolExec ...

最新文章

  1. JS中for循环里面的闭包问题的原因及解决办法
  2. 边开飞机边换引擎?我们造了个新功能保障业务流量无损迁移
  3. indexes和indices的区别
  4. 计算机硬件加速怎么开,显卡硬件加速,小编教你电脑怎么开启显卡硬件加速
  5. rk3399_android7.1调试USB蓝牙模块小结
  6. VSS2005安装和配置过程中遇到的问题
  7. acid事务 mysql_MySQL 事务ACID特性
  8. sql92和sql99的区别
  9. mapxtreme for java_在MapXtreme for Java 4.8.0 中公布新制造的电子地图
  10. 神经网络加速器设计研究:寒武纪DianNao论文阅读
  11. 毕业设计-两轮自平衡小车主控板
  12. c语言手写平衡二叉树非递归实现
  13. 为什么量子计算机是锥形,科学家制作超高精度微腔为量子计算机铺垫
  14. 架构师之路读书会 | 15分钟一本,让我为你精读100本好书,快来加入吧!
  15. 勇者斗恶龙10 android,《勇者斗恶龙》系列35周年纪念直播情报汇总
  16. JAVA基础个人总结,不喜勿喷
  17. 一个传播 Worm.Win32.Otwycal.c / Worm.Win32.Infei.a 的网站v2
  18. Windows10环境下设置播放器默认软件打开方式
  19. 微信怎么传较大视频?微信怎么传特别大的视频?
  20. 51单片机串口通信模板_51单片机串口通信(字符串接收和发送)(示例代码)

热门文章

  1. 测试 软通动力软件测试机试_软件测试培训怎么选择就业方向,都有哪些方向可以选择?...
  2. 浅谈公司的IT管理流程
  3. Lua源码阅读笔记 - table的哈希碰撞
  4. 「2013-8-5」企业基因
  5. 分布式事务基础(分布式事务协议解决方案)
  6. MATLAB处理音频文件基本用法
  7. 使用Layout Inspector工具查看布局嵌套
  8. 关于如何解决hadoop jps时没有datanode
  9. 冷菜的31种调味汁的配制方法
  10. 阿里六年团队Leader实战秘诀|程序员最重要的八种软技能(找工作/就业生必读)