menu
refresh
shufu
这是一个前端开发记录Debug过程和杂谈的博客
疯狂的前端面试题
access_time
brush 4886个字
whatshot 54 ℃

如何实现水平垂直居中

<div>
    <span></span>
</div>
  • 通过定位方式
div{
    position:relative;
}
span{
    position:absolute;/position:fiexd;
    top:0;
    left:0;
    right:0;
    bottom:0;
    margin:auto;
}
  • 通过flex方式
div{
    display:flex;
    justify-content:center;
    align-item:center;
}
  • 通过inline-block方式
div{
    height:500px;
    line-height:500px;
    text-align:center;
}
span{
    display:inline-block;
}

js for循环知识点

for (var i = 0, j = 0; i < 5, j < 9; i++, j++) {
     k = i+j;
}
//k=16
// 当判断语句含有多个语句时,以最后一个判断语句的值为准,因此上面的代码会执行 8 次。
for (var i = 0, j = 0; i < 9, j < 5; i++, j++) {
     k = i+j;
}
//k=8
// 当判断语句含有多个语句时,以最后一个判断语句的值为准,因此上面的代码会执行 4 次。

什么是快速排序

快速排序是从冒泡排序演变的一种排序方法,主要表现为取中间值来作为基准,然后根据这个基准进行交换位置排序。

怎么进行快速排序

套用一个经典例子。

let arr = [1,2,5345456,457,587,678,78,9879,456453,532,423,43,656,8,69,7,32,54,767,8,79,5,324,214,476,587,69,87,854,33,24,365,6578,789,23,534,6,568,7869,667,4354356754,76,878,93,223,5,58,67878,76,87,56,3423,5,568,679,7,95,634,5,3426,8,7,69,70,6759678,74,6,345,587,7698,789,6,4]
function quickSort(arr){
    if (!Array.isArray(arr) || arr.length <=1){
        return arr;
    }
    let leftArr = [],rightArr=[];
    let poiveIndex=Math.floor(arr.length / 2);
    let poiveValue = arr.splice(poiveIndex,1)[0];
    for (let z =0;z<arr.length;z++){
        if (arr[z]<poiveValue){
            leftArr.push(arr[z])
        }else{
            rightArr.push(arr[z])
        }
    }
    return quickSort(leftArr).concat([poiveValue], quickSort(rightArr))
};
quickSort(arr);

什么是扁平化

JavaScript的扁平化主要是表现在Array中,代表把多维数组转换成一维数组。

怎么扁平化数组

let arr = [1,23,[5345,34,5634,6],345645,[3453,5,346,456,346,[3453426,54,76345,6754,754,36,[325432456534645]]]];
let arrStr = JSON.stringify(arr);
//第1个方法:通过原生方法。(推荐)
arr.flat(Infinity);
//第2个方法:通过正则直接删除所有中括号(推荐)
arrStr.replace(/[\[\]]/g, '').split(',');
//第3个方法:通过循环机制来解套,没有第二个方法干脆,没有第一个方法简单,如果层级过多会浪费很多循环性能。不是很推荐。
let resultArr = [];
function FlatArr(arr){
    arr.map((item)=>{
        if (Array.isArray(item)){
            FlatArr(item)
        }else{
            resultArr.push(item)
        }
    })
}
FlatArr(arr);
//第4个方法:和第三个方法很想,但是会比较简单吧。
while(arr.some(Array.isArray)){
    arr = [].concat(...arr)
}
//....还有很多很多方法,但是基本上能说出前3种就够唬住面试官了。

Object、Map有什么不同、有什么相同

Map是Es6中新增加的数据结构,它本质上和Object一样都是以键(key),值(value)组成的集合,不同的在于Object只能使用字符串作为键(key),而Map能使用任意类型作为键(key)。Map还能直接通过size来获取对象长度,这是Object没能提供的方法。

//Object格式
let obj = {
    a:1
}
//Map格式
let map = new Map();
map.set(NaN, 1);
map.set(new String(1),1);
map.size;//2;

