关于前端:字节前端必会面试题

52次阅读

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

常见的程度垂直形式有几种?

// 利用相对定位,先将元素的左上角通过 top:50% 和 left:50% 定位到页面的核心,而后再通过 translate 来调整元素的中心点到页面的核心。该办法须要思考浏览器兼容问题。.parent {position: relative;}

.child {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%,-50%);
}
// 利用相对定位,设置四个方向的值都为 0,并将 margin 设置为 auto,因为宽高固定,因而对应方向实现平分,能够实现程度和垂直方向上的居中。该办法实用于盒子有宽高的状况:.parent {position: relative;}

.child {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
}
// 利用相对定位,先将元素的左上角通过 top:50% 和 left:50% 定位到页面的核心,而后再通过 margin 负值来调整元素的中心点到页面的核心。该办法实用于盒子宽高已知的状况
.parent {position: relative;}

.child {
    position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -50px;     /* 本身 height 的一半 */
    margin-left: -50px;    /* 本身 width 的一半 */
}
// 应用 flex 布局,通过 align-items:center 和 justify-content:center 设置容器的垂直和程度方向上为居中对齐,而后它的子元素也能够实现垂直和程度的居中。该办法要 ** 思考兼容的问题 **,该办法在挪动端用的较多:.parent {
    display: flex;
    justify-content:center;
    align-items:center;
}
// 另外,如果父元素设置了 flex 布局,只须要给子元素加上 `margin:auto;` 就能够实现垂直居中布局
.parent{display:flex;}
.child{margin: auto;}

—- 问题知识点分割线 —-

代码输入后果

const promise = new Promise((resolve, reject) => {console.log(1);
  setTimeout(() => {console.log("timerStart");
    resolve("success");
    console.log("timerEnd");
  }, 0);
  console.log(2);
});
promise.then((res) => {console.log(res);
});
console.log(4);

输入后果如下:

1
2
4
timerStart
timerEnd
success

代码执行过程如下:

  • 首先遇到 Promise 构造函数,会先执行外面的内容,打印1
  • 遇到定时器steTimeout,它是一个宏工作,放入宏工作队列;
  • 持续向下执行,打印出 2;
  • 因为 Promise 的状态此时还是 pending,所以promise.then 先不执行;
  • 继续执行上面的同步工作,打印出 4;
  • 此时微工作队列没有工作,继续执行下一轮宏工作,执行steTimeout
  • 首先执行 timerStart,而后遇到了resolve,将promise 的状态改为 resolved 且保留后果并将之前的 promise.then 推入微工作队列,再执行timerEnd
  • 执行完这个宏工作,就去执行微工作 promise.then,打印出resolve 的后果。

—- 问题知识点分割线 —-

函数柯里化

柯里化(currying) 指的是将一个多参数的函数拆分成一系列函数,每个拆分后的函数都只承受一个参数。

对于曾经柯里化后的函数来说,当接管的参数数量与原函数的形参数量雷同时,执行原函数;当接管的参数数量小于原函数的形参数量时,返回一个函数用于接管残余的参数,直至接管的参数数量与形参数量统一,执行原函数。

—- 问题知识点分割线 —-

对浏览器的缓存机制的了解

浏览器缓存的全过程:

  • 浏览器第一次加载资源,服务器返回 200,浏览器从服务器下载资源文件,并缓存资源文件与 response header,以供下次加载时比照应用;
  • 下一次加载资源时,因为强制缓存优先级较高,先比拟以后工夫与上一次返回 200 时的时间差,如果没有超过 cache-control 设置的 max-age,则没有过期,并命中强缓存,间接从本地读取资源。如果浏览器不反对 HTTP1.1,则应用 expires 头判断是否过期;
  • 如果资源已过期,则表明强制缓存没有被命中,则开始协商缓存,向服务器发送带有 If-None-Match 和 If-Modified-Since 的申请;
  • 服务器收到申请后,优先依据 Etag 的值判断被申请的文件有没有做批改,Etag 值统一则没有批改,命中协商缓存,返回 304;如果不统一则有改变,间接返回新的资源文件带上新的 Etag 值并返回 200;
  • 如果服务器收到的申请没有 Etag 值,则将 If-Modified-Since 和被申请文件的最初批改工夫做比对,统一则命中协商缓存,返回 304;不统一则返回新的 last-modified 和文件并返回 200;

    很多网站的资源前面都加了版本号,这样做的目标是:每次降级了 JS 或 CSS 文件后,为了避免浏览器进行缓存,强制扭转版本号,客户端浏览器就会从新下载新的 JS 或 CSS 文件,以保障用户可能及时取得网站的最新更新。

