关于前端:京东前端高频面试题合集

62次阅读

共计 20042 个字符,预计需要花费 51 分钟才能阅读完成。

说一下类组件和函数组件的区别?

1. 语法上的区别:函数式组件是一个纯函数,它是须要承受 props 参数并且返回一个 React 元素就能够了。类组件是须要继承 React.Component 的,而且 class 组件须要创立 render 并且返回 React 元素,语法上来讲更简单。2. 调用形式

函数式组件能够间接调用,返回一个新的 React 元素;类组件在调用时是须要创立一个实例的,而后通过调用实例里的 render 办法来返回一个 React 元素。3. 状态治理

函数式组件没有状态治理,类组件有状态治理。4. 应用场景

类组件没有具体的要求。函数式组件个别是用在大型项目中来宰割大组件(函数式组件不必创立实例,所有更高效),个别状况下能用函数式组件就不必类组件,晋升效率。

常见的位运算符有哪些?其计算规定是什么?

古代计算机中数据都是以二进制的模式存储的,即 0、1 两种状态,计算机对二进制数据进行的运算加减乘除等都是叫位运算,行将符号位独特参加运算的运算。

常见的位运算有以下几种:

运算符 形容 运算规定
& 两个位都为 1 时,后果才为 1
` ` 两个位都为 0 时,后果才为 0
^ 异或 两个位雷同为 0,相异为 1
~ 取反 0 变 1,1 变 0
<< 左移 各二进制位全副左移若干位,高位抛弃,低位补 0
>> 右移 各二进制位全副右移若干位,负数左补 0,正数左补 1,左边抛弃

1. 按位与运算符(&)

定义: 加入运算的两个数据 按二进制位 进行“与”运算。运算规定:

0 & 0 = 0  
0 & 1 = 0  
1 & 0 = 0  
1 & 1 = 1

总结:两位同时为 1,后果才为 1,否则后果为 0。
例如:3&5 即:

0000 0011 
   0000 0101 
 = 0000 0001

因而 3&5 的值为 1。
留神:正数按补码模式加入按位与运算。

用处:

(1)判断奇偶

只有依据最未位是 0 还是 1 来决定,为 0 就是偶数,为 1 就是奇数。因而能够用 if ((i & 1) == 0) 代替 if (i % 2 == 0) 来判断 a 是不是偶数。

(2)清零

如果想将一个单元清零,即便其全副二进制位为 0,只有与一个各位都为零的数值相与,后果为零。

2. 按位或运算符(|)

定义: 加入运算的两个对象按二进制位进行“或”运算。

运算规定:

0 | 0 = 0
0 | 1 = 1  
1 | 0 = 1  
1 | 1 = 1

总结:加入运算的两个对象只有有一个为 1,其值为 1。
例如:3| 5 即:

0000 0011
  0000 0101 
= 0000 0111

因而,3| 5 的值为 7。
留神:正数按补码模式加入按位或运算。

3. 异或运算符(^)

定义: 加入运算的两个数据按二进制位进行“异或”运算。

运算规定:

0 ^ 0 = 0  
0 ^ 1 = 1  
1 ^ 0 = 1  
1 ^ 1 = 0

总结:加入运算的两个对象,如果两个相应位雷同为 0,相异为 1。
例如:3| 5 即:

0000 0011
  0000 0101 
= 0000 0110

因而,3^5 的值为 6。
异或运算的性质:

  • 交换律:(a^b)^c == a^(b^c)
  • 结合律:(a + b)^c == a^b + b^c
  • 对于任何数 x,都有 x^x=0,x^0=x
  • 自反性: a^b^b=a^0=a;

4. 取反运算符 (~)

定义: 加入运算的一个数据按二进制进行“取反”运算。

运算规定:

~ 1 = 0~ 0 = 1

总结:对一个二进制数按位取反,行将 0 变 1,1 变 0。
例如:~6 即:

0000 0110= 1111 1001

在计算机中,负数用原码示意,正数应用补码存储,首先看最高位,最高位 1 示意正数,0 示意负数。此计算机二进制码为正数,最高位为符号位。
当发现按位取反为正数时,就 间接取其补码,变为十进制:

0000 0110   = 1111 1001 反码:1000 0110 补码:1000 0111

因而,~6 的值为 -7。

5. 左移运算符(<<)

定义: 将一个运算对象的各二进制位全副左移若干位,右边的二进制位抛弃,左边补 0。
设 a=1010 1110,a = a<< 2 将 a 的二进制位左移 2 位、右补 0,即得 a =1011 1000。
若左移时舍弃的高位不蕴含 1,则每左移一位,相当于该数乘以 2。

6. 右移运算符(>>)

定义: 将一个数的各二进制位全副右移若干位,负数左补 0,正数左补 1,左边抛弃。
例如:a=a>>2 将 a 的二进制位右移 2 位,左补 0 或者 左补 1 得看被移数是正还是负。
操作数每右移一位,相当于该数除以 2。

