关于javascript:大厂前端面试考什么

3次阅读

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

nextTick

nextTick 能够让咱们在下次 DOM 更新循环完结之后执行提早回调,用于取得更新后的 DOM

nextTick次要应用了宏工作和微工作。依据执行环境别离尝试采纳

  • Promise
  • MutationObserver
  • setImmediate
  • 如果以上都不行则采纳setTimeout

定义了一个异步办法,屡次调用 nextTick 会将办法存入队列中,通过这个异步办法清空以后队列

Promise.allSettled

形容 :等到所有promise 都返回后果,就返回一个 promise 实例。

实现

Promise.allSettled = function(promises) {return new Promise((resolve, reject) => {if(Array.isArray(promises)) {if(promises.length === 0) return resolve(promises);
            let result = [];
            let count = 0;
            promises.forEach((item, index) => {Promise.resolve(item).then(
                    value => {
                        count++;
                        result[index] = {
                            status: 'fulfilled',
                            value: value
                        };
                        if(count === promises.length) resolve(result);
                    }, 
                    reason => {
                        count++;
                        result[index] = {
                            status: 'rejected'.
                            reason: reason
                        };
                        if(count === promises.length) resolve(result);
                    }
                );
            });
        }
        else return reject(new TypeError("Argument is not iterable"));
    });
}

实现 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);
        }
    });
}

数据类型判断

typeof 能够正确辨认:Undefined、Boolean、Number、String、Symbol、Function 等类型的数据,然而对于其余的都会认为是 object,比方 Null、Date 等,所以通过 typeof 来判断数据类型会不精确。然而能够应用 Object.prototype.toString 实现。

function typeOf(obj) {-   let res = Object.prototype.toString.call(obj).split(' ')[1]
-   res = res.substring(0, res.length - 1).toLowerCase()
-   return res
// 更好的写法
+   return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()}
typeOf([])        // 'array'
typeOf({})        // 'object'
typeOf(new Date)  // 'date'

事件循环机制(Event Loop)

事件循环机制从整体上通知了咱们 JavaScript 代码的执行程序 Event Loop即事件循环,是指浏览器或 Node 的一种解决 javaScript 单线程运行时不会阻塞的一种机制,也就是咱们常常应用 异步 的原理。

先执行 Script 脚本,而后清空微工作队列,而后开始下一轮事件循环,持续先执行宏工作,再清空微工作队列,如此往返。

  • 宏工作:Script/setTimeout/setInterval/setImmediate/ I/O / UI Rendering
  • 微工作:process.nextTick()/Promise

上诉的 setTimeout 和 setInterval 等都是工作源,真正进入工作队列的是他们散发的工作。

优先级

  • setTimeout = setInterval 一个队列
  • setTimeout > setImmediate
  • process.nextTick > Promise
for (const macroTask of macroTaskQueue) {handleMacroTask();    
  for (const microTask of microTaskQueue) {handleMicroTask(microTask);  
  }
}

说一下 HTTP 和 HTTPS 协定的区别?

1、HTTPS 协定须要 CA 证书, 费用较高; 而 HTTP 协定不须要
2、HTTP 协定是超文本传输协定, 信息是明文传输的,HTTPS 则是具备安全性的 SSL 加密传输协定;
3、应用不同的连贯形式, 端口也不同,HTTP 协定端口是 80,HTTPS 协定端口是 443;
4、HTTP 协定连贯很简略, 是无状态的;HTTPS 协定是具备 SSL 和 HTTP 协定构建的可进行加密传输、身份认证的网络协议, 比 HTTP 更加平安

参考 前端进阶面试题具体解答

什么是文档的预解析?

Webkit 和 Firefox 都做了这个优化,当执行 JavaScript 脚本时,另一个线程解析剩下的文档,并加载前面须要通过网络加载的资源。这种形式能够使资源并行加载从而使整体速度更快。须要留神的是,预解析并不扭转 DOM 树,它将这个工作留给主解析过程,本人只解析内部资源的援用,比方内部脚本、样式表及图片。

闭包