Array、Set有什么不同,有什么相同

Set是Es6中新增加的数据结构,它类似数组,但是成员必须是唯一的,没有重复值,Set数据结构不能使用数组的遍历方法来进行循环遍历,需要使用...扩展来转换成普通数组,因为Set的唯一特性,我们可以使用Set来进行去重操作

let set = new Set([123,4123,123,123,123,5,326]);
console.log(set);//Set(4) {123, 4123, 5, 326};
//转普通数组。
[...set];

TCP和UDP有什么不同

  • TCP的有状态连接,每次连接时会进行3次握手的前期确认,保证此次连接的稳定性,因此它是可靠的,如果要求数据准确度较高的场景,可以选用TCP连接,TCP只能一对一连接
  • UDP是无状态连接,UDP不保证传输的数据稳定性、完整性,因此UDP是不可靠的,但是也因为它的特性(头部开销小),所以它的数据传输很高效,可以一对多、多对多连接,主要应用在实时性比较强的场景(直播、聊天、游戏等场景)

Http缓存(是自己对HTTP的一些理解,不代表标准答案。)

一般的HTTP缓存说的是get缓存

  • 强缓存 (我喜欢说是时间缓存)

    因为强缓存有一个特性,就是依据cache-control/expires的缓存时间没过期的话,就会直接使用浏览器本地缓存数据,不会进行任务请求,通过控制台我们能看到状态码表现是200,在Size会标记成disk cache / memory cache标志 代表用的是本地、内存缓存,没有经过服务器请求。

    • cache-control

    一般使用 max-age设置缓存时间

    • Expires

    expires: Wed, 26 May 2021 03:28:53 GMT
    GMT时间标记,表现和cache-control的意思差不多,优先级比cache-control低,但是会和cache-control同时存在。

  • 协议缓存 (我喜欢说是hash标记缓存)

    协议缓存出现的场景主要是为了解决强缓存的一些缺点,强缓存是根据时间缓存,会出现很多场景下,过了强缓存的时间,但是服务器的数据依旧没有任何更改,但是因为强缓存的特性,依旧会重新发起一个请求获取数据,随之ETag协议缓存就出现了,浏览器会发起一个请求,与服务器对比资源是否进行修改,如果浏览器的ETag和服务器的不一致则会进行获取请求返回200状态码,如果服务器资源没有进行任何修改,则会返回304状态码告知浏览器使用缓存中的数据。
    Last-Modified是从资源中读取到最后修改时间,并且返回到请求中标记。Lst-Modified和ETag的表现大致上是一致的,只在于一个是用hash标记一个是用GMT时间标记。

    • ETag/If-None-Match

    hash模式

    • Last-Modified/If-Modified-since

    GMT模式

HTTP2的了解么,和HTTP1.1有什么不同

HTTP 2.0与HTTP 1.1区别 - FrankYou - 博客园

HTTP2.0是HTTP协议标准的第二个版本,HTTP2.0主要基于SPDY协议。
HTTP2和HTTP1的不同主要如下

  • HTTP2采用二进制格式而非文本格式。
  • HTTP2是完全多路复用的,而非有序并阻塞的——只需一个连接即可实现并行
  • HTTP2进行了报头压缩以降低请求开销。
  • HTTP2新增了主动推送功能。

Http状态码了解过那些?304状态吗代表什么

HTTP常用状态码:(说一下常用的即可)

200 请求成功
301 永久重定向
302 临时重定向
304 无更改,缓存重定向到本地缓存
400 请求url错误、参数错误等
401 无用户认证
403 服务器拒绝
404 资源不存在。
500 服务器错误
502 服务器未响应
503 服务器超载

304状态码代表数据在服务端无更改,一般浏览器接收到304状态码会重新命中内存缓存。

var、let、const有什么区别?

  • var

es6之前就已经存在的定义变量声明,会被变量提升,在全局中定义的变量可以在window下读取到,例如:

var test = 'a';
window.test  // 'a';
  • let

es6新增加的局部变量生命,不会被变量提升,有暂时性死区的特性,可以进行重新定义,即使在全局中声明也不会挂载到window上,例子:

let test = 'a'
window.test // undefined
test = 'b' 
console.log(test) //'b'
  • const

es6新增的常量声明,和let一样,不会被变量提升,有暂时性死区的特性,从名字上看出来是常量,代表声明后不可变(如果只是简单声明一个原始类型就不可更改,如果声明的是一个对象类型,可以进行内部属性、方法更改),在全局中声明也不会挂载到window上。例子:

const test = 'a';
window.test //undefined
test = 'c' // Uncaught TypeError: Assignment to constant variable.
const obj = {};
obj.a = 'a';
console.log(obj) // {a:'a'}

怎么用es5写一个类、怎么继承?

//定义
function People(name,age){
    this.name = name;
    this.age = age
}
People.prototype.eat = function(){
    console.log('Ta在吃饭')
}
//使用
let chinaPeople = new People('张三','20');

//继承 (其他的继承方式就不用说了,就说这个寄生式组合继承就行);
function BlackPeople(name, age ,color){
    People.call(this, name, age);
    this.color = color
}
BlackPeople.prototype = new People();
BlackPeople.prototype.constructor = BlackPeople;
BlackPeople.prototype.language = function(){
    console.log('Ta说印度语')
}
let indiaPeople = new BlackPeople('婆娜', '20', 'black');

数组有那些常用的方法

let arr = [];
arr.map();
arr.forEach()
arr.some();
arr.every();
arr.filter();
arr.keys();
arr.values();
arr.reduce();
arr.pop();
arr.push();
arr.shift();
arr.unshift();
arr.splice();
arr.join();
arr.concat();
arr.fill()
//......很多 、差不多有30来个吧。一般能说一下大概有多少个,然后说一下常用的几个即可

es6最常用有那些新特性

Map

Set

剪头函数()=>{}

Promise

async/await

class

let

const

import / export

...运算符

模版字符串``

解构赋值

这里说的都是常用的。 其他的proxy、Symbol、Generator、修饰器等看自己以往的学习

什么是Promise、Promise的实现原理

Promise代表承诺,是Es6中新增的异步操作函数,内部有3个状态,Pending(等待、进行中)、Resolve(已完成)、Reject(已失败),状态一经改变不可二次更改。最大的应用场景是为了解释回调地狱

利用了观察者模式的思想,只要通过特定书写方式来注册对应状态事件处理函数,然后更新状态来调用注册过的处理函数。

手写简易Promise;

答案:

const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
class MyPromise {
  constructor(fn) {
    this.status = PENDING;
    this.value = null;
    this.resolvedCallBacks = [];
    this.rejectedCallBacks = [];
    try {
      fn(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = RESOLVED;
      this.value = value;
      this.resolvedCallBacks.map((cb)=>cb(this.value))
    }
  };
  reject = (value) => {
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.value = value;
      this.rejectedCallBacks.map((cb) => cb(this.value));
    }
  };

  then = (onFulfilled,onRejected) => {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v=>v;
    onRejected = typeof onRejected === 'function' ? onRejected: r=>{throw r;};
    if (this.status === PENDING) {
      this.resolvedCallBacks.push(onFulfilled);
      this.rejectedCallBacks.push(onRejected);
    }
    if (this.status === RESOLVED) {
      onFulfilled(this.value);
    }
    if (this.status === REJECTED){
      onRejected(this.value);
    }
  }
}

/

const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';
function MyPromise(fn) {
  this.status = PENDING;
  this.value = null;
  this.resolvedCallBacks = [];
  this.rejectedCallBacks = [];
  try {
    fn(this.resolve,this.reject);
  } catch (e) {
    this.reject(e)
  }
}