7. 原码、补码、反码

下面提到了补码、反码等常识,这里就补充一下。
计算机中的 有符号数 有三种示意办法,即原码、反码和补码。三种示意办法均有符号位和数值位两局部,符号位都是用 0 示意“正”,用 1 示意“负”,而数值位,三种示意办法各不相同。

(1)原码

原码就是一个数的二进制数。例如:10 的原码为 0000 1010

(2)反码

  • 负数的反码与原码雷同,如:10 反码为 0000 1010
  • 正数的反码为除符号位,按位取反,即 0 变 1,1 变 0。

例如:-10

原码:1000 1010
反码:1111 0101

(3)补码

  • 负数的补码与原码雷同,如:10 补码为 0000 1010
  • 正数的补码是原码除符号位外的所有位取反即 0 变 1,1 变 0,而后加 1,也就是反码加 1。

例如:-10

原码:1000 1010
反码:1111 0101
补码:1111 0110

对对象与数组的解构的了解

解构是 ES6 提供的一种新的提取数据的模式,这种模式可能从对象或数组里有针对性地拿到想要的数值。1)数组的解构 在解构数组时,以元素的地位为匹配条件来提取想要的数据的:

const [a, b, c] = [1, 2, 3]

最终,a、b、c 别离被赋予了数组第 0、1、2 个索引位的值:

数组里的 0、1、2 索引位的元素值,精准地被映射到了左侧的第 0、1、2 个变量里去,这就是数组解构的工作模式。还能够通过给左侧变量数组设置空占位的形式,实现对数组中某几个元素的精准提取:

const [a,,c] = [1,2,3]

通过把两头位留空,能够顺利地把数组第一位和最初一位的值赋给 a、c 两个变量:

2)对象的解构 对象解构比数组构造略微简单一些,也更显弱小。在解构对象时,是以属性的名称为匹配条件,来提取想要的数据的。当初定义一个对象:

const stu = {
  name: 'Bob',
  age: 24
}

如果想要解构它的两个自有属性,能够这样:

const {name, age} = stu

这样就失去了 name 和 age 两个和 stu 平级的变量:

留神,对象解构严格以属性名作为定位根据,所以就算调换了 name 和 age 的地位,后果也是一样的:

const {age, name} = stu

Promise 以及相干办法的实现

题目形容: 手写 Promise 以及 Promise.all Promise.race 的实现

实现代码如下:

class Mypromise {constructor(fn) {
    // 示意状态
    this.state = "pending";
    // 示意 then 注册的胜利函数
    this.successFun = [];
    // 示意 then 注册的失败函数
    this.failFun = [];

    let resolve = (val) => {
      // 放弃状态扭转不可变(resolve 和 reject 只准触发一种)if (this.state !== "pending") return;

      // 胜利触发机会  扭转状态 同时执行在 then 注册的回调事件
      this.state = "success";
      // 为了保障 then 事件先注册(次要是思考在 promise 外面写同步代码)promise 标准 这里为模仿异步
      setTimeout(() => {
        // 执行以后事件外面所有的注册函数
        this.successFun.forEach((item) => item.call(this, val));
      });
    };

    let reject = (err) => {if (this.state !== "pending") return;
      // 失败触发机会  扭转状态 同时执行在 then 注册的回调事件
      this.state = "fail";
      // 为了保障 then 事件先注册(次要是思考在 promise 外面写同步代码)promise 标准 这里模仿异步
      setTimeout(() => {this.failFun.forEach((item) => item.call(this, err));
      });
    };
    // 调用函数
    try {fn(resolve, reject);
    } catch (error) {reject(error);
    }
  }

  // 实例办法 then

  then(resolveCallback, rejectCallback) {
    // 判断回调是否是函数
    resolveCallback =
      typeof resolveCallback !== "function" ? (v) => v : resolveCallback;
    rejectCallback =
      typeof rejectCallback !== "function"
        ? (err) => {throw err;}
        : rejectCallback;
    // 为了放弃链式调用  持续返回 promise
    return new Mypromise((resolve, reject) => {
      // 将回调注册到 successFun 事件汇合外面去
      this.successFun.push((val) => {
        try {
          //    执行回调函数
          let x = resolveCallback(val);
          //(最难的一点)// 如果回调函数后果是一般值 那么就 resolve 进来给下一个 then 链式调用  如果是一个 promise 对象(代表又是一个异步)那么调用 x 的 then 办法 将 resolve 和 reject 传进去 等到 x 外部的异步 执行结束的时候(状态实现)就会主动执行传入的 resolve 这样就管制了链式调用的程序
          x instanceof Mypromise ? x.then(resolve, reject) : resolve(x);
        } catch (error) {reject(error);
        }
      });

      this.failFun.push((val) => {
        try {
          //    执行回调函数
          let x = rejectCallback(val);
          x instanceof Mypromise ? x.then(resolve, reject) : reject(x);
        } catch (error) {reject(error);
        }
      });
    });
  }
  // 静态方法
  static all(promiseArr) {let result = [];
    // 申明一个计数器 每一个 promise 返回就加一
    let count = 0;
    return new Mypromise((resolve, reject) => {for (let i = 0; i < promiseArr.length; i++) {
      // 这里用 Promise.resolve 包装一下 避免不是 Promise 类型传进来
        Promise.resolve(promiseArr[i]).then((res) => {// 这里不能间接 push 数组  因为要管制程序一一对应(感激评论区斧正)
            result[i] = res;
            count++;
            // 只有全副的 promise 执行胜利之后才 resolve 进来
            if (count === promiseArr.length) {resolve(result);
            }
          },
          (err) => {reject(err);
          }
        );
      }
    });
  }
  // 静态方法
  static race(promiseArr) {return new Mypromise((resolve, reject) => {for (let i = 0; i < promiseArr.length; i++) {Promise.resolve(promiseArr[i]).then((res) => {
            //promise 数组只有有任何一个 promise 状态变更  就能够返回
            resolve(res);
          },
          (err) => {reject(err);
          }
        );
      }
    });
  }
}

