JS相关面试题

手写代码

防抖
function debounce(time, fn) {
      let _time = null;

      return function () {

        clearTimeout(_time);

        _time = setTimeout(() => {

          fn.apply(this, arguments);

        }, time);

      };
 }
节流
   function throttling(fn, time) {
      let _time = null;
      return function () {
        if (_time) {
          return;  //这一步和防抖不一样
        }
        _time = setTimeout(() => {
          fn.apply(this, arguments);//apply改变this指向
          _time = null;
        }, time);
      };
    }
手写Promis
 <script>
      function getResult() {
        return new Promise((resove, reject) => {
          setTimeout(() => {
            resove("resove 成功");
          }, 2000);
        }).then(
          (res) => {
            console.log("success", res);
          },
          (err) => {
            console.log("error", err);
          }
        );
      }
      getResult();
</script>

闭包

答:闭包说的通俗一点就是可以在函数外部访问到函数内部的变量。因为正常情况下函数外部是访问不到函数内部作用域变量的,作用域分为了全局.函数级.块级作用域.

表象判断是不是闭包:函数嵌套函数,内部函数被return 内部函数调用外层函数的局部变量

优点:可以隔离作用域,不造成全局污染

缺点:由于闭包长期驻留内存,则长期这样会导致内存泄露

如何解决内存泄露:将暴露外部的闭包变量置为null

适用场景:我在性能优化的过程中,使用节流防抖函数就是闭包的原理,导航栏获取下标的使用

项目后期的时候进行了一定的性能优化.比方说:对级联菜单的功能,使用了节流防抖的方式,减少了http请求,

或者一个注册模块的时候,要实时(oninput)去后台验证用户名是否已注册给出相应的提示.

闭包的应用的时候,处理能够进行导航栏菜单获取当前下标.

巧妙的转嫁到节流防抖函数上.如果忘了节流防抖函数可以提一下lodash函数库.(自愿)