首先阐明什么是闭包,闭包简略来说就是函数嵌套函数,外部函数援用来内部函数的变量,从而导致垃圾回收
机制没有把以后变量回收掉,这样的操作带来了内存透露的影响,当内存透露到肯定水平会影响你的我的项目运行
变得卡顿等等问题。因而在我的项目中咱们要尽量避免内存透露。

手写 bind、apply、call

// call

Function.prototype.call = function (context, ...args) {
  context = context || window;

  const fnSymbol = Symbol("fn");
  context[fnSymbol] = this;

  context[fnSymbol](...args);
  delete context[fnSymbol];
}
// apply

Function.prototype.apply = function (context, argsArr) {
  context = context || window;

  const fnSymbol = Symbol("fn");
  context[fnSymbol] = this;

  context[fnSymbol](...argsArr);
  delete context[fnSymbol];
}
// bind

Function.prototype.bind = function (context, ...args) {
  context = context || window;
  const fnSymbol = Symbol("fn");
  context[fnSymbol] = this;

  return function (..._args) {args = args.concat(_args);

    context[fnSymbol](...args);
    delete context[fnSymbol];   
  }
}

JS 整数是怎么示意的?

  • 通过 Number 类型来示意,遵循 IEEE754 规范,通过 64 位来示意一个数字,(1 + 11 + 52),最大平安数字是 Math.pow(2, 53) – 1,对于 16 位十进制。(符号位 + 指数位 + 小数局部无效位)

代码输入后果

Promise.resolve('1')
  .then(res => {console.log(res)
  })
  .finally(() => {console.log('finally')
  })
Promise.resolve('2')
  .finally(() => {console.log('finally2')
      return '我是 finally2 返回的值'
  })
  .then(res => {console.log('finally2 前面的 then 函数', res)
  })

输入后果如下:

1
finally2
finally
finally2 前面的 then 函数 2

.finally()个别用的很少,只有记住以下几点就能够了:

  • .finally()办法不论 Promise 对象最初的状态如何都会执行
  • .finally()办法的回调函数不承受任何的参数,也就是说你在 .finally() 函数中是无奈晓得 Promise 最终的状态是 resolved 还是 rejected
  • 它最终返回的默认会是一个上一次的 Promise 对象值,不过如果抛出的是一个异样则返回异样的 Promise 对象。
  • finally 实质上是 then 办法的特例

.finally()的谬误捕捉:

Promise.resolve('1')
  .finally(() => {console.log('finally1')
    throw new Error('我是 finally 中抛出的异样')
  })
  .then(res => {console.log('finally 前面的 then 函数', res)
  })
  .catch(err => {console.log('捕捉谬误', err)
  })

输入后果为:

'finally1'
'捕捉谬误' Error: 我是 finally 中抛出的异样

Vuex 有哪些根本属性? 为什么 Vuex 的 mutation 中不能做异步操作?

有五种,别离是 State、Getter、Mutation、Action、Module
1、state => 根本数据(数据源寄存地)
2、getters => 从根本数据派生进去的数据
3、mutations => 提交更改数据的办法,同步
4、actions => 像一个装璜器,包裹 mutations,使之能够异步。5、modules => 模块化 Vuex

1、Vuex 中所有的状态更新的惟一路径都是 mutation,异步操作通过 Action 来提交 mutation 实现,这样能够不便地跟踪每一个状态的变动,从而可能实现一些工具帮忙更好地理解咱们的利用。2、每个 mutation 执行实现后都会对应到一个新的状态变更,这样 devtools 就能够打个快照存下来,而后就能够实现 time-travel 了。如果 mutation 反对异步操作,就没有方法晓得状态是何时更新的,无奈很好的进行状态的追踪,给调试带来艰难。

IE 兼容

  • attchEvent(‘on’ + type, handler)
  • detachEvent(‘on’ + type, handler)

实现 LazyMan

题目形容:

实现一个 LazyMan,能够依照以下形式调用:
LazyMan(“Hank”)输入:
Hi! This is Hank!

LazyMan(“Hank”).sleep(10).eat(“dinner”)输入
Hi! This is Hank!
// 期待 10 秒..
Wake up after 10
Eat dinner~

