前言

为了巩固自己的知识和提升技术栈的广度以及深度,每天都会学习一道面试题(本文会持续更新)

如何获取数组中最大的数 (2020/11/27)

1.排序法

const arr = [1, 2, 3, 4, 5, 6];
arr.sort((a, b) => {
  return b - a;
});
const max = arr[0];
console.log(max); // 输出最大值 6

2.假设法

const arr = [2, 1, 3, 5, 4, 6];
for (let i = 1; i < arr.length; i++) {
  max = arr[0];
  const cur = arr[i];
  cur > max ? (max = cur) : max;
}
console.log(max); // 输出最大值 6

3.内置 Max()方法

/**
 * 内置函数Math.max()支持传递多个参数,但不支持直接传递一个数组,所以需要借助apply()来传递一个数组。
 *
 * apply()与call()方法类似,区别在于apply()接受的是参数数组,call()接受的是参数列表。
 */
const arr = [1, 2, 3, 4, 5, 6];
const max = Math.max.apply(null, arr);
console.log(max); // 输出最大值 6

4.ES6 扩展运算符

const arr = [1, 2, 3, 4, 5, 6];
console.log(Math.max(...arr)); // 输出最大值 6

什么是虚拟 DOM (2020/11/28)

DOM 树是对 HTML 的抽象,VDOM 就是对真实 DOM 树的抽象。VDOM 不会触及浏览器的部分,只是存在于 Javascript 空间的树形结构。

虚拟 DOM(VDOM)是真实 DOM 的内存表示,一种编程概念,一种模式。它会和真实的 DOM 同步,在更新 DOM 树时,虚拟 DOM 使用 diff 算法对比新旧 DOM 的变化,实现局部更新,节省了浏览器性能。

DNS 如何进行解析 (2020/11/29)

  • DNS(Domain Name System): "域名系统"的英文缩写,是互联网上作为域名和 IP 地址相互映射的一个分布式数据库,它用于 TCP/IP 网络,它所提供的服务是用来将主机名和域名转换为 IP 地址的工作。

  • 域名解析: 通过域名,最终得到该域名对应的 IP 地址的过程就是域名解析。

DNS 实例解析:

1.打开浏览器,输入一个域名(例如输入www.4399.com),发出一个 DNS 请求到本地 DNS 服务器(中国电信等网络连接服务商提供)。

2.本地 DNS 服务器查询 DNS 请求是否存在缓存记录,存在,返回结果;不存在,则本地 DNS 服务器向 DNS 根服务器(随机根服务器d.root.servers.net)发送查询请求。

3.DNS 根服务器(DNS 根服务器不记录存储具体的域名和 IP 的映射关系)返回域服务器地址

4.根据返回结果,本地 DNS 服务器向域服务器(返回对应域服务器地址c.gtld.servers.net)发送查询请求。

5.域服务器(域服务器不会直接返回域名与 IP 的映射关系)返回本域名的解析服务器地址

6.根据返回结果,本地 DNS 服务器向域名的解析服务器(例如: .4399.com域服务器)发送查询请求。

7.域名的解析服务器返回请求域名对应的 IP 地址(例如: 116.207.132.239)。

8.根据返回结果,本地 DNS 服务器向客户端返回具体 IP 地址,并将查询结果缓存在本地 DNS 服务器中,加速下次客户端的访问。

如何获取图片原始尺寸信息 (2020/11/30)

/**
 * naturalWidth && naturalHeight 不兼容IE6/7/8
 */
getImageOriginal = () => {
  let img = document.getElementById("bg-img");
  const width = img.naturalWidth;
  const height = img.naturalHeight;
};

React 类组件和函数组件的区别 (2020/12/01)

  • 类组件: 拥有更多的特性,拥有 state 状态和生命周期钩子。

  • 函数组件: 单纯接受 props 参数,是一个无状态组件。

随着 Hooks 的产生,函数组件也可以拥有自身的 state 状态。虽然 Hooks 解决了函数组件 state 的问题,使得代码更加简洁轻便,但是类组件还是拥有着函数组件没有的其他特性。

什么是 JSX (2020/12/02)