—- 问题知识点分割线 —-

如何防止 ajax 数据申请从新获取

一般而言,ajax 申请的数据都放在 redux 中存取。

—- 问题知识点分割线 —-

Promise.resolve

Promise.resolve = function(value) {
    // 1. 如果 value 参数是一个 Promise 对象,则一成不变返回该对象
    if(value instanceof Promise) return value;
    // 2. 如果 value 参数是一个具备 then 办法的对象,则将这个对象转为 Promise 对象,并立刻执行它的 then 办法
    if(typeof value === "object" && 'then' in value) {return new Promise((resolve, reject) => {value.then(resolve, reject);
        });
    }
    // 3. 否则返回一个新的 Promise 对象,状态为 fulfilled
    return new Promise(resolve => resolve(value));
}

—- 问题知识点分割线 —-

数组去重

应用 indexOf/includes 实现

function unique(arr) {var res = [];
    for(var i = 0; i < arr.length; i++) {if(res.indexOf(arr[i]) === -1) res.push(arr[i]);
        // if(!res.includes(arr[i])) res.push(arr[i]);
    }
    return res;
}

应用 filter(forEach) + indexOf/includes 实现

// filter
function unique(arr) {var res = arr.filter((value, index) => {
        // 只存第一个呈现的元素
        return arr.indexOf(value) === index;
    });
    return res;
}
// forEach
function unique(arr) {var res = [];
    arr.forEach((value) => {if(!res.includes(value)) res.push(value);
    });
    return res;
}

非 API 版本(原生)实现

function unique(arr) {var res = [];
    for(var i = 0; i < arr.length; i++) {
        var flag = false;
        for(var j = 0; j < res.length; j++) {if(arr[i] === res[j]) {
                flag = true;
                break;
            }
        }
        if(flag === false) res.push(arr[i]);
    }
    return res;
}

ES6 应用 Set + 扩大运算符(…)/Array.from() 实现

function unique(arr) {// return [...new Set(arr)];
    return Array.from(new Set(arr));
}

—- 问题知识点分割线 —-

documentFragment 是什么?用它跟间接操作 DOM 的区别是什么?

MDN 中对 documentFragment 的解释:

DocumentFragment,文档片段接口,一个没有父对象的最小文档对象。它被作为一个轻量版的 Document 应用,就像规范的 document 一样,存储由节点(nodes)组成的文档构造。与 document 相比,最大的区别是 DocumentFragment 不是实在 DOM 树的一部分,它的变动不会触发 DOM 树的从新渲染,且不会导致性能等问题。

当咱们把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 本身,而是它的所有子孙节点。在频繁的 DOM 操作时,咱们就能够将 DOM 元素插入 DocumentFragment,之后一次性的将所有的子孙节点插入文档中。和间接操作 DOM 相比,将 DocumentFragment 节点插入 DOM 树时,不会触发页面的重绘,这样就大大提高了页面的性能。

—- 问题知识点分割线 —-

写版本号排序的办法

题目形容: 有一组版本号如下[‘0.1.1’, ‘2.3.3’, ‘0.302.1’, ‘4.2’, ‘4.3.5’, ‘4.3.4.5’]。当初须要对其进行排序,排序的后果为 [‘4.3.5′,’4.3.4.5′,’2.3.3′,’0.302.1′,’0.1.1’]

实现代码如下:

arr.sort((a, b) => {
  let i = 0;
  const arr1 = a.split(".");
  const arr2 = b.split(".");

  while (true) {const s1 = arr1[i];
    const s2 = arr2[i];
    i++;
    if (s1 === undefined || s2 === undefined) {return arr2.length - arr1.length;}

    if (s1 === s2) continue;

    return s2 - s1;
  }
});
console.log(arr);

—- 问题知识点分割线 —-

谈一谈队头阻塞问题

什么是队头阻塞?

对于每一个 HTTP 申请而言,这些工作是会被放入一个工作队列中串行执行的,一旦队首工作申请太慢时,就会阻塞前面的申请解决,这就是 HTTP 队头阻塞 问题。

