美文网首页
面试整理-Javascript(上)

面试整理-Javascript(上)

作者: 前端艾希 | 来源:发表于2020-05-16 23:31 被阅读0次

    1. 你还有什么要问我的?

    • 请问咱们公司为什么要招人?
    • 如果我加入了公司,我的工作内容是什么?
    • 如果我加入了公司,您对我的期望是什么?
    • 请问咱们公司的企业文化是什么样的?

    2. 面试题

    2.1 为Object提供iterator接口

    let a = {
        name: 'bing',
        age: 25
    }
    
    let gen = function * iterator () {
        let keys = Object.keys(this)
        let i = 0
        while (i < keys.length) {
            yield this[keys[i]]
            i += 1
        }
        return
    }
    
    a[Symbol.iterator] = gen
    
    // 测试
    for (let val of a) {
        console.log(val)
    }
    // bing
    // 25
    

    2.2 ES 6 以后的新特性

    2.2.1 ES 7

    • 指数函数的中缀表达式,**表示Math.pow()
    • Array.prototype.includes,快速查找数组中是否包含某个元素,包括NaN

    2.2.2 ES 8

    • Object.values(obj)/Object.entries(obj),Object上的静态方法,遍历出对象的value/{key: value}
    • String.prototype.padStart(str.length, '填充字符串'),实例方法,从字符串前面开始填充传入的第二个参数,直至长度变成传入的第一个参数。
    • String.prototype.padEnd(str.length, '填充字符串'),字符串追加填充,参数同上
    • Object.getOwnPropertyDescriptors(obj),获取对象的所有自有的属性的属性描述对象。
    • 结尾允许逗号

    2.2.3 ES 9

    • Promise.prototype.finally(callback),Promise无论如何都会执行finally的回调。
    • rest支持Object,例如:{...obj}

    2.2.4 ES 10

    • 可选的catch变量,即,catch(e){}如果不需要变量e可简写为catch {}
    • JSON超集
    • Symbol的description属性,为Symbol添加description属性,能够直接访问description
    • 修订Function.prototype.toString,函数实例的toString方法现在反悔精确字符,包括空格和注释。
    • Object.fromEntires(),可以把键值对类型的数据结构转为一个对象。
    • String.trimStart/trimEnd(),去除字符串前或后的空格
    • Array.prototype.flat(times),将数组拉平,times是层数,默认是一层。
    • Array.prototype.flatMap(),先对数组执行了map方法,然后再执行拉平

    2.2.5 ES 11

    • import()函数,根据传参动态懒加载
    • Promise.allSettled(),和Promise.all一样使用,不过不具有一票否决制了,可以拿到里面素有promise的结果。

    2.3 函数

    2.3.1 匿名函数如何调用自身实现递归?

    因为函数内的arguments.callee属性指向函数自身,所以可以通过arguments.callee()来调用自身。

    2.3.2 箭头函数有什么特点?

    1. 箭头函数内不暴露arguments对象;2. 箭头函数没有自己的super或者new.target

    2.3.3 什么是高阶函数?

    如果某个函数可以接受另一个函数作为参数,该函数就称之为高阶函数。最常见的就是回调函数作为参数传递。

    2.3.4 什么是函数的重载?

    重载就是函数名一样,但是随着传入的参数个数不一样,调用的逻辑或者返回的结果不一样。

    2.4 作用域

    2.4.1 什么是作用域?

    作用域即函数或者变量的可见区域,在除该区域外的任何区域无法被访问。

    2.4.2 ES 6以后作用域分为哪几种?

    全局作用域和局部作用域(函数作用域,块级作用域)

    2.4.3 在块级作用域中声明函数需要注意什么?

    为了保持向下兼容,在{}中声明的函数仍然能够在外部被访问到,如果想要达到预期效果,我们应当使用let来定义一个变量来接收匿名函数,这样定义的函数在代码块外部就不能被访问了。

    2.4.4 为什么要用let而不用var

    1. 因为变量提升会让人难以发现错误;
    2. 因为var可以重复声明变量,导致后面的覆盖前面的;
    3. var会造成变量污染

    所以除了定义全局变量外。尽量避免使用var

    2.5 执行上下文

    执行上下文就是当前JavaScript代码被解析和执行时所在的环境,故也称之为执行环境。

    2.5.1 执行上下文分为哪几种?

    • 全局执行上下文,不在任何函数中的代码都位于全局执行上下文中。它做了两件事:1. 创建了一个全局对象,在浏览器中这个全局对象就是window,2. 将this指针指向这个全局对象,一个程序中只能存在一个全局执行上下文。
    • 函数执行上下文,每次调用函数时,都会为该函数创建一个新的执行上下文,每个函数都拥有自己的执行上下文,但是只有在函数被调用的时候才会被创建。
    • eval执行上下文。

    2.5.2 执行上下文的生命周期

    创建阶段 → 执行阶段 → 回收阶段

    2.5.3 请简述执行上下文的创建阶段都做了什么?

    这里只说函数执行上下文。

    1. 创建变量对象:首先初始化函数的参数arguments,提升函数声明和变量声明。
    2. 创建作用域链:作用于链是在变量对象之后创建的。确定outer的指向。
    3. 确定this的指向。this指向调用该函数的对象。
    4. 压入执行栈。

    2.6 this相关

    2.6.1 为什么要有this?

    JavaScript允许在函数体内部引用当前执行上下文的其他变量,所以JavaScript需要一种机制可以依靠其优雅,准确的指向当代码运行时的上下文环境。

    2.6.2 new操作符原理

    1. 创造一个空对象
    2. 让空对象的原型指向构造函数的原型
    3. 将构造函数的this指向空对象然后执行构造函数
    4. 判断构造函数是否返回非空对象,如果是则返回构造函数的返回内容,否则就返回新生成的对象。
    function New(constructor, ...args){
        const _obj = {}
        _obj.__proto__ = constructor.prototype
        _obj.constructor = constructor
        const retu = constructor.call(_obj, ...args)
        return typeof retu === 'object' && retu !== null ? retu : _obj
    }
    

    2.6.3 为什么要使用call,apply,bind?

    这三者的核心概念是借用,即借助已实现的方法,改变方法中this的指向减少重复代码,节省内存。

    2.6.4 bind的应用场景

    1. 保存函数的参数,在声明函数的时候就可以绑定参数。
    2. 保存this,声明函数时可以绑定this,当然也可以用箭头函数

    2.6.5 实现call

    思路:

    1. 参考call的语法规则,需要设置一个参数thisArg,也就是this的指向;
    2. 将thisArg封装为一个Object;
    3. 通过为thisArg创建一个临时方法,这样thisArg就是调用该临时方法的对象了,会将该临时方法的this隐式指向到thisArg上
    4. 执行thisArg的临时方法,并传递参数;
      删除临时方法,返回方法的执行结果。
    /**
     * 用原生JavaScript实现call
     */
    Function.prototype.myCall = function(thisArg, ...arr) {
    
      //1.判断参数合法性/////////////////////////
      if (thisArg === null || thisArg === undefined) {
        //指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
        thisArg = window;
      } else {
        thisArg = Object(thisArg);//创建一个可包含数字/字符串/布尔值的对象,
                                  //thisArg 会指向一个包含该原始值的对象。
      }
    
      //2.搞定this的指向/////////////////////////
      const specialMethod = Symbol("anything"); //创建一个不重复的常量
      //如果调用myCall的函数名是func,也即以func.myCall()形式调用;
      //根据上篇文章介绍,则myCall函数体内的this指向func
      thisArg[specialMethod] = this; //给thisArg对象建一个临时属性来储存this(也即func函数)
      //进一步地func作为thisArg对象的一个方法被调用,那么func中的this便
      //指向thisArg对象。由此,巧妙地完成将this隐式地指向到thisArg!
      let result = thisArg[specialMethod](...arr);
      
      //3.收尾
      delete thisArg[specialMethod]; //删除临时方法
      return result; //返回临时方法的执行结果
    };
    
    let obj = {
      name: "coffe1891"
    };
    
    function func() {
      console.log(this.name);
    }
    
    func.myCall(obj);//>> coffe1891
    

    2.6.6 实现一个apply

    /**
     * 用原生JavaScript实现apply
     */
    Function.prototype.myApply = function(thisArg) {
      if (thisArg === null || thisArg === undefined) {
        thisArg = window;
      } else {
        thisArg = Object(thisArg);
      }
    
      //判断是否为【类数组对象】
      function isArrayLike(o) {
        if (
          o && // o不是null、undefined等
          typeof o === "object" && // o是对象
          isFinite(o.length) && // o.length是有限数值
          o.length >= 0 && // o.length为非负值
          o.length === Math.floor(o.length) && // o.length是整数
          o.length < 4294967296
        )
          // o.length < 2^32
          return true;
        else return false;
      }
    
      const specialMethod = Symbol("anything");
      thisArg[specialMethod] = this;
    
      let args = arguments[1]; // 获取参数数组
      let result;
    
      // 处理传进来的第二个参数
      if (args) {
        // 是否传递第二个参数
        if (!Array.isArray(args) && !isArrayLike(args)) {
          throw new TypeError(
            "第二个参数既不为数组,也不为类数组对象。抛出错误"
          );
        } else {
          args = Array.from(args); // 转为数组
          result = thisArg[specialMethod](...args); // 执行函数并展开数组,传递函数参数
        }
      } else {
        result = thisArg[specialMethod]();
      }
    
      delete thisArg[specialMethod];
      return result; // 返回函数执行结果
    };
    

    2.6.7 实现bind

    /**
     * 用原生JavaScript实现bind
     */
    Function.prototype.myBind = function(objThis, ...params) {
      const thisFn = this;//存储调用函数,以及上方的params(函数参数)
      //对返回的函数 secondParams 二次传参
      let funcForBind = function(...secondParams) {
        //检查this是否是funcForBind的实例?也就是检查funcForBind是否通过new调用
        const isNew = this instanceof funcForBind;
    
        //new调用就绑定到this上,否则就绑定到传入的objThis上
        const thisArg = isNew ? this : Object(objThis);
    
        //用call执行调用函数,绑定this的指向,并传递参数。返回执行结果
        return thisFn.call(thisArg, ...params, ...secondParams);
      };
    
      //复制调用函数的prototype给funcForBind
      funcForBind.prototype = Object.create(thisFn.prototype);
      return funcForBind;//返回拷贝的函数
    };
    

    参考链接

    前端内参

    相关文章

      网友评论

          本文标题:面试整理-Javascript(上)

          本文链接:https://www.haomeiwen.com/subject/jgidohtx.html