LazyMan(“Hank”).eat(“dinner”).eat(“supper”)输入
Hi This is Hank!
Eat dinner~
Eat supper~

LazyMan(“Hank”).eat(“supper”).sleepFirst(5)输入
// 期待 5 秒
Wake up after 5
Hi This is Hank!
Eat supper

实现代码如下:

class _LazyMan {constructor(name) {this.tasks = [];
    const task = () => {console.log(`Hi! This is ${name}`);
      this.next();};
    this.tasks.push(task);
    setTimeout(() => {// 把 this.next() 放到调用栈清空之后执行
      this.next();}, 0);
  }
  next() {const task = this.tasks.shift(); // 取第一个工作执行
    task && task();}
  sleep(time) {this._sleepWrapper(time, false);
    return this; // 链式调用
  }
  sleepFirst(time) {this._sleepWrapper(time, true);
    return this;
  }
  _sleepWrapper(time, first) {const task = () => {setTimeout(() => {console.log(`Wake up after ${time}`);
        this.next();}, time * 1000);
    };
    if (first) {this.tasks.unshift(task); // 放到工作队列顶部
    } else {this.tasks.push(task); // 放到工作队列尾部
    }
  }
  eat(name) {const task = () => {console.log(`Eat ${name}`);
      this.next();};
    this.tasks.push(task);
    return this;
  }
}
function LazyMan(name) {return new _LazyMan(name);
}

深拷贝(思考到复制 Symbol 类型)

题目形容: 手写 new 操作符实现

实现代码如下:

function isObject(val) {return typeof val === "object" && val !== null;}

function deepClone(obj, hash = new WeakMap()) {if (!isObject(obj)) return obj;
  if (hash.has(obj)) {return hash.get(obj);
  }
  let target = Array.isArray(obj) ? [] : {};
  hash.set(obj, target);
  Reflect.ownKeys(obj).forEach((item) => {if (isObject(obj[item])) {target[item] = deepClone(obj[item], hash);
    } else {target[item] = obj[item];
    }
  });

  return target;
}

// var obj1 = {
// a:1,
// b:{a:2}
// };
// var obj2 = deepClone(obj1);
// console.log(obj1);

说一下购物车的逻辑?

//vue 中购物车逻辑的实现
1. 购物车信息用一个数组来存储,数组中保留对象,对象中有 id 和 count 属性

2. 在 vuex 中 state 中增加一个数据 cartList 用来保留这个数组

3. 因为商品详情页须要用到退出购物车性能,所以咱们须要提供一个 mutation, 用来将购物车信息退出 cartList 中

4. 退出购物车信息的时候,遵循如下规定:如果购物车中曾经有了该商品信息,则数量累加,如果没有该商品信息,则新增一个对象

5. 在商品详情页,点击退出购物车按钮的时候,调用 vuex 提供的 addToCart 这个 mutation 将以后的商品信息(id count)传给 addTocart  this.$store.commit("addToCart", {id:  , count:})

// js 中购物车逻辑的实现
1. 商品页点击“退出购物车”按钮,触发事件

2. 事件调用购物车“减少商品”的 Js 程序(函数、对象办法)3. 向 Js 程序传递传递“商品 id”、“商品数量”等数据

4. 存储“商品 id”、“商品数量”到浏览器的 localStorage 中

** 展现购物车中的商品 ******

1. 关上购物车页面

2. 从 localStorage 中取出“商品 Id”、“商品数量”等信息。3. 调用服务器端“取得商品详情”的接口失去购物车中的商品信息(参数为商品 Id)4. 将取得的商品信息显示在购物车页面。** 实现购物车中商品的购买 ******

1. 用户对购物车中的商品实现购买流程,产生购物订单

2. 革除 localStorage 中存储的曾经购买的商品信息

备注 1:购物车中商品存储的数据除了“商品 id”、“商品数量”之外,依据产品要求还能够有其余的信息,例如残缺的商品详情(这样就不必掉服务器接口取得详情了)、购物车商品的过期工夫,超过工夫的购物车商品在下次关上网站或者购物车页面时被革除。备注 2:购物车商品除了存储在 localStorage 中,依据产品的需要不同,也能够存储在 sessionStorage、cookie、session 中,或者间接向服务器接口发动申请存储在服务器上。何种状况应用哪种形式存储、有啥区别请本人剖析。