有什么解决办法吗👇

并发连贯

咱们晓得对于一个域名而言,是容许调配多个长连贯的,那么能够了解成减少了工作队列,也就是说不会导致一个工作阻塞了该工作队列的其余工作,在 RFC 标准 中规定客户端最多并发 2 个连贯,不过理论状况就是要比这个还要多,举个例子,Chrome 中是 6 个。

域名分片

  • 顾名思义,咱们能够在一个域名下分出多个二级域名进去,而它们最终指向的还是同一个服务器,这样子的话就能够并发解决的工作队列更多,也更好的解决了队头阻塞的问题。
  • 举个例子,比方TianTian.com,能够分出很多二级域名,比方Day1.TianTian.comDay2.TianTian.com,Day3.TianTian.com, 这样子就能够无效解决队头阻塞问题。

—- 问题知识点分割线 —-

对原型、原型链的了解

在 JavaScript 中是应用构造函数来新建一个对象的,每一个构造函数的外部都有一个 prototype 属性,它的属性值是一个对象,这个对象蕴含了能够由该构造函数的所有实例共享的属性和办法。当应用构造函数新建一个对象后,在这个对象的外部将蕴含一个指针,这个指针指向构造函数的 prototype 属性对应的值,在 ES5 中这个指针被称为对象的原型。一般来说不应该可能获取到这个值的,然而当初浏览器中都实现了 proto 属性来拜访这个属性,然而最好不要应用这个属性,因为它不是标准中规定的。ES5 中新增了一个 Object.getPrototypeOf() 办法,能够通过这个办法来获取对象的原型。

当拜访一个对象的属性时,如果这个对象外部不存在这个属性,那么它就会去它的原型对象里找这个属性,这个原型对象又会有本人的原型,于是就这样始终找上来,也就是原型链的概念。原型链的止境一般来说都是 Object.prototype 所以这就是新建的对象为什么可能应用 toString() 等办法的起因。

特点: JavaScript 对象是通过援用来传递的,创立的每个新对象实体中并没有一份属于本人的原型正本。当批改原型时,与之相干的对象也会继承这一扭转。

—- 问题知识点分割线 —-

函数柯里化

什么叫函数柯里化?其实就是将应用多个参数的函数转换成一系列应用一个参数的函数的技术。还不懂?来举个例子。

function add(a, b, c) {return a + b + c}
add(1, 2, 3)
let addCurry = curry(add)
addCurry(1)(2)(3)

当初就是要实现 curry 这个函数,使函数从一次调用传入多个参数变成屡次调用每次传一个参数。

function curry(fn) {let judge = (...args) => {if (args.length == fn.length) return fn(...args)
        return (...arg) => judge(...args, ...arg)
    }
    return judge
}

—- 问题知识点分割线 —-

如何判断数组类型

Array.isArray

—- 问题知识点分割线 —-

数组可能调用的函数有那些?

  • push
  • pop
  • splice
  • slice
  • shift
  • unshift
  • sort
  • find
  • findIndex
  • map/filter/reduce 等函数式编程办法
  • 还有一些原型链上的办法:toString/valudOf

—- 问题知识点分割线 —-

单行、多行文本溢出暗藏

  • 单行文本溢出
overflow: hidden;            // 溢出暗藏
text-overflow: ellipsis;      // 溢出用省略号显示
white-space: nowrap;         // 规定段落中的文本不进行换行
  • 多行文本溢出
overflow: hidden;            // 溢出暗藏
text-overflow: ellipsis;     // 溢出用省略号显示
display:-webkit-box;         // 作为弹性伸缩盒子模型显示。-webkit-box-orient:vertical; // 设置伸缩盒子的子元素排列形式:从上到下垂直排列
-webkit-line-clamp:3;        // 显示的行数

留神:因为下面的三个属性都是 CSS3 的属性,没有浏览器能够兼容,所以要在后面加一个-webkit- 来兼容一部分浏览器。

—- 问题知识点分割线 —-

介绍一下 HTTPS 和 HTTP 区别

HTTPS 要比 HTTPS 多了 secure 安全性这个概念,实际上,HTTPS 并不是一个新的应用层协定,它其实就是 HTTP + TLS/SSL 协定组合而成,而安全性的保障正是 SSL/TLS 所做的工作。