MyPromise.prototype.resolve = (value) => {
  if (this.status === PENDING) {
    this.status = RESOLVED;
    this.value = value;
    this.resolvedCallBacks.map((cb) => cb(this.value));
  }
};
MyPromise.prototype.reject = (value) => {
  if (this.status === PENDING) {
    this.status = REJECTED;
    this.value = value;
    this.rejectedCallBacks.map((cb) => cb(this.value));
  }
};

MyPromise.prototype.then = (onFulfilled, onRejected) => {
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (v) => v;
  onRejected =
    typeof onRejected === 'function'
      ? onRejected
      : (r) => {
          throw r;
        };
  if (this.status === PENDING) {
    this.resolvedCallBacks.push(onFulfilled);
    this.rejectedCallBacks.push(onRejected);
  }
  if (this.status === RESOLVED) {
    onFulfilled(this.value);
  }
  if (this.status === REJECTED) {
    onRejected(this.value);
  }
};

async/await和Promise的区别

  • async/await是Generator+Promise的语法糖实现。
  • Async的写法类似同步代码,比Promise.then模式更加优雅

宏任务和微任务的执行优先级

// 执行优先级
setTimeout(function(){console.log(1)},1000);
new Promise((resolve)=>{
    console.log(2);
    resolve();
    console.log(3);
})
function fo(){
    console.log(7)
}
async function foo(){
    console.log(5);
    await fo();
    console.log(6);
}
foo();
console.log(4);
// 请写出打印流程

// 2、3、5、7、4、6、1

setInterval的运行原理

setInterval执行后,JavaScript的事件机制会按设定的时间定时触发生成一个回掉函数放置到执行栈中执行。

setInterval有什么缺点

因为JavaScript是属于单线程模式的语言,所以setInterval的任务可能会受线程中执行的代码阻塞影响。

变量提升

var a = 10;
var a ;
a = ?
// 10
//解释:内核解析后的代码是这样 
var a;
var a;
a = 10;

隐形类型转换

[] == ![] //?
null == undefined //? 
null === undefined //?
> [] == ![]判断逻辑
// true
// 解释: !号优先级最高。所以执行流程如下
[] == false
// 解析器判断到有Boolean类型时,会把Boolean类型转换成Number类型。因此下一步结果是
[] == 0
// 解析器解析到对象格式和数值格式进行判断时,会进行[].valueof()取值,因此下一步结果是
'' == 0;
// 到这一步的结果其实已经明朗了。 ''会转换成0  最终结果是
0 == 0;
// true;
--- 
> null == undefined
// true
//解释:执行流程如下
null == 0;
0 == 0;
---

>null === undefined的流程就不需要解释了吧。
// false

null类型

typeof null //?
//object
// 解释: 这是个历史遗留问题,因为JavaScript语言设计的时候是设计00开头为Object格式,但是因为null 代表000000 全零, 所以typeof就会在判断的时候就会错误认为null是object对象。

内存指向考察题

//1、
let arr = [{a:1},{a:2},{a:3}];
arr.map((item)=>{item.a++})
console.log(arr) //?

//2、
let arr = [1,2,3,4,5,6];
arr.map(item=>{item++});
console.log(arr) // ?

//解答1、
[{a:2},{a:3},{a:4}]
//解答2、
[1,2,3,4,5,6]
//解释:map本身就不能修改自身的,为什么1能修改呢。。 是因为1的序列是对象。因此修改的是内存指向的属性。2本身就是原始类型,因为map不修改自身的特性,因此++不会生效

Vuex的实现原理

  • state

作为状态管理容器对象,用来进行同意的状态管理

  • getters

作为state的计算属性。

  • mutation

vuex中唯一修改state的方法,不能直接调用,需要通过commit来调用,可以使用常量来作为keys。

  • action

提交的是mutation操作,而不是直接改变,可以在里面写异步代码。

Vuex的Mutations和Actions的应用场景

  • mutation

vuex中唯一修改state的方法,不能直接调用,需要通过commit来调用,可以使用常量来作为keys。

  • action

提交的是mutation操作,而不是直接改变,可以在里面写异步代码。