// 应用
// let promise1 = new Mypromise((resolve, reject) => {//   setTimeout(() => {//     resolve(123);
//   }, 2000);
// });
// let promise2 = new Mypromise((resolve, reject) => {//   setTimeout(() => {//     resolve(1234);
//   }, 1000);
// });

// Mypromise.all([promise1,promise2]).then(res=>{//   console.log(res);
// })

// Mypromise.race([promise1, promise2]).then(res => {//   console.log(res);
// });

// promise1
//   .then(
//     res => {//       console.log(res); // 过两秒输入 123
//       return new Mypromise((resolve, reject) => {//         setTimeout(() => {//           resolve("success");
//         }, 1000);
//       });
//     },
//     err => {//       console.log(err);
//     }
//   )
//   .then(
//     res => {//       console.log(res); // 再过一秒输入 success
//     },
//     err => {//       console.log(err);
//     }
//   );

扩大思考: 如何勾销 promise

Promise.race()办法能够用来竞争 Promise
能够借助这个个性 本人包装一个 空的 Promise 与要发动的 Promise 来实现

function wrap(pro) {let obj = {};
  // 结构一个新的 promise 用来竞争
  let p1 = new Promise((resolve, reject) => {
    obj.resolve = resolve;
    obj.reject = reject;
  });

  obj.promise = Promise.race([p1, pro]);
  return obj;
}

let testPro = new Promise((resolve, reject) => {setTimeout(() => {resolve(123);
  }, 1000);
});

let wrapPro = wrap(testPro);
wrapPro.promise.then((res) => {console.log(res);
});
wrapPro.resolve("被拦挡了");

代码输入问题

window.number = 2;
var obj = {
 number: 3,
 db1: (function(){console.log(this);
   this.number *= 4;
   return function(){console.log(this);
     this.number *= 5;
   }
 })()}
var db1 = obj.db1;
db1();
obj.db1();
console.log(obj.number);     // 15
console.log(window.number);  // 40

这道题目看清起来有点乱,然而实际上是考查 this 指向的:

  1. 执行 db1()时,this 指向全局作用域,所以 window.number 4 = 8,而后执行匿名函数,所以 window.number 5 = 40;
  2. 执行 obj.db1(); 时,this 指向 obj 对象,执行匿名函数,所以 obj.numer * 5 = 15。

对 CSS 工程化的了解

CSS 工程化是为了解决以下问题:

  1. 宏观设计:CSS 代码如何组织、如何拆分、模块构造怎么设计?
  2. 编码优化:怎么写出更好的 CSS?
  3. 构建:如何解决我的 CSS,能力让它的打包后果最优?
  4. 可维护性:代码写完了,如何最小化它后续的变更老本?如何确保任何一个共事都能轻松接手?

以下三个方向都是时下比拟风行的、普适性十分好的 CSS 工程化实际:

  • 预处理器:Less、Sass 等;
  • 重要的工程化插件:PostCss;
  • Webpack loader 等。

基于这三个方向,能够衍生出一些具备典型意义的子问题,这里咱们一一来看:

(1)预处理器:为什么要用预处理器?它的呈现是为了解决什么问题?