SSL

安全套接层(Secure Sockets Layer)

TLS

(传输层平安,Transport Layer Security)

当初支流的版本是 TLS/1.2, 之前的 TLS1.0、TLS1.1 都被认为是不平安的,在不久的未来会被齐全淘汰。

HTTPS 就是身披了一层 SSL 的 HTTP

那么区别有哪些呢👇

  • HTTP 是明文传输协定,HTTPS 协定是由 SSL+HTTP 协定构建的可进行加密传输、身份认证的网络协议,比 HTTP 协定平安。
  • HTTPS 比 HTTP 更加平安,对搜索引擎更敌对,利于 SEO, 谷歌、百度优先索引 HTTPS 网页。
  • HTTPS 规范端口 443,HTTP 规范端口 80。
  • HTTPS 须要用到 SSL 证书,而 HTTP 不必。

我感觉记住以下两点 HTTPS 次要作用就行👇

  1. 对数据进行加密,并建设一个信息安全通道,来保障传输过程中的数据安全;
  2. 对网站服务器进行实在身份认证。

HTTPS 的毛病

  • 证书费用以及更新保护。
  • HTTPS 升高肯定用户访问速度(实际上优化好就不是毛病了)。
  • HTTPS 耗费 CPU 资源,须要减少大量机器。

—- 问题知识点分割线 —-

请实现 DOM2JSON 一个函数,能够把一个 DOM 节点输入 JSON 的格局

题目形容:

<div>
  <span>
    <a></a>
  </span>
  <span>
    <a></a>
    <a></a>
  </span>
</div>

把上诉 dom 构造转成上面的 JSON 格局

{
  tag: 'DIV',
  children: [
    {
      tag: 'SPAN',
      children: [{ tag: 'A', children: [] }
      ]
    },
    {
      tag: 'SPAN',
      children: [{ tag: 'A', children: [] },
        {tag: 'A', children: [] }
      ]
    }
  ]
}

实现代码如下:

function dom2Json(domtree) {let obj = {};
  obj.name = domtree.tagName;
  obj.children = [];
  domtree.childNodes.forEach((child) => obj.children.push(dom2Json(child)));
  return obj;
}

扩大思考: 如果给定的不是一个 Dom 树结构 而是一段 html 字符串 该如何解析?

那么这个问题就相似 Vue 的模板编译原理 咱们能够利用正则 匹配 html 字符串 遇到开始标签 完结标签和文本 解析结束之后生成对应的 ast 并建设相应的父子关联 一直的 advance 截取残余的字符串 直到 html 全副解析结束

—- 问题知识点分割线 —-

对执行上下文的了解

1. 执行上下文类型

(1)全局执行上下文

任何不在函数外部的都是全局执行上下文,它首先会创立一个全局的 window 对象,并且设置 this 的值等于这个全局对象,一个程序中只有一个全局执行上下文。

(2)函数执行上下文

当一个函数被调用时,就会为该函数创立一个新的执行上下文,函数的上下文能够有任意多个。

(3)eval函数执行上下文

执行在 eval 函数中的代码会有属于他本人的执行上下文,不过 eval 函数不常应用,不做介绍。

2. 执行上下文栈
  • JavaScript 引擎应用执行上下文栈来治理执行上下文
  • 当 JavaScript 执行代码时,首先遇到全局代码,会创立一个全局执行上下文并且压入执行栈中,每当遇到一个函数调用,就会为该函数创立一个新的执行上下文并压入栈顶,引擎会执行位于执行上下文栈顶的函数,当函数执行实现之后,执行上下文从栈中弹出,继续执行下一个上下文。当所有的代码都执行结束之后,从栈中弹出全局执行上下文。
let a = 'Hello World!';
function first() {console.log('Inside first function');
  second();
  console.log('Again inside first function');
}
function second() {console.log('Inside second function');
}
first();
// 执行程序
// 先执行 second(), 在执行 first()
3. 创立执行上下文

创立执行上下文有两个阶段:创立阶段 执行阶段

1)创立阶段

(1)this 绑定

  • 在全局执行上下文中,this 指向全局对象(window 对象)
  • 在函数执行上下文中,this 指向取决于函数如何调用。如果它被一个援用对象调用,那么 this 会被设置成那个对象,否则 this 的值被设置为全局对象或者 undefined