Vue的Computed和Watch有什么不同

  • Computed

支持缓存,只有当依赖发生变化时才会进行重新计算,

不支持异步,在Computed里面执行异步操作时会无效。

  • Watch

不支持缓存,数据发生变化时会触发相应的操作

支持异步

能监听props、state、computed的值。

Vue的数据挟持原理

Vue数据挟持原理 - 知乎
Vue数据绑定原理之数据劫持 - 掘金

Vue组件传值方式

  • 父子级传值

    > 通过props 传递给子级数据,通过$emit来返回给父级监听变化。
  • 兄弟级传值

    > - this.$parent.$children
    > - eventBus
    > - VueX
  • 跨组件传值

    > - eventBus
    > - VueX
    > - 如果是孙字辈的层级关系 可以使用proivde/inject来定义传递。
    

keep-alive的作用

keep-alive是vue-router中的一个api。他主要用来缓存组件内容,避免前进后退时反复刷新操作,
keep-alive原理
附带上掘金高赞答案。有遇到问过的。

Vue-router有几种模式、有什么不同。

  • hash模式

    >通过内部监听hash的变化来切换不同的模块显示,一般不会重新刷新页面,只会进行异步请求,兼容性较好,唯一的缺点就在url上会有一个很丑的#,在对外业务网站上可能需要使用nginx来重定向掩盖这个#号的显示。
  • history模式

    >使用Html5的api history功能实现,比较符合正常url显示使用,需要在服务器上配置支持,要不然刷新会进入404页面。
    

$nextTick的实现原理

$nextTick是Vue实现的一个异步方法,内部通过原生Promise.then、MutationObserver、setImmediate来进行异步操作,如果执行环境不支持以上几个方法时,会降级使用setTimeout来代替。

Object.defineProperty有什么缺点。

Object.defineProperty对数组、对象的监听有缺陷,对数组的一些方法和对象的新增属性无法监听,Vue重写了数组的方法和内置了Set方法才解决了这个问题。

浏览器渲染原理(渲染过程)

这个问题我只能说个大概,如果你要详细的完整的 只能去百度或者谷歌完整。

浏览器获取到html文档解析成DOM树
解析dom时请求资源
处理css文件,构建成CSSOM树
将DOM树和CSSOM树合并成渲染树
GPU根据渲染树构建布局层
最后再显示出来。

call、bind、apply的作用和区别。

这三个方法都能改变当前this指向,第一个参数为this指向的对象,如果为null或者undefined则会重新指定为window。

  • call:能接收零散数据,例:fn.call(this,'a','b'),call的改变只是临时改变this指向,立即执行。
  • apply:接收数组格式数据传入,例:fn.call(this,['a','b']),apply和call一样都只是临时改变this指向
  • bind:接收数据和call基本一致,例:fn.bind(this,'a','b'),和call不同在于,它只是改变this指向,不会立即执行,并且是永久改变。

js的强制类型转换有那些。

这个题得看面试官要问什么,有时候他们是想问隐形强制转换,有些时候他们又只是想问能一些转换方法的了解,所以这个题很模糊。。

如果是隐形强制转换的话 就是 四则、==、判断等场景

如果是转换方法:

  • 转Number (Number、parseFloat、parseInt)
  • 转String (String、toString)
  • 转Boolean (Boolean)

怎么判断null 说出几种方法

let _null = null;
if (_null === null){};
if (JSON.stringify(_null) === 'null'){};
if (!_null && _null !== undefined && _null !== 0){}

怎么实现一个浅拷贝