预处理器,其实就是 CSS 世界的“轮子”。预处理器反对咱们写一种相似 CSS、但理论并不是 CSS 的语言,而后把它编译成 CSS 代码:那为什么写 CSS 代码写得好好的,偏偏要转去写“类 CSS”呢?这就和原本用 JS 也能够实现所有性能,但最初却写 React 的 jsx 或者 Vue 的模板语法一样——为了爽!要想晓得有了预处理器有多爽,首先要晓得的是传统 CSS 有多不爽。随着前端业务复杂度的进步,前端工程中对 CSS 提出了以下的诉求:

  1. 宏观设计上:咱们心愿能优化 CSS 文件的目录构造,对现有的 CSS 文件实现复用;
  2. 编码优化上:咱们心愿能写出构造清晰、扼要易懂的 CSS,须要它具备高深莫测的嵌套层级关系,而不是无差别的一铺到底写法;咱们心愿它具备变量特色、计算能力、循环能力等等更强的可编程性,这样咱们能够少写一些无用的代码;
  3. 可维护性上:更强的可编程性意味着更优质的代码构造,实现复用意味着更简略的目录构造和更强的拓展能力,这两点如果能做到,天然会带来更强的可维护性。

这三点是传统 CSS 所做不到的,也正是预处理器所解决掉的问题。预处理器广泛会具备这样的个性:

  • 嵌套代码的能力,通过嵌套来反映不同 css 属性之间的层级关系;
  • 反对定义 css 变量;
  • 提供计算函数;
  • 容许对代码片段进行 extend 和 mixin;
  • 反对循环语句的应用;
  • 反对将 CSS 文件模块化,实现复用。

(2)PostCss:PostCss 是如何工作的?咱们在什么场景下会应用 PostCss?

它和预处理器的不同就在于,预处理器解决的是 类 CSS,而 PostCss 解决的就是 CSS 自身。Babel 能够将高版本的 JS 代码转换为低版本的 JS 代码。PostCss 做的是相似的事件:它能够编译尚未被浏览器广泛支持的先进的 CSS 语法,还能够主动为一些须要额定兼容的语法减少前缀。更强的是,因为 PostCss 有着弱小的插件机制,反对各种各样的扩大,极大地强化了 CSS 的能力。

PostCss 在业务中的应用场景十分多:

  • 进步 CSS 代码的可读性:PostCss 其实能够做相似预处理器能做的工作;
  • 当咱们的 CSS 代码须要适配低版本浏览器时,PostCss 的 Autoprefixer 插件能够帮忙咱们主动减少浏览器前缀;
  • 容许咱们编写面向未来的 CSS:PostCss 可能帮忙咱们编译 CSS next 代码;

(3)Webpack 能解决 CSS 吗?如何实现? Webpack 能解决 CSS 吗:

  • Webpack 在裸奔的状态下,是不能解决 CSS 的,Webpack 自身是一个面向 JavaScript 且只能解决 JavaScript 代码的模块化打包工具;
  • Webpack 在 loader 的辅助下,是能够解决 CSS 的。

如何用 Webpack 实现对 CSS 的解决:

  • Webpack 中操作 CSS 须要应用的两个要害的 loader:css-loader 和 style-loader
  • 留神,答出“用什么”有时候可能还不够,面试官会狐疑你是不是在背答案,所以你还须要理解每个 loader 都做了什么事件:

    • css-loader:导入 CSS 模块,对 CSS 代码进行编译解决;
    • style-loader:创立 style 标签,把 CSS 内容写入标签。

在理论应用中,css-loader 的执行程序肯定要安顿在 style-loader 的后面。因为只有实现了编译过程,才能够对 css 代码进行插入;若提前插入了未编译的代码,那么 webpack 是无奈了解这坨货色的,它会无情报错。

对 AJAX 的了解,实现一个 AJAX 申请

AJAX 是 Asynchronous JavaScript and XML 的缩写,指的是通过 JavaScript 的 异步通信,从服务器获取 XML 文档从中提取数据,再更新以后网页的对应局部,而不必刷新整个网页。

创立 AJAX 申请的步骤:

  • 创立一个 XMLHttpRequest 对象。
  • 在这个对象上 应用 open 办法创立一个 HTTP 申请,open 办法所须要的参数是申请的办法、申请的地址、是否异步和用户的认证信息。
  • 在发动申请前,能够为这个对象 增加一些信息和监听函数。比如说能够通过 setRequestHeader 办法来为申请增加头信息。还能够为这个对象增加一个状态监听函数。一个 XMLHttpRequest 对象一共有 5 个状态,当它的状态变动时会触发 onreadystatechange 事件,能够通过设置监听函数,来解决申请胜利后的后果。当对象的 readyState 变为 4 的时候,代表服务器返回的数据接管实现,这个时候能够通过判断申请的状态,如果状态是 2xx 或者 304 的话则代表返回失常。这个时候就能够通过 response 中的数据来对页面进行更新了。
  • 当对象的属性和监听函数设置实现后,最初调 用 sent 办法来向服务器发动申请,能够传入参数作为发送的数据体。
const SERVER_URL = "/server";
let xhr = new XMLHttpRequest();
// 创立 Http 申请
xhr.open("GET", url, true);
// 设置状态监听函数
xhr.onreadystatechange = function() {if (this.readyState !== 4) return;
  // 当申请胜利时
  if (this.status === 200) {handle(this.response);
  } else {console.error(this.statusText);
  }
};
// 设置申请失败时的监听函数
xhr.onerror = function() {console.error(this.statusText);
};
// 设置申请头信息
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
// 发送 Http 申请
xhr.send(null);