代码输入后果

setTimeout(function () {console.log(1);
}, 100);

new Promise(function (resolve) {console.log(2);
  resolve();
  console.log(3);
}).then(function () {console.log(4);
  new Promise((resove, reject) => {console.log(5);
    setTimeout(() =>  {console.log(6);
    }, 10);
  })
});
console.log(7);
console.log(8);

输入后果为:

2
3
7
8
4
5
6
1

代码执行过程如下:

  1. 首先遇到定时器,将其退出到宏工作队列;
  2. 遇到 Promise,首先执行外面的同步代码,打印出 2,遇到 resolve,将其退出到微工作队列,执行前面同步代码,打印出 3;
  3. 继续执行 script 中的代码,打印出 7 和 8,至此第一轮代码执行实现;
  4. 执行微工作队列中的代码,首先打印出 4,如遇到 Promise,执行其中的同步代码,打印出 5,遇到定时器,将其退出到宏工作队列中,此时宏工作队列中有两个定时器;
  5. 执行宏工作队列中的代码,这里咱们须要留神是的第一个定时器的工夫为 100ms,第二个定时器的工夫为 10ms,所以先执行第二个定时器,打印出 6;
  6. 此时微工作队列为空,继续执行宏工作队列,打印出 1。

做完这道题目,咱们就须要分外留神,每个定时器的工夫,并不是所有定时器的工夫都为 0 哦。

如何进攻 XSS 攻打?

能够看到 XSS 危害如此之大,那么在开发网站时就要做好进攻措施,具体措施如下:

  • 能够从浏览器的执行来进行预防,一种是应用纯前端的形式,不必服务器端拼接后返回(不应用服务端渲染)。另一种是对须要插入到 HTML 中的代码做好充沛的本义。对于 DOM 型的攻打,次要是前端脚本的不牢靠而造成的,对于数据获取渲染和字符串拼接的时候应该对可能呈现的恶意代码状况进行判断。
  • 应用 CSP,CSP 的实质是建设一个白名单,通知浏览器哪些内部资源能够加载和执行,从而避免恶意代码的注入攻打。
  1. CSP 指的是内容安全策略,它的实质是建设一个白名单,通知浏览器哪些内部资源能够加载和执行。咱们只须要配置规定,如何拦挡由浏览器本人来实现。
  2. 通常有两种形式来开启 CSP,一种是设置 HTTP 首部中的 Content-Security-Policy,一种是设置 meta 标签的形式
  • 对一些敏感信息进行爱护,比方 cookie 应用 http-only,使得脚本无奈获取。也能够应用验证码,防止脚本伪装成用户执行一些操作。

说一下 JSON.stringify 有什么毛病?

1. 如果 obj 外面有工夫对象,则 JSON.stringify 后再 JSON.parse 的后果,工夫将只是字符串的模式,而不是对象的模式
2. 如果 obj 里有 RegExp(正则表达式的缩写)、Error 对象,则序列化的后果将只失去空对象;3、如果 obj 里有函数,undefined,则序列化的后果会把函数或 undefined 失落;4、如果 obj 里有 NaN、Infinity 和 -Infinity,则序列化的后果会变成 null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果 obj 中的对象是有构造函数生成的,则应用 JSON.parse(JSON.stringify(obj))深拷贝后,会抛弃对象的 constructor;6、如果对象中存在循环援用的状况也无奈正确实现深拷贝;

说一下常见的检测数据类型的几种形式?

typeof  其中数组、对象、null 都会被判断为 Object,其余判断都正确

instanceof 只能判断援用数据类型, 不能判断根本数据类型

constructor 它有 2 个作用 一是判断数据的类型,二是对象实例通过 constructor 对象拜访它的构造函数。须要留神的事件是如果创立一个对象来扭转它的原型,constructor 就不能来判断数据类型了

Object.prototype.toString.call()
正文完
 0