let arr = [123,123,5];
let arr2 = arr;
//使用函数扩展符:
let obj3 = {...obj}
//使用JSON.parse+JSON.stringify
//(很多教程上都把这个方法说是深拷贝,其实根本就只能算是一个伪深拷贝而已)
let obj = {
    a:1,
    b:[2,3,4]
}
let obj1 = JSON.parse(JSON.stringify(obj))
console.log(obj1)// {a: 1, b: Array(3)}
// 为什么会说JSON.parse+JSON.stringify是伪深拷贝呢,, 通过下面的例子能表现出来。
let _obj = {
    a:1,
    b:[2,3,4],
    c:function(){console.log(obj.a)},//就增加了个c函数
}
let obj2 = JSON.parse(JSON.stringify(_obj));
console.log(obj2)//{a: 1, b: Array(3)}
//和obj1的结果基本一致,c完全就没办法拷贝出来,所以通过这种方法进行的拷贝不是完整意义上的深拷贝。只能说是伪深拷贝或者浅拷贝。

怎么实现一个深拷贝

// 完全完整并且考虑一切场景的深拷贝建议使用lodash库(cloneDeep)来使用,
// 原生深拷贝的应该只有Object.assign。
let obj4 = Object.assign({},obj);

实现一个长度为10的数组,并且全部补全为0

Array(10).fill(0)

require和import的使用有什么不同

require是commonJs的标准模块引入方法,
require引入的数据是全部引入,
require模块输出的是拷贝。
require是在运行时加载。

import是Es6标准化的模块引入方法,
import能局部引入。
import输出的是模块的引用。
import是编译时输出。

本地存储 localStorage、sessionStorage、cookie有什么区别。

首先localStorage、sessionStorage的存储大小是一致的,以key-value形式存储,一般在5m大小左右,这个根据不同浏览器的支持性不同有上下浮动差异,不会携带到请求上。

  • localStorage

    > 永久存储,不手动清除时会永久保存下去。
  • sessionStorage

    > 临时存储,关闭页面时会自动清除
  • cookie

    > 以字符串方式存储,存储大小有限,http请求每次都会携带cookie到服务器中,这样无形会增加请求的压力,服务端能控制,需要手动设置存储时间,使用较为麻烦,需要自己手动封装函数才能get和set。
    

什么是跨域(这里一般能顺带解释浏览器的同源策略),怎么会产生跨域、怎么解决跨域。

跨域的产生主要是浏览器的同源策略限制,它是浏览器的一个约定,也是浏览器最基础的安全功能,不同域名(主域名、子域名)、端口、协议(http、https)都会产生跨域限制,

几种解决跨域的方法

  • document.domain

在主域名相同、子域名不同时,可以使用这个方法来进行通信。

    //a.test.com
    document.domain = 'test.com';
    //b.test.com
    document.domain = 'test.com';
  • JSONP

    > 最核心的思想,通过创建一个script标签来向浏览器进行get请求(只能get请求),因为script的src请求是不受浏览器的同源策略影响。
    
<script src="http://test.com/data.php?callback=dosomething"></script>
<script type="text/javascript">
    function dosomething(res){
        // 处理获得的数据
        console.log(res.data)
    }
</script>
  • CORS

    > 这个主要是让服务端打开CORS的支持,通过设置Access-Control-Allow-Origin来进行。
    
  • Node代理

    > 如果是spa项目则只能在开发环境下使用,可以使用node的代理来暂时解决这个跨域问题,但是一般只能在开发环境下使用,发布生产环境下需要服务端CORS或者nginx转发处理
    >
    > 如果是ssr项目则可以发布到生产上使用。
    

怎么判断对象类型

//1、使用instanceof来判断。
Array instanceof Object //true 
//不过instanceof来判断的方法也不准  就和typeof的情况一样。 所以这里就需要使用toString来获取到内部的[[class]]字段来判断了
//2、使用toString来判断内部[[class]];(推荐)
Object.prototype.toString.call([1,2,3,4]) //[object Array]
//通过这个方法来获取内部的[[class]] 然后在if判断使用===即可。
//3、使用原型链的constructor来判断 (注意 原型链会存在被改变的可能。)
[].constructor === Array

怎么做性能优化

根据自己实际情况回答,大致能说出一些差不多了

