前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >前端面试题锦集:第一期

前端面试题锦集:第一期

作者头像
terrence386
发布2022-07-15 09:47:59
2770
发布2022-07-15 09:47:59
举报

封面图

面试只是起点,能力才是终局

声明变量 let, const ,var

var 声明变量存在变量提升的问题,容易造成全局变量污染。

let 声明变量只在当前作用域内有效,存在暂时性死区机制。

在变量未声明前对变量进行操作会报错,就是因为存在暂时性死区。只有在变量声明后才可对变量进行操作。

const 使用方法和let基本一致。但是const 保存当前变量的引用。意味着const声明一个Object类型的变量,依然可以修改该变量的属性

new 操作符执行的过程

使用new操作符会执行以下过程:

  1. 在内存中创建一个新对象。
  2. 这个新对象内部的[[prototype]]被赋值为构造函数的prototype属性。
  3. 构造函数内部的this被赋值给这个新对象。
  4. 执行构造函数内部的代码,给新对象添加属性。
  5. 如果构造函数返回非空对象,则返回该对象;否则返回创建的新对象。

Class类构造函数 和 构造函数的 区别

  1. 调用class类构造函数必须使用new操作符。
  2. 普通构造函数如果不使用new操作符,就以全局对象作为内部对象。

对象,函数,原型对象 ,prototype ,proto 和 [[Prototype]]的关系

这几个名词的关系比较容易混淆。

基本关系是:

每个函数本质上是一个对象。每个函数都有一个prototype属性和__proto__,prototype属性的指向是原型对象。[[Prototype]]可以理解为浏览器内部实现的一个原型对象的原型。可以使用__proto__属性访问到[[Prototype]]

this, call , apply , bind

this 的指向大致分为4种:

  • 作为对象的方法调用

当函数作为对象的方法被调用时,this 指向该对象

  • 作为普通函数调用

当函数不作为对象的属性被调用时,也就是我们常说的普通函数方式,此时的 this 总是指 向全局对象。

  • 构造器调用

当用 new 运算符调用函数时,该函数总会返回一个对象,通常情况下,构造器里的 this 就指向返回的这个对象。

  • Function.prototype.call 或 Function.prototype.apply 调用

跟普通的函数调用相比,用 Function.prototype.call 或 Function.prototype.apply 可以动态地改变传入函数的 this。

call 和 apply其实是Function的原型上的两个方法。也就是Function.prototype.call 或 Function.prototype.apply。

bind被用来修改函数内部的this指向,大部分浏览器已经内置了Function.prototype.bind。当然我们也可以手动实现一个bind

代码语言:javascript
复制
Fucntion.prototype.bind = function(ctx){
  let self = this;
  return function(){
    return self.apply(ctx,arguments)
  }
}

有几个地方经常用到call或者apply:

  1. 类型判断
代码语言:javascript
复制
const isType(type) => { 
  return Object.prototype.toString.call(obj) === `[object ${type}]`
}
  1. 类数组对象转为数组
代码语言:javascript
复制
Array.prototype.slice.call(arrLike)
  1. 求数组中的最值
代码语言:javascript
复制
function smallest(array){ 
  return Math.min.apply( Math, array ); 
} 
function largest(array){ 
  return Math.max.apply( Math, array ); 
} 

function smallest(){ 
  return Math.min.apply( Math, arguments ); 
} 
function largest(){ 
  return Math.max.apply( Math, arguments ); 
} 

高阶函数

高阶函数是指将函数作为参数或者返回值的函数。将函数作为参数可以将变化的部分封装起来,隔离代码中变化和不变的部分。

判断数据类型:

代码语言:javascript
复制
const isType = (type) => {
  return (obj) => { Object.prototype.toString().call(obj) === `[object ${type} ]`}
}

节流函数

节流函数是限制函数调用频率的技术。比如 resize,mousemove,scroll事件。节流函数的实现原理是:将被执行函数用定时器延时一段时间后执行。如果本次没有执行完,则忽略调用函数的请求。

代码语言:javascript
复制
  let throttle = function (fn,interval) {

    let timer = null
    let self = this

    return function (){
      let args = arguments;
      let firstTime = true;

      if(firstTime){
        fn.apply(self,args)
        return firstTime = false
      }
      if(timer){
        return 
      }

      timmer = setTimeOut(()=>{
        clearTimeout(timer)
        fn.call(self,args)
      },interval || 5000)

    }

  }

防抖函数

防抖这个技术点允许我们将多个相似的调用分成一组,或者可以理解为多个相同的事件最后只执行一次

代码语言:javascript
复制
let debounce = function(fn){
  let timer = null
  return function(){
    timer = setTimeout(()=>{
      fn.call(this,argumnets)
    },500)
  }

}

防抖的应用场景:按钮重复点击search输入框鼠标事件