下面例子的标签语法是 JSX 语法。

/**
 * JSX 是一个 Javascript 的语法扩展,既不是字符串也不是 HTML,具有 Javascript 的全部功能。
 *
 * JSX 语法中,可以在大括号中放置任何有效的 Javascript 表达式。
 *
 * JSX 也是一个表达式,可以赋值给变量、也可以作为参数传入函数。
 */
const element = <h1>Hello, World!</h1>;

React 中 state 和 props 有什么区别 (2020/12/03)

相同点:

React 组件的数据分为两种,props 和 state,两个数据的改变,都可能会引发组件重新渲染。
(props 和 state 可以保存信息,信息可以控制组件的渲染输出)

区别:

1.props 是组件的对外接口;state 是组件的内部状态。

2.props 是传递给组件的(类似于函数的形参);state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量)。

3.props 是不可修改的,所有 React 组件都必须像纯函数一样保护它们的 props 不被修改;state 是多变的、可以修改的,每次 setState 都会异步更新。

什么时候应该选择 Class 实现一个组件,什么时候应该选择一个函数实现一个组件 (2020/12/04)

Class 类组件: 拥有自身 state 状态管理,拥有生命周期方法,继承 ReactComponent 实例化。

无状态的函数组件: 函数组件没有链接到 ReactComponent 实例的 this 属性和生命周期方法。

原则一: 如果你的组件需要访问 this 就用 Class 实现组件。

原则二: 如果你的组件需要生命周期方法,使用 Class 实现组件。

黄金原则: 如果你可以使用无状态组件,那就使用函数实现一个组件。

总结:

  • 如果可以使用无状态函数,就是尽管用函数。

  • 否则,就使用 Class 类。

setState 可以接受函数为参数吗? 有什么作用 (2020/12/05)

setState 函数的弊端: setState 不会立刻改变(更新)React 组件中的 state 值(产生的效果不直观)。

作用: 函数式的 setState 可以解决 setState 存在的弊端,让 setState 函数产生的结果更直观有效。函数接收两个参数,第一个是当前的 state 值,第二个是当前的 props 值,函数应该返回一个对象(代表要对 this.setState 的更改)。

// 函数式 setState
class Counter extends React.Component {
  state = { count: 0 };
  handleIncrement = () => {
    this.setState((state, props) => {
      return {
        count: state.count + 1,
      };
    });
  };

  render() {
    const { count } = this.state;
    return (
      <div>
        <button onClick={this.handleIncrement}>+++</button>
        <span>{count}</span>
      </div>
    );
  }
}

描述一下 React 组件的各个生命周期函数 (2020/12/06)

React 组件的生命周期分为三个部分: 装载期(Mounting)更新期(Updating)卸载期(Unmounting)

1.装载期(组件第一次在 DOM 树中渲染的过程)

装载期调用函数顺序:

/**
 * constructor(创造一个组件的实例,调用对应的构造函数)
 *
 * getInitialState(初始化组件的内部状态this.state) && getDefaultProps(初始化外部接口props)
 *
 * componentWillMount(在render函数之前调用,可以在服务端调用、也可以在浏览器端调用)
 *
 * render(返回一个JSX描述结构的纯函数)
 *
 * componentDidMount(在render函数之后调用,函数调用时,render函数返回的结构已经渲染、组件已经"装载"到DOM树中;只能在浏览器端调用)
 */

2.更新期(组件被重新渲染的过程)

更新期调用函数顺序:

/**
 * componentWillReceiveProps(nextProps): 只要是父组件的render函数被调用,render函数里子组件就会更新,
 * 不管props变与不变,都会触发该函数。通过this.setState方法触发的更新过程不会调用这个函数。
 *
 * shouldComponentUpdate(nextProps, nextState): 决定一个组件什么时候不需要渲染。返回一个布尔值,true,render重新渲染;false,render不需要渲染。
 *
 * componentWillUpdate(在render函数之前调用,只有在浏览器端调用)
 *
 * render(渲染纯函数,决定了渲染什么)
 *
 * componentDidUpdate(prevProps, prevState): 在render函数之后调用,可以在服务端调用,也可以在浏览器端调用。
 */