应用 Promise 封装 AJAX:

// promise 封装实现:function getJSON(url) {
  // 创立一个 promise 对象
  let promise = new Promise(function(resolve, reject) {let xhr = new XMLHttpRequest();
    // 新建一个 http 申请
    xhr.open("GET", url, true);
    // 设置状态的监听函数
    xhr.onreadystatechange = function() {if (this.readyState !== 4) return;
      // 当申请胜利或失败时,扭转 promise 的状态
      if (this.status === 200) {resolve(this.response);
      } else {reject(new Error(this.statusText));
      }
    };
    // 设置谬误监听函数
    xhr.onerror = function() {reject(new Error(this.statusText));
    };
    // 设置响应的数据类型
    xhr.responseType = "json";
    // 设置申请头信息
    xhr.setRequestHeader("Accept", "application/json");
    // 发送 http 申请
    xhr.send(null);
  });
  return promise;
}

对于原型的继承咱们借助寄生组合继承

function Person(obj) {
    this.name = obj.name
    this.age = obj.age
}
Person.prototype.add = function(value){console.log(value)
}
var p1 = new Person({name:"番茄", age: 18})

function Person1(obj) {Person.call(this, obj)
    this.sex = obj.sex
}
// 这一步是继承的要害
Person1.prototype = Object.create(Person.prototype)
Person1.prototype.play = function(value){console.log(value)
}
var p2 = new Person1({name:"鸡蛋", age: 118, sex: "男"})

z-index 属性在什么状况下会生效

通常 z-index 的应用是在有两个重叠的标签,在肯定的状况下管制其中一个在另一个的上方或者下方呈现。z-index 值越大就越是在下层。z-index 元素的 position 属性须要是 relative,absolute 或是 fixed。

z-index 属性在下列状况下会生效:

  • 父元素 position 为 relative 时,子元素的 z -index 生效。解决:父元素 position 改为 absolute 或 static;
  • 元素没有设置 position 属性为非 static 属性。解决:设置该元素的 position 属性为 relative,absolute 或是 fixed 中的一种;
  • 元素在设置 z -index 的同时还设置了 float 浮动。解决:float 去除,改为 display:inline-block;

实现一个扇形

用 CSS 实现扇形的思路和三角形基本一致,就是多了一个圆角的款式,实现一个 90°的扇形:

div{
    border: 100px solid transparent;
    width: 0;
    heigt: 0;
    border-radius: 100px;
    border-top-color: red;
}

10 个 Ajax 同时发动申请,全副返回展现后果,并且至少容许三次失败,说出设计思路

这个问题置信很多人会第一工夫想到 Promise.all,然而这个函数有一个局限在于如果失败一次就返回了,间接这样实现会有点问题,须要变通下。以下是两种实现思路

// 以下是不残缺代码,着重于思路 非 Promise 写法
let successCount = 0
let errorCount = 0
let datas = []
ajax(url, (res) => {if (success) {
         success++
         if (success + errorCount === 10) {console.log(datas)
         } else {datas.push(res.data)
         }
     } else {
         errorCount++
         if (errorCount > 3) {
            // 失败次数大于 3 次就应该报错了
             throw Error('失败三次')
         }
     }
})
// Promise 写法
let errorCount = 0
let p = new Promise((resolve, reject) => {if (success) {resolve(res.data)
     } else {
         errorCount++
         if (errorCount > 3) {
            // 失败次数大于 3 次就应该报错了
            reject(error)
         } else {resolve(error)
         }
     }
})
Promise.all([p]).then(v => {console.log(v);
});

首屏和白屏工夫如何计算

首屏工夫的计算,能够由 Native WebView 提供的相似 onload 的办法实现,在 ios 下对应的是 webViewDidFinishLoad,在 android 下对应的是 onPageFinished 事件。

白屏的定义有多种。能够认为“没有任何内容”是白屏,能够认为“网络或服务异样”是白屏,能够认为“数据加载中”是白屏,能够认为“图片加载不进去”是白屏。场景不同,白屏的计算形式就不雷同。

办法 1:当页面的元素数小于 x 时,则认为页面白屏。比方“没有任何内容”,能够获取页面的 DOM 节点数,判断 DOM 节点数少于某个阈值 X,则认为白屏。办法 2:当页面呈现业务定义的错误码时,则认为是白屏。比方“网络或服务异样”。办法 3:当页面呈现业务定义的特征值时,则认为是白屏。比方“数据加载中”。

行内元素有哪些?块级元素有哪些?空 (void) 元素有那些?

  • 行内元素有:a b span img input select strong
  • 块级元素有:div ul ol li dl dt dd h1 h2 h3 h4 h5 h6 p