(2)创立词法环境组件

  • 词法环境是一种有 标识符——变量映射 的数据结构,标识符是指变量 / 函数名,变量是对理论对象或原始数据的援用。
  • 词法环境的外部有两个组件:加粗款式 :环境记录器: 用来贮存变量个函数申明的理论地位 外部环境的援用:能够拜访父级作用域

(3)创立变量环境组件

  • 变量环境也是一个词法环境,其环境记录器持有变量申明语句在执行上下文中创立的绑定关系。

2)执行阶段 此阶段会实现对变量的调配,最初执行完代码。

简略来说执行上下文就是指:

在执行一点 JS 代码之前,须要先解析代码。解析的时候会先创立一个全局执行上下文环境,先把代码中行将执行的变量、函数申明都拿进去,变量先赋值为 undefined,函数先申明好可应用。这一步执行完了,才开始正式的执行程序。

在一个函数执行之前,也会创立一个函数执行上下文环境,跟全局执行上下文相似,不过函数执行上下文会多出 this、arguments 和函数的参数。

  • 全局上下文:变量定义,函数申明
  • 函数上下文:变量定义,函数申明,thisarguments

—- 问题知识点分割线 —-

对 Promise 的了解

Promise 是异步编程的一种解决方案,它是一个对象,能够获取异步操作的音讯,他的呈现大大改善了异步编程的窘境,防止了天堂回调,它比传统的解决方案回调函数和事件更正当和更弱小。

所谓 Promise,简略说就是一个容器,外面保留着某个将来才会完结的事件(通常是一个异步操作)的后果。从语法上说,Promise 是一个对象,从它能够获取异步操作的音讯。Promise 提供对立的 API,各种异步操作都能够用同样的办法进行解决。

(1)Promise 的实例有 三个状态:

  • Pending(进行中)
  • Resolved(已实现)
  • Rejected(已回绝)

当把一件事件交给 promise 时,它的状态就是 Pending,工作实现了状态就变成了 Resolved、没有实现失败了就变成了 Rejected。

(2)Promise 的实例有 两个过程

  • pending -> fulfilled : Resolved(已实现)
  • pending -> rejected:Rejected(已回绝)

留神:一旦从进行状态变成为其余状态就永远不能更改状态了。

Promise 的特点:

  • 对象的状态不受外界影响。promise 对象代表一个异步操作,有三种状态,pending(进行中)、fulfilled(已胜利)、rejected(已失败)。只有异步操作的后果,能够决定以后是哪一种状态,任何其余操作都无奈扭转这个状态,这也是 promise 这个名字的由来——“承诺”;
  • 一旦状态扭转就不会再变,任何时候都能够失去这个后果。promise 对象的状态扭转,只有两种可能:从 pending 变为 fulfilled,从pending 变为rejected。这时就称为resolved(已定型)。如果扭转曾经产生了,你再对 promise 对象增加回调函数,也会立刻失去这个后果。这与事件(event)齐全不同,事件的特点是:如果你错过了它,再去监听是得不到后果的。

Promise 的毛病:

  • 无奈勾销 Promise,一旦新建它就会立刻执行,无奈中途勾销。
  • 如果不设置回调函数,Promise 外部抛出的谬误,不会反馈到内部。
  • 当处于 pending 状态时,无奈得悉目前停顿到哪一个阶段(刚刚开始还是行将实现)。

总结: Promise 对象是异步编程的一种解决方案,最早由社区提出。Promise 是一个构造函数,接管一个函数作为参数,返回一个 Promise 实例。一个 Promise 实例有三种状态,别离是 pending、resolved 和 rejected,别离代表了进行中、已胜利和已失败。实例的状态只能由 pending 转变 resolved 或者 rejected 状态,并且状态一经扭转,就凝固了,无奈再被扭转了。

状态的扭转是通过 resolve() 和 reject() 函数来实现的,能够在异步操作完结后调用这两个函数扭转 Promise 实例的状态,它的原型上定义了一个 then 办法,应用这个 then 办法能够为两个状态的扭转注册回调函数。这个回调函数属于微工作,会在本轮事件循环的开端执行。

留神: 在结构 Promise 的时候,构造函数外部的代码是立刻执行的

正文完
 0