3.卸载期(组件从 DOM 中删除的过程)

卸载期:

/**
 * componentWillUnmount(React组件要从DOM树中删除前,调用卸载函数): componentWillUnmount 中的工作与 componentDidMount相关联。
 *
 * componenWillUnmount可以清理React组件生产的内存垃圾,避免造成内存泄漏。
 */

什么是 shouldComponentUpdate 函数? 有什么作用 (2020/12/07)

shouldComponentUpdate 函数是 React 组件生命周期中更新期的一个具有高性能、决定性的函数。在更新过程中,React 组件首先调用 shouldComponentUpdate 函数,如果函数返回 true,就继续更新过程(render 函数);返回 false,就立刻终止更新过程,不会触发后续更新渲染。

作用:

1.shouldComponentUpdate 函数返回一个布尔值,告知 React 组件在更新过程中是否要继续更新。

2.shouldComponentUpdate 函数使用恰当,可以大大提高 React 组件的性能。

当组件的 setState 函数被调用之后,会发生什么 (2020/12/08)

setState 函数调用后触发组件更新,页面重新绘制。根据生命周期中的更新期,setState 函数不会调用 componentWillReceiveProps 函数,但是其余的更新函数调用不变。

/**
 * shouldComponentUpdate(当shouldComponentUpdate函数被调用的时候,this.state没有得到更新)
 *
 * componentWillUpdate(当componentWillUpdate函数被调用的时候,this.state依然没有得到更新)
 *
 * render(直到render函数被调用的时候,this.state才得到更新)
 *
 * componentDidUpdate()
 */

什么是 Fiber? 是为了解决什么问题 (2020/12/09)

Fiber 是更新过程碎片化的数据结构(每执行完一段更新过程,就把控制权交还React负责任务协调的模块,比对是否有新的紧急的任务需要更新,有,则优先更新紧急任务;无,则回到节点继续更新剩余任务)。

解决问题:

在 16.0 版本之前的 React 中,更新过程是同步的一层组件套一层组件,逐渐深入的过程,在更新完所有组件之前不会停止,调用时长长。而 Javascript 具有单线程的特点,每个同步任务时长不能太长,时长过长会对其他的输入的响应削弱甚至无响应,React Fiber 就是为了解决同步操作时长过长导致的性能问题。

什么是 HoC(Higher-Order Component)? 适用于什么场景 (2020/12/10)

HOC 高阶组件: 接受一个组件作为输入,返回一个新组件作为结果的函数。

import React from 'react';

function wrapperComponent(wrappedComponent) {
  return class WrappingComponent extends React.Component {
    render() {
      const {id, ...props} = this.props;
      return <WrappedComponent {...props}>
    }
  }
}

export default wrapperComponent;

使用场景:

1.重用代码: 有时一个功能逻辑会在多处 React 组件中使用,利用高阶组件的方式把这部分公用逻辑提取出来,就可以减少组件代码的重复使用率。

2.修改现有 React 组件的行为: 对于一些高度封装的 React 组件(第三方组件或团队内自己封装好的组件),对这一类 React 组件进行二次开发时,不想改动封装组件内部逻辑,利用高阶组件的方式就可以基于原有的组件函数产生新的组件,这样就不会对原有的组件产生任何改动,保证项目代码运行正常。

为什么我们利用循环产生的组件中要用上 key 这个特殊的 prop (2020/12/11)

在 React 组件中,循环渲染的组件需要加上 key 属性,没有 key 时,React 会发出警告(a key should be provided for list items),告知我们组件必须要有 key 属性。

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((item, index) => {
    return <li key={item.toString()}>{item}</li>;
  });

  return (
    <div>
      <ul>{listItems}</ul>
    </div>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById("root")
);

key 的作用: key 属性帮助 React 识别元素,确定元素的增删查改;在循环渲染的组件中,可以根据特定的 key 值,对相应的组件做特别的操作处理等。

let 与 const 有什么区别 (2020/12/12)

相似:

  • 都是只在声明所在的块级作用域内有效。
  • 都不能在所在作用域内有相同名称的变量和函数。