空元素,即没有内容的 HTML 元素。空元素是在开始标签中敞开的,也就是空元素没有闭合标签:

  • 常见的有:<br><hr><img><input><link><meta>
  • 鲜见的有:<area><base><col><colgroup><command><embed><keygen><param><source><track><wbr>

URL 有哪些组成部分

一个残缺的 URL 包含以下几局部:

  • 协定局部:该 URL 的协定局部为“http:”,这代表网页应用的是 HTTP 协定。在 Internet 中能够应用多种协定,如 HTTP,FTP 等等本例中应用的是 HTTP 协定。在 ”HTTP” 前面的“//”为分隔符;
  • 域名局部
  • 端口局部:跟在域名前面的是端口,域名和端口之间应用“:”作为分隔符。端口不是一个 URL 必须的局部,如果省略端口局部,将采纳默认端口(HTTP 协定默认端口是 80,HTTPS 协定默认端口是 443);
  • 虚拟目录局部:从域名后的第一个“/”开始到最初一个“/”为止,是虚拟目录局部。虚拟目录也不是一个 URL 必须的局部。本例中的虚拟目录是“/news/”;
  • 文件名局部:从域名后的最初一个“/”开始到“?”为止,是文件名局部,如果没有“?”, 则是从域名后的最初一个“/”开始到“#”为止,是文件局部,如果没有“?”和“#”,那么从域名后的最初一个“/”开始到完结,都是文件名局部。本例中的文件名是“index.asp”。文件名局部也不是一个 URL 必须的局部,如果省略该局部,则应用默认的文件名;
  • 锚局部:从“#”开始到最初,都是锚局部。本例中的锚局部是“name”。锚局部也不是一个 URL 必须的局部;
  • 参数局部:从“?”开始到“#”为止之间的局部为参数局部,又称搜寻局部、查问局部。本例中的参数局部为“boardID=5&ID=24618&page=1”。参数能够容许有多个参数,参数与参数之间用“&”作为分隔符。

实现节流函数和防抖函数

函数防抖的实现:

function debounce(fn, wait) {
  var timer = null;

  return function() {
    var context = this,
      args = [...arguments];

    // 如果此时存在定时器的话,则勾销之前的定时器从新记时
    if (timer) {clearTimeout(timer);
      timer = null;
    }

    // 设置定时器,使事件间隔指定事件后执行
    timer = setTimeout(() => {fn.apply(context, args);
    }, wait);
  };
}

函数节流的实现:

// 工夫戳版
function throttle(fn, delay) {var preTime = Date.now();

  return function() {
    var context = this,
      args = [...arguments],
      nowTime = Date.now();

    // 如果两次工夫距离超过了指定工夫,则执行函数。if (nowTime - preTime >= delay) {preTime = Date.now();
      return fn.apply(context, args);
    }
  };
}

// 定时器版
function throttle (fun, wait){
  let timeout = null
  return function(){
    let context = this
    let args = [...arguments]
    if(!timeout){timeout = setTimeout(() => {fun.apply(context, args)
        timeout = null 
      }, wait)
    }
  }
}

怎么解决白屏问题

1、加 loading
2、骨架屏

AJAX

题目形容: 利用 XMLHttpRequest 手写 AJAX 实现

实现代码如下:

const getJSON = function (url) {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();
    xhr.open("GET", url, false);
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.onreadystatechange = function () {if (xhr.readyState !== 4) return;
      if (xhr.status === 200 || xhr.status === 304) {resolve(xhr.responseText);
      } else {reject(new Error(xhr.responseText));
      }
    };
    xhr.send();});
};

如何判断一个对象是不是空对象?

Object.keys(obj).length === 0

手写题:在线编程,getUrlParams(url,key); 就是很简略的获取 url 的某个参数的问题,但要思考边界状况,多个返回值等等

setTimeout 模仿 setInterval

形容 :应用setTimeout 模仿实现 setInterval 的性能。

实现

