JS面试题(2)

117 阅读3分钟

1. 手写instanceof

出题目的

  • 考察原型链的理解

知识点

instanceof

  • instanceof运算符用于检测结构函数的prototype属性是否出现在某个实例对象的原型链上
  • 可以判断继承关系,只要是在同一条原型链上,就可以返回true

演示代码

   class Person {
        constructor(name){
            this.name = name;
        }
    }
    class Student extends Person {
        constructor(name,age){
            super(name);
            this.age =age;
        }
    }
    const zhangsan = new Student;
    console.log(myInstance(zhangsan,Student));
    console.log(myInstance(zhangsan,Person));
    console.log(myInstance(zhangsan,Object));
    console.log(myInstance(zhangsan,Array));

    function myInstance(obj1,obj2){
        let objPrototype = obj1.__proto__
        while(true){
            if(objPrototype == null){
                return false;
            }
            if(objPrototype === obj2.prototype) {
                return true;
            }
            objPrototype = objPrototype.__proto__
        }
    }

2. this不同场景,如何取值?

this易混场景

  • 普通函数下的this
  • call apply bind的this
  • 定时器中的this
  • 箭头函数中的this

演示代码

      //1.普通函数下的this
        // 严格模式:this——>undefined
        "use strict"
        // 普通模式:this——>window
        function a(){
            console.log(this);
        }
        a()
        //2.call apply bind的this
        function a(){
            console.log(this);
        }
        // 情况1:什么都不传,this——>window
        // call
        // a.call()
        // a.call(undefined)
        // a.call(null)

        // 情况2:传什么,this就是什么
        // apply
        // a.apply(123)
        // a.apply(undefined)
        // a.apply(null)
        
        // bind:bind需要一个函数接收
        const fn = a.bind({x:1902})
        fn()
        /* 
            3.定时器中的this
            情况一:定时器+funtion  this——>window
            情况二:定时器+箭头函数  this——>上层作用域的this
        */
        //情况一:this——>window 
        /*例一
         setTimeout(function () {
            console.log(this);
        }, 100);
        */

        /*例二
         function fn() {
            setTimeout(function () {
                console.log(this);
            }, 100);
        }
        fn.call({x:100});
        */

        /*例三
         const a = {
            fn(){
                setTimeout(function(){
                    console.log(this);
                },100);
            }
        }
         a.fn();
        */
        // 情况二
        /* 例一
        class Obj{
            fn(){
                setTimeout(() => {
                    console.log(this);
                }, 100);
            }
        }
        const o = new Obj()
        o.fn()
        */

        /*例二
        function fn(){
            setTimeout(() => {
                console.log(this);
            }, 100);
        }
        fn.call({x:100})
        */
HTML部分
        <button id="btn">
            test
        </button>
JS部分
        /*
        4.箭头函数中的 this
        情况1:有function作用域的,this是上层作用域的this
        情况2:没有function作用域的,this是window
        */

        /*
        class Obj{
            say=()=>{
                console.log(this); // this——> Obj{say:f}
            }
        }
        const obj1 = new Obj;
        obj1.say();

        const obj2 ={ 
            say:()=>{
                console.log(this);// this ——>window
            }
        }
        obj2.say()
        */

        const oBtn = document.getElementById("btn");
        // oBtn.onclick = function(){
        //     console.log(this);
        // }
        oBtn.onclick = ()=>{
            console.log(this);
        }

3. 手写bind函数

知识点

  • Function.prototype.myBind
  • Array.prototype.clice.call()
  • array.shift()

演示代码

       function fn(a, b, c, d) {
            console.log(this);
            console.log(a, b, c, d);
            return "this is return"
        }

        // bind会返回一个函数要用一个变量接收bind返回到函数
        // const dp = fn.bind({x:23},1,2,3,4);
        // console.log(dp()) ;

        //手写bind
        Function.prototype.myBind = function () {
            const fn = this;
            // arguments是一个类数组:[{ x: 10 }, 1, 2, 3]
            // const _this =  arguments[0];

            // 将arguments变成数组,用一个变量接收数组[1, 2, 3, 5, 6]
            const arr = Array.prototype.slice.call(arguments);
            // 用shift方法获取[{ x: 140 },1, 2, 3, 5, 6]中的{ x: 140 }
            const _this =  arr.shift();
            return function(){
                return fn.apply(_this, arr)
            }
        }
        const dp = fn.myBind({ x: 23 }, 1, 2, 3, 4);
        console.log(dp());

4. 谈谈闭包和闭包使用场景

什么是闭包

概念:闭包是作用域的一种特殊应用

触发闭包的情况

  1. 函数当作返回值被返回
  2. 函数当作参数被传递
  3. 自执行匿名函数

必报的应用

  1. 隐藏变量
  2. 解决for in的问题

作用域

全局作用域、局部作用域

自由变量

不在自己作用域里的变量,就是自由变量 自由变量的值:在函数定义的地方向上层作用域查找。与函数调用位置无关

演示代码

   //情况一:函数当作返回值被返回
       // function fn(){
       //     const a = 1;
       //     return function(){
       //         console.log(a); // 1
       //     }
       // }
       // const a = 5;
       // const cb = fn();
       // cb()

       // 情况二:函数当作参数传覅
       function fn(cb){
           const a = 100
           cb()
       }
       const a = 500;
       fn(function(){
           console.log(a); // 500
       });

       //情况三:自执行匿名函数
       (function(index){
           console.log(index); // 10
       })(10)
HTML部分
        <button>0</button>
        <button>1</button>
        <button>2</button>
        <button>3</button>
        <button>4</button>

JS部分
        const btn = document.querySelectorAll("button");
        // 方法一
        // for(var i = 0;i<btn.length;i++){
        //     (function(index){
        //         btn[i].onclick = function(){
        //             console.log(index);
        //         }
        //     })(i)
        // }

        // 方法二
        for(let i = 0;i<btn.length;i++){
            btn[i].onclick = function(){
                console.log(i);
            }
        }
//隐藏变量
        function fn(){
            const data = {};

            return {
                set : function(key,val){
                    data[key] = val;
                },
                get : function(val){
                    return data[val]
                },
            };
        }
        const json = fn();
        json.set("age",18);
        console.log(json.get("age"));

5. 请描述event loop的机制

JavaScript是如何执行的?

  • 自上而下,从左到右一行一行执行
  • 如果有一行报错,后面的代码不执行
  • 先执行同步代码,再执行异步代码(setTimeout、Ajax)

Event Loop过程

  1. 同步代码,一行一行放在Call Stack中执行
  2. 遇到异步,会先“记录”下代码,等待执行时机(setTimeout、Ajax)。时机到了,将之前“记录”的代码放入Callback Queue
  3. 当Call Stack为空(同步代码执行完),EventLoop开始工作
  4. EventLoop轮询查找Callback Queue中是否可执行的代码。如果有,将代码移动到Call Stack中执行
  5. Event Loop如果没有找到可以执行的代码,则会继续轮询查找

Event Loop.png

演示代码

        console.log("start");

        setTimeout(()=>{
            console.log("setTimeout");
        },2000);
        console.log("end");
        //输出结果:start,emd,setTimeout