区别:

  • const 声明的变量(常量)必须初始化;let 声明的变量不用初始化。
  • const 声明的变量(常量)不可以赋值改变;let 声明的变量可以改变,值和类型都可以改变,没有限制。

什么是 React Hooks? (2020/12/13)

Hooks 是 React 16.8 中添加的一个全新的 API,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部的代码"钩"进来。React Hooks 就是那些钩子,你需要什么功能,就是用什么钩子。React 默认提供了一些常用的钩子,或者也可以使用封装属于自己的钩子。所有的钩子都是为函数引入外部功能,所以 React 约定,钩子一律使用use前缀命名。

React Hooks 使用规则是什么? (2020/12/14)

Hooks 就是 Javascript 函数,但是使用它们会有两个额外的规则:

  • 只能在函数最外层调用 Hooks。不要再循环、条件判断或者子函数中调用。

  • 只能在React 的函数组件中调用 Hooks。不要在其他 Javascript 函数中调用。

简述 Flux 思想 (2020/12/15)

一个 Flux 包含四个部分:

  • Dispatcher: 处理动作分发,维持 Store 之间的依赖关系。

  • Store: 负责存储数据和处理数据相关逻辑。

  • Action: 驱动 Dispatcher 的 Javascript 对象。

  • View: 视图部分,负责显示用户界面。

Flux 的好处,最重要的就是"单向数据流"的管理方式。在 Flux 的理念中,如果要改变界面,必须改变 store 中的状态;如果要改变 store 中的状态,必须派发一个 action 对象。

Flux 流程:

1.用户访问 View

2.View 发出用户指定的 Action。

3.Dispatcher 收到 Action,要求 Store 进行相应的更新。

4.Store 更新后,发出一个"change"事件。

5.View 收到"change"事件后,更新页面。

options 请求方法有什么用 (2020/12/16)

options 请求方法的主要用途有两个:

1.获取服务器支持的 HTTP 请求方法。

2.用来检查服务器的性能(形如: AJAX进行跨域请求时的预检,需要向另外一个域名的资源发送一个HTTP OPTIONS请求头,用以判断实际发送的请求是否安全)。

简述 https 原理,以及与 http 的区别 (2020/12/17)

HTTPS 原理:

1.客户端(Client)发起一个 HTTPS 的请求。

2.服务端(Server)把事先配置好的公钥证书返回给客户端。

3.客户端验证公钥证书有效性,验证通过则继续;不通过则显示警告信息。

4.客户端使用伪随机数生成器生成加密所使用的对称密钥,然后使用证书的公钥加密这个对称密钥,发送给服务端。

5.服务端使用自己的私钥解密这个消息,获得密钥。

6.服务端使用获得的对称密钥加密"明文内容 A",发送给客户端。

7.客户端使用对称密钥解密响应的密文,得到"明文内容 A"。

8.客户端再次发起 HTTPS 请求,使用对称密钥加密请求的"明文内容 B"。

9.服务端使用之前获得的对称密钥解密密文,得到"明文内容 B"。

区别:

1、https 协议需要到 ca 申请证书,一般免费证书较少,因而需要一定费用。

2、http 是超文本传输协议,信息是明文传输,https 则是具有安全性的 ssl 加密传输协议。

3、http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。

4、http 的连接很简单,是无状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全。

实现数组扁平化 (2020/12/18)

1.flat() 方法

const arr = [[1, 2], [3, 4], 5, 6];
console.log(arr.flat()); // 输出: [1,2,3,4,5,6]

2.reduce() 与 concat() 方法

const arr = [[1, 2], [3, 4], 5, 6];
console.log(arr.reduce((acc, val) => acc.concat(val), [])); // 输出: [1,2,3,4,5,6]

3.扩展运算符

const arr = [[1, 2], [3, 4], 5, 6];
console.log([].concat(...arr)); // 输出: [1,2,3,4,5,6]

4.正则式

const arr = [[1, 2], [3, 4], 5, 6];
const results = JSON.parse(
  "[" + JSON.stringify(arr).replace(/\[|\]/g, "") + "]"
);
console.log(results); // 输出: [1,2,3,4,5,6]

实现数组去重 (2020/12/19)