const mySetInterval(fn, time) {
    let timer = null;
    const interval = () => {timer = setTimeout(() => {fn();  // time 工夫之后会执行真正的函数 fn
            interval();  // 同时再次调用 interval 自身}, time)
    }
    interval();  // 开始执行
    // 返回用于敞开定时器的函数
    return () => clearTimeout(timer);
}

// 测试
const cancel = mySetInterval(() => console.log(1), 400);
setTimeout(() => {cancel();
}, 1000);  
// 打印两次 1

介绍下 promise 的个性、优缺点,外部是如何实现的,入手实现 Promise

1)Promise 根本个性

  • 1、Promise 有三种状态:pending(进行中)、fulfilled(已胜利)、rejected(已失败)
  • 2、Promise 对象承受一个回调函数作为参数, 该回调函数承受两个参数,别离是胜利时的回调 resolve 和失败时的回调 reject;另外 resolve 的参数除了正常值以外,还可能是一个 Promise 对象的实例;reject 的参数通常是一个 Error 对象的实例。
  • 3、then 办法返回一个新的 Promise 实例,并接管两个参数 onResolved(fulfilled 状态的回调);onRejected(rejected 状态的回调,该参数可选)
  • 4、catch 办法返回一个新的 Promise 实例
  • 5、finally 办法不论 Promise 状态如何都会执行,该办法的回调函数不承受任何参数
  • 6、Promise.all()办法将多个多个 Promise 实例,包装成一个新的 Promise 实例,该办法承受一个由 Promise 对象组成的数组作为参数 (Promise.all() 办法的参数能够不是数组,但必须具备 Iterator 接口,且返回的每个成员都是 Promise 实例),留神参数中只有有一个实例触发 catch 办法,都会触发 Promise.all()办法返回的新的实例的 catch 办法,如果参数中的某个实例自身调用了 catch 办法,将不会触发 Promise.all()办法返回的新实例的 catch 办法
  • 7、Promise.race()办法的参数与 Promise.all 办法一样,参数中的实例只有有一个率先扭转状态就会将该实例的状态传给 Promise.race()办法,并将返回值作为 Promise.race()办法产生的 Promise 实例的返回值
  • 8、Promise.resolve()将现有对象转为 Promise 对象,如果该办法的参数为一个 Promise 对象,Promise.resolve()将不做任何解决;如果参数 thenable 对象 (即具备 then 办法),Promise.resolve() 将该对象转为 Promise 对象并立刻执行 then 办法;如果参数是一个原始值,或者是一个不具备 then 办法的对象,则 Promise.resolve 办法返回一个新的 Promise 对象,状态为 fulfilled,其参数将会作为 then 办法中 onResolved 回调函数的参数,如果 Promise.resolve 办法不带参数,会间接返回一个 fulfilled 状态的 Promise 对象。须要留神的是,立刻 resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的完结时执行,而不是在下一轮“事件循环”的开始时。
  • 9、Promise.reject()同样返回一个新的 Promise 对象,状态为 rejected,无论传入任何参数都将作为 reject()的参数

2)Promise 长处

  • ①对立异步 API

    • Promise 的一个重要长处是它将逐步被用作浏览器的异步 API,对立当初各种各样的 API,以及不兼容的模式和手法。
  • ②Promise 与事件比照

    • 和事件相比拟,Promise 更适宜解决一次性的后果。在后果计算出来之前或之后注册回调函数都是能够的,都能够拿到正确的值。Promise 的这个长处很天然。然而,不能应用 Promise 解决屡次触发的事件。链式解决是 Promise 的又一长处,然而事件却不能这样链式解决。
  • ③Promise 与回调比照

    • 解决了回调天堂的问题,将异步操作以同步操作的流程表达出来。
  • ④Promise 带来的额定益处是蕴含了更好的错误处理形式(蕴含了异样解决),并且写起来很轻松(因为能够重用一些同步的工具,比方 Array.prototype.map())。

3)Promise 毛病

  • 1、无奈勾销 Promise,一旦新建它就会立刻执行,无奈中途勾销。
  • 2、如果不设置回调函数,Promise 外部抛出的谬误,不会反馈到内部。
  • 3、当处于 Pending 状态时,无奈得悉目前停顿到哪一个阶段(刚刚开始还是行将实现)。
  • 4、Promise 真正执行回调的时候,定义 Promise 那局部实际上曾经走完了,所以 Promise 的报错堆栈上下文不太敌对。

4)简略代码实现
最简略的 Promise 实现有 7 个次要属性, state(状态), value(胜利返回值), reason(错误信息), resolve 办法, reject 办法, then 办法

class Promise{constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    let resolve = value => {if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
      }
    };
    let reject = reason => {if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
      }
    };
    try {
      // 立刻执行函数
      executor(resolve, reject);
    } catch (err) {reject(err);
    }
  }
  then(onFulfilled, onRejected) {if (this.state === 'fulfilled') {let x = onFulfilled(this.value);
    };
    if (this.state === 'rejected') {let x = onRejected(this.reason);
    };
  }
}

5)面试够用版

function myPromise(constructor){ let self=this;
  self.status="pending" // 定义状态扭转前的初始状态 
  self.value=undefined;// 定义状态为 resolved 的时候的状态 
  self.reason=undefined;// 定义状态为 rejected 的时候的状态 
  function resolve(value){
    // 两个 ==="pending",保障了了状态的扭转是不不可逆的 
    if(self.status==="pending"){
      self.value=value;
      self.status="resolved"; 
    }
  }
  function reject(reason){
     // 两个 ==="pending",保障了了状态的扭转是不不可逆的
     if(self.status==="pending"){
        self.reason=reason;
        self.status="rejected"; 
      }
  }
  // 捕捉结构异样 
  try{constructor(resolve,reject);
  }catch(e){reject(e);
    } 
}
myPromise.prototype.then=function(onFullfilled,onRejected){ 
  let self=this;
  switch(self.status){case "resolved": onFullfilled(self.value); break;
    case "rejected": onRejected(self.reason); break;
    default: 
  }
}