节流和防抖的区别在于:一个是定时执行,一个是只执行一次

class类中的constructor 和 super

  1. constructor 是类的构造函数,这个是非必需的。不定义构造函数相当于构造函数为空。
  2. 派生类的方法可以通过super关键字引用他们的原型。
  3. 在类构造函数中可以使用super调用父类的构造函数。
  4. 从这些方面来看,我们在定义React组件的时候,props实际上是构造函数的参数。

React组件中的props是什么

我们在定义React组件的时候,props实际上是构造函数的参数。

浅复制 和 深复制

浅复制只复制对象的引用地址,并不复制对象本身,新旧对象共用一个块儿内存。深拷贝会创建一个一模一样的对象,且新旧对象并不共享内存,修改新对象不会修改元对象。

浅拷贝方法:Object.assign(),Array.prorotype.concat(),Array.prototype.slice()

深拷贝方法:JSON.parse(),lodash._cloneDeep()

浏览器的事件循环模型

对象被分配在对内存中;基本类型的数据存放在栈内存;

一个Javascript运行时包含了一个待处理消息的消息队列。每个消息队列都关联着这个消息的回调函数。

在事件循环的某个时刻,运行时会从最先进入队列的消息开始处理队列中的消息。被处理的消息会移除队列,并作为输入参数来调用与之关联的函数。函数的处理会一致进行到执行栈为空为止;然后事件循环就会处理队列中的下一个消息。

异步非阻塞

异步非阻塞,我们一直在说异步非阻塞这个词。到底什么是异步非阻塞?

我觉得可以这样理解:

异步的目的是为了非阻塞。也就是不阻塞。

setTimeout, setInterval为什么是异步的?

这个问题很难从底层原理解释清楚,但是我们可以通过下面的代码了解到这个特点:

代码语言:javascript
复制
console.log(1)
setTimeout(()=>{console.log('set-time-out')},3000)
console.log(2)

结果如下:

打印出1 之后,并没有执行setTimeout, 而是等到2打印出来之后,才打印set-time-out。

原理就是上面提到的浏览器的事件循环机制,简单点说就是当执行栈里的执行的代码遇到异步的代码时,会将它添加到消息队列中,等到执行栈里的代码执行完成后,会处理消息队列中的代码,依次循环往复。

任务 微任务 宏任务

(macro)task,宏任务可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。

浏览器为了能够使得JS内部(macro)task与DOM任务能够有序的执行,会在一个(macro)task执行结束后,在下一个(macro)task 执行开始前,对页面进行重新渲染,流程如下:

(macro)task->渲染->(macro)task->...

宏任务包含:

  • script(整体代码)
  • setTimeout
  • setInterval
  • I/O
  • UI交互事件
  • postMessage
  • MessageChannel
  • setImmediate(Node.js 环境)

microtask,微任务可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前task任务后,下一个task之前,在渲染之前。

所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染。也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)。

微任务包含:

  • Promise.then
  • Object.observe
  • MutaionObserver
  • process.nextTick(Node.js 环境)

Promise基本原理

Promise的实现本质上是将回调函数封装在内部,其实现方式类似于发布订阅模式,then的时候将回调push到缓存数组中,resolve的时候遍历缓存数组执行回调函数。

依据Promise A+ 规范。三种状态,thenable, 执行器。逐渐完善

代码语言:javascript
复制
//极简的实现
class Promise {
    callbacks = [];
    constructor(fn) {
        fn(this._resolve.bind(this));
    }
    then(onFulfilled) {
        this.callbacks.push(onFulfilled);
    }
    _resolve(value) {
        this.callbacks.forEach(fn => fn(value));
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JavaScript高级程序设计 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 封面图
  • 声明变量 let, const ,var
  • new 操作符执行的过程
  • Class类构造函数 和 构造函数的 区别
  • 对象,函数,原型对象 ,prototype ,proto 和 [[Prototype]]的关系
  • this, call , apply , bind
  • 高阶函数
  • 节流函数
  • 防抖函数
  • class类中的constructor 和 super
  • React组件中的props是什么
  • 浅复制 和 深复制
  • 浏览器的事件循环模型
  • 异步非阻塞
  • setTimeout, setInterval为什么是异步的?
  • 任务 微任务 宏任务
  • Promise基本原理
相关产品与服务
消息队列 CMQ 版
消息队列 CMQ 版(TDMQ for CMQ,简称 TDMQ CMQ 版)是一款分布式高可用的消息队列服务,它能够提供可靠的,基于消息的异步通信机制,能够将分布式部署的不同应用(或同一应用的不同组件)中的信息传递,存储在可靠有效的 CMQ 队列中,防止消息丢失。TDMQ CMQ 版支持多进程同时读写,收发互不干扰,无需各应用或组件始终处于运行状态。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档