1.ES6 Set() 方法

const arr = [1, 3, 2, 4, 3, 1, 5, 2];
console.log([...new Set(arr)]); // 输出: [1,3,2,4,5]

2.两层 for 循环 + splice()

const arr = [1, 3, 2, 4, 3, 1, 5, 2];
function unique(arr) {
  let length = arr.length;
  for (let i = 0; i < length; i++) {
    for (let j = i + 1; j < length; j++) {
      if (arr[i] === arr[j]) {
        arr.splice(j, 1);
        length--;
        j--;
      }
    }
  }
  return arr;
}
const results = unique(arr);
console.log(results); // 输出: [1,3,2,4,5]

3.indexOf() 方法

const arr = [1, 3, 2, 4, 3, 1, 5, 2];
const unique = (arr) => {
  let results = [];
  for (let i = 0; i < arr.length; i++) {
    if (results.indexOf(arr[i]) === -1) {
      results.push(arr[i]);
    }
  }
  return results;
};
console.log(unique(arr)); // 输出: [1,3,2,4,5]

4.includes() 方法

const arr = [1, 3, 2, 4, 3, 1, 5, 2];
const unique = (arr) => {
  let results = [];
  for (let i = 0; i < arr.length; i++) {
    if (!results.includes(arr[i])) {
      results.push(arr[i]);
    }
  }
  return results;
};
console.log(unique(arr)); // 输出: [1,3,2,4,5]

你知道那些 http 头部 (2020/12/20)

HTTP 头域包括通用头、请求头、响应头和实体头。每个头域由域名、冒号、域值组成。

1.通用头部是客户端和服务端都可以使用的头部,可以在客户端、服务端和其他应用程序之间提供一些非常有用的通用功能,如 Date 头部。

2.请求头部是请求报文特有的,它们为服务器提供了一些额外信息,比如客户端希望接收什么类型的数据,如 Accept 头部。

3.响应头部便于客户端提供信息,比如,客服端在与哪种类型的服务器进行交互,如 Server 头部。

4.实体头部指的是用于应对实体主体部分的头部,比如,可以用实体头部来说明实体主体部分的数据类型,如 Content-Type 头部。

实现深浅拷贝 (2020/12/21)

1.浅拷贝: 如果数组元素是基本类型,拷贝一份,互不影响;而如果数组元素是对象或者数组,就会拷贝对象和数组的引用。这样原数组(对象)和新数组(对象)都会改变的复制引用拷贝方法称为浅拷贝。数组的concat()slice()方法就是一种浅拷贝。

const arr = [1, 2, { name: "tom" }];
const newArr = arr.concat();
arr[2].name = "jerry";
console.log(arr); // 输出 [1,2,{name:'jerry'}]
console.log(newArr); // 输出 [1,2,{name:'jerry'}]

实现浅拷贝:

function shallowCopy(obj) {
  if (typeof obj != "object") return; // 判断传入的参数,确保拷贝的只有对象
  let newObj = obj instanceof Array ? [] : {}; // 判断参数obj类型,新建数组或者对象
  // 遍历obj,判断是obj的属性才拷贝
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] = obj[key];
    }
  }
  return newObj;
}

2.深拷贝: 完全的拷贝一个对象和数组,原数组(对象)和新数组(对象)两者相互分离,两者之间的修改完全独立且不会相互影响,这样的复制引用拷贝方法称为深拷贝。JSON.stringify()方法就是一种深拷贝。

const arr = [1, 2, { name: "tom" }];
const newArr = JSON.parse(JSON.stringify(arr));
arr[2].name = "jerry";
console.log(arr); // 输出 [1,2,{name:'jerry'}]
console.log(newArr); // 输出 [1,2,{name:'tom'}]

实现深拷贝:

function deepCopy(obj) {
  if (typeof obj != "object") return;
  let newObj = obj instanceof Array ? [] : {};
  // 判断属性值的类型,如果是对象,递归调用深拷贝函数
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] =
        typeof obj[key] === "object" ? deepCopy(obj[key]) : obj[key];
    }
  }
  return newObj;
}

实现函数柯里化 (2020/12/22)