// 测试
var p=new myPromise(function(resolve,reject){resolve(1)}); 
p.then(function(x){console.log(x)})
// 输入 1 

6)大厂专供版

const PENDING = "pending"; 
const FULFILLED = "fulfilled"; 
const REJECTED = "rejected";
const resolvePromise = (promise, x, resolve, reject) => {if (x === promise) {
    // If promise and x refer to the same object, reject promise with a TypeError as the reason.
    reject(new TypeError('循环援用'))
  }
  // if x is an object or function,
  if (x !== null && typeof x === 'object' || typeof x === 'function') {
    // If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
    let called
    try { // If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
      let then = x.then // Let then be x.then
      // If then is a function, call it with x as this
      if (typeof then === 'function') {// If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)
        // If/when rejectPromise is called with a reason r, reject promise with r.
        then.call(x, y => {if (called) return
          called = true
          resolvePromise(promise, y, resolve, reject)
        }, r => {if (called) return
          called = true
          reject(r)
        })
      } else {
        // If then is not a function, fulfill promise with x.
        resolve(x)
      }
    } catch (e) {if (called) return
      called = true
      reject(e)
    }
  } else {
    // If x is not an object or function, fulfill promise with x
    resolve(x)
  }
}
function Promise(excutor) {
  let that = this; // 缓存以后 promise 实例例对象
  that.status = PENDING; // 初始状态
  that.value = undefined; // fulfilled 状态时 返回的信息
  that.reason = undefined; // rejected 状态时 回绝的起因 
  that.onFulfilledCallbacks = []; // 存储 fulfilled 状态对应的 onFulfilled 函数
  that.onRejectedCallbacks = []; // 存储 rejected 状态对应的 onRejected 函数
  function resolve(value) { // value 胜利态时接管的终值
    if(value instanceof Promise) {return value.then(resolve, reject);
    }
    // 实际中要确保 onFulfilled 和 onRejected ⽅办法异步执⾏行行,且应该在 then ⽅办法被调⽤用的那⼀一轮事件循环之后的新执⾏行行栈中执⾏行行。setTimeout(() => {
      // 调⽤用 resolve 回调对应 onFulfilled 函数
      if (that.status === PENDING) {// 只能由 pending 状态 => fulfilled 状态 (防止调⽤用屡次 resolve reject)
        that.status = FULFILLED;
        that.value = value;
        that.onFulfilledCallbacks.forEach(cb => cb(that.value));
      }
    });
  }
  function reject(reason) { // reason 失败态时接管的拒因
    setTimeout(() => {
      // 调⽤用 reject 回调对应 onRejected 函数
      if (that.status === PENDING) {// 只能由 pending 状态 => rejected 状态 (防止调⽤用屡次 resolve reject)
        that.status = REJECTED;
        that.reason = reason;
        that.onRejectedCallbacks.forEach(cb => cb(that.reason));
      }
    });
  }

  // 捕捉在 excutor 执⾏行行器器中抛出的异样
  // new Promise((resolve, reject) => {//     throw new Error('error in excutor')
  // })
  try {excutor(resolve, reject);
  } catch (e) {reject(e);
  }
}
Promise.prototype.then = function(onFulfilled, onRejected) {
  const that = this;
  let newPromise;
  // 解决理参数默认值 保障参数后续可能持续执⾏行行
  onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
  onRejected = typeof onRejected === "function" ? onRejected : reason => {throw reason;};
  if (that.status === FULFILLED) { // 胜利态
    return newPromise = new Promise((resolve, reject) => {setTimeout(() => {
        try{let x = onFulfilled(that.value);
          resolvePromise(newPromise, x, resolve, reject); // 新的 promise resolve 上⼀一个 onFulfilled 的返回值
        } catch(e) {reject(e); // 捕捉前⾯面 onFulfilled 中抛出的异样 then(onFulfilled, onRejected);
        }
      });
    })
  }
  if (that.status === REJECTED) { // 失败态
    return newPromise = new Promise((resolve, reject) => {setTimeout(() => {
        try {let x = onRejected(that.reason);
          resolvePromise(newPromise, x, resolve, reject);
        } catch(e) {reject(e);
        }
      });
    });
  }
  if (that.status === PENDING) { // 期待态
// 当异步调⽤用 resolve/rejected 时 将 onFulfilled/onRejected 收集暂存到汇合中
    return newPromise = new Promise((resolve, reject) => {that.onFulfilledCallbacks.push((value) => {
        try {let x = onFulfilled(value);
          resolvePromise(newPromise, x, resolve, reject);
        } catch(e) {reject(e);
        }
      });
      that.onRejectedCallbacks.push((reason) => {
        try {let x = onRejected(reason);
          resolvePromise(newPromise, x, resolve, reject);
        } catch(e) {reject(e);
        }
      });
    });
  }
};

正文完
 0