请求

  • 1、压缩合并第三方组件库减少请求
  • 2、根据项目选择使用CDN(如果是功能站,不建议使用第三方CDN)
  • 3、尽量开启整站CDN化
  • 4、更新频率少的请求可以使用本地缓存。
  • 5、link标签CDN预解析化
  • 6、懒请求(commonJs的require、es9/10的import())

图片优化

  • 1、图片精灵的使用
  • 2、图片格式、尺寸的正确选择
  • 3、压缩图片
  • 4、可以使用base64减少请求。
  • 5、多使用IconFont
  • 6、能使用css实现的样式尽量使用css
  • 7、图片懒加载

html

  • 1、html结构精简语义化(主要为了加快浏览器渲染DOM的过程)
  • 2、压缩html代码
  • 3、style标签放置到head里面,script标签放置到body标签结束之前

css

  • 1、css层级嵌套优化(使用less、sass时尽量注意不要使用太多层级嵌套)
  • 2、css代码压缩
  • 3、公用css提取

其他性能优化

  • 1、首屏代码使用率优化。
  • 2、多使用节流和防抖
  • 。。。。。。

介绍一下js的节流、防抖,怎么实现一个简单的节流、防抖

首先,这两个的逻辑都是通过延迟执行的思想进行实现的,不同在于应用场景,

  • 节流:让连续触发的事件在某个时间段内只执行一次。
function throttle(fn, delay){
    let valid = true;
    return function(){
        if (!valid){
            return false;
        }
        valid = false;
        setTimeout(()=>{
            fn();
            valid = true;
        },delay)
    }
}
  • 防抖:在第一次触发事件时,不立即执行函数,而是在期限时间后执行,如果200ms内不断触发,则会清除之前的计时器,重新指定计时器。
function debounce(fn, delay){
    let times = null;
    return function(){
        if (times){clearTimeout(times)};
        times = setTimeout(fn, delay)
    }
}

0.1+0.2 !=0.3//为什么? 怎么解决这个问题。

在JavaScript中的二进制浮点数不是十分精确,这是计算机的标准问题。(没必要回答多精准吧,大概只要能说出为啥的就行)

//1、简单解决,加大倍数然后再除回去即可
(1000 + 2000) / 10000
//2、使用第三方精度库bignumber.js库

什么是纯函数。

纯函数

在程序设计中,若一个函数符合以下要求,则它可能被认为是纯函数:


  • 此函数在相同的输入值时,需产生相同的输出。函数的输出和输入值以外的其他隐藏信息或状态无关,也和由I/O设备产生的外部输出无关。
  • 该函数不能有语义上可观察的函数副作用,诸如“触发事件”,使输出设备输出,或更改输出值以外物件的内容等。
  • 纯函数的输出可以不用和所有的输入值有关,甚至可以和所有的输入值都无关。但纯函数的输出不能和输入值以外的任何资讯有关。纯函数可以传回多个输出值,但上述的原则需针对所有输出值都要成立。若引数是传引用调用,若有对参数物件的更改,就会影响函数以外物件的内容,因此就不是纯函数。

纯函数有什么副作用

副作用

在计算机科学中,函数副作用指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响。例如修改全局变量(函数外的变量),修改参数或改变外部存储。

在某些情况下函数副作用会给程序设计带来不必要的麻烦,给程序带来十分难以查找的错误,并降低程序的可读性。严格的函数式语言要求函数必须无副作用。

什么是函数柯里化(一般有问纯函数 就必问柯里化)

柯里化
大佬,JavaScript 柯里化,了解一下? - 掘金

#如无特别声明,该文章均为 shufu 原创,转载请遵循 署名-非商业性使用 4.0 国际(CC BY-NC 4.0) 协议,即转载请注明文章来源。
#最后编辑时间为: 2020 年 08 月 23 日


account_circle
email
explore


DreamCat

主题名称:DreamCat | 版本:X1.6-20201226

主题开发:HanFengA7 | TeddyNight | Dev-Leo | CornWorld

鸣谢:学神之女

鸣谢:学神之女