柯里化: 一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术(将一个接受多个参数的函数变为接受一个参数返回一个函数的固定形式)。

实现柯里化:

function curry() {
  const _args = [...arguments];
  function fn() {
    _args.push(...arguments);
    return fn;
  }
  fn.toString = function () {
    return _args.reduce((sum, cur) => sum + cur);
  };
  return fn;
}

实现判断一个类型的方法,可以区分 Null 与 Object (2020/12/23)

typeof 是一元操作符,可以对数据类型进行判断,返回值是表示操作数类型的一个字符串。但是 Javascript 的数据类型中,Null 和 Object 的 typeof 都返回 object字符串,难以区分两者数据类型;为了更好的去区分两者之间的关系,可以使用 Object.prototype.toString 来检验两者的区分。

var class2type = {};

// 生成class2type映射
"Boolean Number String Function Array Date RegExp Object Error Null Undefined"
  .split(" ")
  .map(function (item, index) {
    class2type["[object " + item + "]"] = item.toLowerCase();
  });

function type(obj) {
  // 兼容IE6
  if (obj === null) {
    return obj + "";
  }
  // 基本类型使用typeof,引用类型使用Object.prototype.toString
  return typeof obj === "object" || obj === "function"
    ? class2type[Object.prototype.toString.call(obj)] || "object"
    : typeof obj;
}

实现防抖节流 (2020/12/24)

防抖: 触发高频时间后 N 秒内函数只会执行一次,如果 N 秒内高频时间再次触发,则重新计算时间。防抖常用于用户进行搜索输入节约请求资源......

const debounce = (fn, time) => {
  let timeout = null;
  return function () {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      fn.apply(this, arguments);
    }, time);
  };
};

节流: 高频时间触发,但 N 秒内只会执行一次,所以节流会稀释函数的执行频率。节流常用于鼠标不断点击触发、监听滚动事件......

const throttle = (fn, time) => {
  let flag = true;
  return function () {
    if (!flag) {
      return;
    }
    flag = false;
    setTimeout(() => {
      fn.apply(this, time);
      flag = true;
    }, time);
  };
};

检查重复字符串 (2020/12/25)

1.方法一: 正则式判断

/**
 * 1.利用()进行分组。
 *
 * 2.利用/加数字表示引用,例如: /1就是引用第一个分组。
 *
 * 思路: 将[a-zA-Z]做分组,引用分组进行判断是否有连续重复。
 */
const str = "hello";
const str1 = "tom";
function isRepeatStr(str) {
  return /([a-zA-Z])\1/.test(str);
}
const res = isRepeatStr(str);
const res1 = isRepeatStr(str1);
console.log(res); // 输出 true
console.log(res1); // 输出 false

2.方法二: 字符串

/**
 * charAt(): 从一个字符串中返回指定的字符。
 */
const str = "hello";
const str1 = "tom";
function isRepeatStr(str) {
  let reg = /[a-zA-Z]/;
  for (let i = 0; i < str.length; i++) {
    if (str.charAt(i) == str.charAt(i + 1) && reg.test(str[i])) {
      return true;
    }
  }
  return false;
}
const res = isRepeatStr(str);
const res1 = isRepeatStr(str1);
console.log(res); // 输出 true
console.log(res1); // 输出 false

call()、apply()、bind()的区别 (2020/12/26)

1.call() 和 apply() 改变了函数的 this 上下文后便执行该函数,bind() 则是返回改变了上下文后的一个函数。

2.call() 和 apply() 的第一个参数都是要改变上下文的对象,call() 第二个参数以参数列表的形式展现;apply() 第二个参数则是以参数数组的形式展现。

/**
 * call()语法:
 * fun.call(thisArg, arg1, arg2,......)
 * thisArg: 函数运行时指向的this值。
 * arg1,arg2......: 绑定函数调用时传入的参数列表。
 */

/**
 * apply()语法:
 * apply(thisArg, [argsArray])
 * thisArg: 函数运行时指向的this值。
 * [argsArray]: 绑定函数调用时传入的参数数组。
 */