Tips:说道闭包可能就会问道垃圾回收机制

  通俗的说法就是js是一个弱类型语言.不用的就会被js引擎的垃圾回收机制(GC)自动回收掉,据我了解是用了一种**标记回收**的方式吧,就是从根节点上把能访问到的都做上标记.定期检测.没有标记的就被清除掉.释放掉内存.因为闭包的一直被引用着就一直有标记就回收不了了.可以置为null就可以,当然垃圾回收机制的打标记.js引擎也做了一定的优化.来提升性能.大体上我就了解这些[再有兴趣的可以参考https://segmentfault.com/a/1190000018605776]

垃圾回收机制有没有了解

  js引擎在进行垃圾标识的时候,为了提高性能,有一些处理方式,我记得好像能分代清除(哪些不常活跃,就不检查);另一个叫空闲时间回收 增量回收,我的理解就是分开,类似异步的检测来理解,是否放开

说一下JS中的原型链的理解

答:原型链是理解JS面向对象很重要的一点,这里主要涉及到两个点,一是_ proto ,二是prototype,举个例子吧,这样还好说点,例如:我用function创建一个Person类,然后用new Person创建一个对象的实例假如叫p1吧,在Person类的原型 prototype添加一个方法,例如:play方法,那对象实例p1如何查找到play这个方法呢,有一个查找过程,具体流程是这样的:

首先在p1对象实例上查找是否有play方法,如果有则调用执行,如果没有则用p1.proto(proto是一个指向的作用,指向上一层的原型)往创建p1的类的原型上查找,也就是说往Person.prototype上查找,如果在Person.prototype找到play方法则执行,否则继续往上查找,则用Person.prototye.proto继续往上查找,找到Object.prototype,如果Object.prototype有play方法则执行之,否则用Object.prototype.proto继续再往上查找,但Object.prototpye.proto上一级是null,也就是原型链的顶级,结束原型链的查找,这是我对原型链的理解

Tips附加分.你像我最近做的项目里面有些公共常用的方法.我放到了utils里面.然后在vue的main.js里面导入进来.然后通过Vue.prototype.方法名绑定到原型上,就利用了这种原型链的原理

说一下JS继承(含ES6的)

答:JS继承实现方式也很多,主要分ES5和ES6继承的实现

先说一下ES5是如何实现继承的

ES5实现继承主要是基于prototype来实现的,具体有三种方法

  一是**原型链继承**:即 B.prototype=new A()

  二是借**用构造函数继承(call或者apply的方式继承)**

    function B(name,age) {

      A.call(this,name,age)

    }

   三**是组合继承**

  组合继承是结合第一种和第二种方式

再说一下ES6是如何实现继承的

ES6继承是目前比较新的继承方式,用class定义类,用extends继承类,用super()表示父类,【下面代码部分只是熟悉,不用说课】

  ```JavaScript

例如:创建A类 class A { constructor() { //构造器代码,new时自动执行 } 方法1( ) { //A类的方法 } 方法2( ) { //A类的方法 } }

创建B类并继承A类 class B extends A { constructor() { super() //表示父类 } } 实例化B类: var b1=new B( ) b1.方法1( )

  Tips加分项,如何和项目关联? 说到小程序里面如何实现的请求

## 说一下JS原生事件如何绑定

  答:JS原生绑定事件我知道的有三种吧:

    一是html事件处理程序

    二是DOM0级事件处理程序

    三是DOM2级事件处理程序

    其中:html事件现在早已不用了,就是在html各种标签上直接添加事件,类似于css的行内样式,缺点是不好维护,因为散落在标签中,也就是耦合度太高

    例如:<button onclick=”事件处理函数”>点我</button>

    第二类是DOM0级事件:主要是先获取dom元素,然后直接给dom元素添加事件

    例如:var btn=document.getElementById(‘id元素’)

      btn.onclick=function() {

        //要处理的事件逻辑

      }

    DOM0事件如何移除呢?很简单:btn.onclick=null;置为空就行

    优点:兼容性好

    缺点:只支持冒泡,不支持捕获

    第三类是DOM2级事件,提供了专门的绑定和移除方法,不覆盖

    例如: var btn=document.getElementById(‘id元素’)

    //绑定事件

    btn.addEventListener(‘click’,绑定的事件处理函数名,false)

    //移除事件

    btn.removeEventListener(‘click’,要移除的事件处理函数名,false)

    优点:支持给个元素绑定多个相同事件,支持冒泡和捕获事件机制



## &nbsp;**说一下JS原生常用dom操作方法?**

  答:js原生dom操作方法有?

  查找:getElementByid,

        getElementsByTagName,

      querySelector,

      querySelectorAll

      previousSibling(上一个兄弟节点)和nextSibling(下一个兄弟)

  插入:appendChild,insertBefore

  删除:removeChild

  克隆:cloneNode

  设置和获取属性:setAttribute(“属性名”,”值”),getAttibute(“属性名”)

## **(了解)JS设计模式有哪些(单例模式观察者模式等)**

  JS设计模式有很多,但我知道的有单例模式,工厂模式.观察者模式单例模式:就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。

  ![image.png](https://flowus.cn/preview/9f248dfb-10a7-4fe2-b60a-ca08770d6b89)

  工厂模式:

    观察者模式: 观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。

    总的来说,观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响到另一边的变化

## **说一下你对JS面向对象的理解**

  答:JS面向对象主要基于function来实现的,通过function来模拟类,通过prototype来实现类方法的共享,跟其他语言有着本质的不同,自从有了ES6后,把面向对象类的实现更像后端语言的实现了,通过class来定义类,通过extends来继承父类,constructor代表构造方法,super代表父类的构造函数.其实ES6类的实现本质上是一个语法糖,不过对于开发简单了好多。

## 说一下JS数组常用方法(至少6个)

  答:在开发中,数组使用频率很频繁,JS数组常用方法有:push,pop,unshift,shift,**splice**,join,concat,forEach,sort,reduce,**filter,map**,**some,every,includes,find,findIndex** 好多,不过都是平时开发中很常用的方法,大家可以补充一点儿es6的

## 说一下JS数组内置遍历方法有哪些和区别

  答:JS数组内置遍历(遍历就是循环的意思)方法主要有:

  **forEach:**这个方法是为了取代for循环遍历数组的,返回值为undefined例如:

JavaScript let arrInfo=[4,6,6,8,5,7,87] arrInfo.forEach((item,index,arr)=>{ //遍历逻辑 }) 其中: item代码遍历的每一项, index:代表遍历的每项的索引, arr代表数组本身 filter:是一个过滤遍历的方法,如果返回条件为true,则返回满足条件为true的新数组

let arrInfo=[4,16,6,8,45,7,87] let resultArr=arrInfo.filter((item,index,arr)=>{ //例如返回数组每项值大于9的数组 return item>9 }) map : 这个map方法主要对数组的复杂逻辑处理时用的多,有返回值.特别是react中遍历数据,也经常用到,写法和forEach类似 some : 这个some方法用于只要数组中至少存在一个满足条件的结果,返回值就为true,否则返回false, 写法和forEach类似 every : 这个every方法用于数组中每一项都得满足条件时,才返回true,否则返回false, 写法和forEach类似

数组 的reduce方法 // 这是一个做数组每一项值的累加的方法吧.就是可以把前面的计算结果与后面的每一项,第一个参数是函数 (函数的第一个参数是每次累加的和,第二个参数是数组下一个元素,然后是下标 ) 第二个参数初始值 array.reduce(function(prev, currentValue, currentIndex, arr), initValue)

  ![image.png](https://flowus.cn/preview/2b6405b0-8c4c-452b-90c1-44630418e28c)



## 说一下JS作用域和作用域链

  答:JS作用域也就是JS识别变量的范围,作用域链也就是JS查找变量的顺序

    先说作用域,JS作用域主要包括全局作用域、局部作用域和ES6的块级作用域

      全局作用域:也就是定义在window下的变量范围,在任何地方都可以访问,

      局部作用域:是只在函数内部定义的变量范围

      块级作用域:简单来说用let和const在任意的代码块中定义的变量都认为是块级作用域中的变量,例如在for循环中用let定义的变量,在if语句中用let定义的变量等等出了这个{}块就没用了

  注:尽量不要使用全局变量,因为容易导致全局的污染,命名冲突,对bug查找不利。

  而所谓的作用域链就是由最内部的作用域往最外部,查找变量的过程.形成的链条就是作用域链,重名就近原则

  作用域链,从找到的地方外外层,一直找到window

  原型链往外层找,最终找到null



## 说一下从输入URL到页面加载完中间发生了什么?

  输入了一个域名,要先去检查本地的hosts文件,看看是否有域名映射关系,如果没有的话,要通过DNS解析找到这个域名对应的服务器地址(ip),通过TCP请求链接服务,通过WEB服务器(nginx)返回资源,浏览器根据返回数据构建DOM树,再把css形成一个样式表.这两个东西结合,变成了render树.render树上,绘制节点位置坐标.页面上通过重绘和回流的过程,渲染出页面来.

## 说一下JS事件代理(也称事件委托)及实现原理

  **jq里面实现方式**: $(“ul”).on(‘click’,’li’,function(){})

  好处:减少绑定太多事件.能够给动态添加的元素绑定事件

  答:JS事件代理就是通过给父级元素(例如:ul)绑定事件,不给子级元素(例如:li)绑定事件,然后当点击子级元素时,通过事件冒泡机制在其绑定的父元素上触发事件处理函数,主要目的是为了提升性能,因为我不用给每个子级元素绑定事件,只给父级元素绑定一次就好了,在**原生js里面是通过event对象的targe属性实现**

JavaScript var ul = document.querySelector("ul"); ul.onclick = function(e){//e指event,事件对象 var target = e.target||e.srcElement;//target获取触发事件的目标源(li) if(target.nodeName.toLowerCase() == 'li'){ //目标(li)节点名转小写字母,不转的话是大写字母,执行逻辑 alert(target.innerHTML) } } ```

jq方式实现相对而言简单 $(“ul”).on(“click”,“li”,function(){//事件逻辑}) 其中第二个参数指的是触发事件的具体目标,好处:特别是给动态添加的元素绑定事件,这个特别起作用

说一下JS数据类型有哪些?

答:JS数据类型有:

基本数据类型:number,string,Boolean,null,undefined,symbol(ES6新增)

复杂类型/引用类型:Object,Array,function

如何检测数据类型?

基本类型typeof检测

引用类型 arr instanceof Array 因死谈斯奥父

通用检测方式 Object.prototype.toString.call (0) //'[object Number]'

另外数组的检测Array.isArray(arr1) // true

Typeof(null) // ‘object’

Typeof(NaN) // ‘number’

typeof([1]) // ‘object’

说一下call,apply,bind区别

答:call,apply,bind主要作用都是改变this指向的,(箭头函数不能被改变this指向) 但使用上略有区别,说一下区别:

call和apply的主要区别是在传递参数上不同,call后面传递的参数是以逗号的形式分开的,apply传递的参数是数组形式 [Apply是以A开头的,所以应该是跟Array(数组)形式的参数]

bind返回的是一个函数形式,如果要执行,则后面要再加一个小括号 例如:bind(obj,参数1,参数2,)(),bind只能以逗号分隔形式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值