/**
 * bind()语法:
 * bind(thisArg[, arg1[, arg2[, ......]]])
 * thisArg: 函数运行时原函数指向的this值。
 * [, arg1[, arg2[, ......]]]: 绑定函数调用时传入的参数。
 */

区别总结: 函数需要改变 this 指向时使用 call()、apply()、bind()。

  • 传递参数不多,使用 call(thisArg, ...args)
  • 传递参数较多,使用 apply(thisArg, [...args])
  • 生成一个新函数长期绑定某个函数的某个对象使用,使用 bind(thisArg, ...args)

实现 add(1)(2)(3) (2020/12/27)

function add(...args) {
  return args.reduce((a, b) => a + b);
}

function currying(fn) {
  let args = [];
  return function temp(...newArgs) {
    if (newArgs.length) {
      args = [...args, ...newArgs];
      return temp;
    } else {
      let value = fn.apply(this, args);
      args = [];
      return value;
    }
  };
}

let addCurry = currying(add);
console.log(addCurry(1)(2)(3)()); // 输出 6

HTTP 状态码有哪些,分别代表什么意思? (2020/12/28)

HTTP 状态码:

1xx:指示信息类,表示请求已接受,继续处理。

2xx:指示成功类,表示请求已成功接受。

3xx:指示重定向,表示要完成请求必须进行更近一步的操作。

4xx:指示客户端错误,请求有语法错误或请求无法实现。

5xx:指示服务器错误,服务器未能实现合法的请求。

常见的状态码:

200 OK:客户端请求成功

206 Partial Content:客户发送了一个带有Range头的GET请求,服务器完成了它(当时音频或视频文件很大时返回206)

301 Moved Permanently:所请求的页面已经转移至新的URL

302 Found:所请求的页面已经临时转移至新的URL

304 Not Modified:客户端有缓冲的文档并发出看一个条件性的请求,服务器告诉客户,原来缓冲的文档还可以继续使用

403 Forbidden:对请求页面的访问被禁止

404 Not Found:请求资源不存在

500 Internal Server Error:服务器发生不可预期的错误原来缓冲的文档还可以继续使用

503 Server Unavailable:请求未完成,服务器临时过载或宕机,一段时间后可恢复正常

浏览器输入 url 后发生了什么? (2020/12/29)

/**
 * 大致流程:
 *
 * 1.url解析: 浏览器输入url,判断输入的url是否是一个合法的url;同时检查浏览器是否存在相对应的缓存,存在,返回缓存并渲染页面;不存在,则发送DNS请求。
 *
 * 2.DNS域名解析: 浏览器向DNS服务器请求解析输入url对应的IP地址,返回解析的IP地址给客户端。
 *
 * 3.建立TCP连接: 接收解析好的IP地址,根据IP地址和默认端口80,和服务器建立TCP连接。
 *
 * 4.发送HTTP请求: 浏览器发出读取文件的HTTP请求。
 *
 * 5.服务器处理请求: 服务器对浏览器HTTP请求做出响应,返回相对应的HTML文本给浏览器。
 *
 * 6.关闭TCP连接: 释放关闭TCP连接。
 *
 * 7.浏览器解析HTML,渲染页面: 浏览器接收服务器返回的HTML文本,解析HTML文本内容渲染页面。
 */

判断数组的几种方法 (2020/12/30)

数组判断方法:

1.Array.isArray()

const arr = [1, 2, 3];
const arr1 = "tom";
console.log(Array.isArray(arr)); // 输出 true
console.log(Array.isArray(arr1)); // 输出 false

2.obj instanceof Array

const arr = [1, 2, 3];
const arr1 = "tom";
console.log(arr instanceof Array); // 输出 true
console.log(arr1 instanceof Array); // 输出 false

3.obj.constructor === Array

const arr = [1, 2, 3];
const arr1 = "tom";
console.log(arr.constructor === Array); // 输出 true
console.log(arr1.constructor === Array); // 输出 false

4.Object.prototype.toString

const arr = [1, 2, 3];
const arr1 = "tom";
console.log(Object.prototype.toString.call(arr) === "[object Array]"); // 输出 true
console.log(Object.prototype.toString.call(arr1) === "[object Array]"); // 输出 false