js必会面试题

181 阅读5分钟

2-1值类型与引用类型有什么区别?

哪些类型是值类型?哪些类型是引用类型?

  • 值类型:String、Number、Boolean、Symbol
  • 引用类型:JSON、Array、null

演示值类型与引用类型的堆栈模型

演示值类型与引用类型的堆栈模型.png

2-2手写深拷贝

如何实现深拷贝

  • JSON.parse(JSON.stringify(obj))
    不能存放函数、时间对象、正则...
  • 递归
    没有考虑循环引用。
  • 演示代码
<script>
        let obj1 = {
            name:"张三",
            age:"18",
            address: {
                city:"北京",
            },
            hobby: ["台球","篮球"]
        }

        const obj2 = deepClone(obj1);
        console.log(obj1);
        obj2.age = 20;
        obj2.address.city = "上海"
        console.log(obj2);



        function deepClone(obj) {
            if(typeof obj !=="object" || obj == null){
                return obj
            }

            let res = obj instanceof Array ? [] : {};

            for(key in obj){
                if(obj.hasOwnProperty(key)){
                    res[key] = deepClone(obj[key]);
                }
            }

            return res
        }
    </script>

2-3何时使用== 何时使用===

==隐式转换

    100 == '100'
    0 == ''
    0 == false
    false == ''
    null == undefined

只有 obj == null 使用双等

    if(obj === null || obj === undefined) {
        
    }

演示代码

只有在判断一个对象是否等于null的时候用==,除此之外情况我们都是使用===的。

<script>
        console.log(100 == '100');
        console.log(0 == '');
        console.log(0 == fasle);
        console.log('' == false);
        console.log(null == undefined);

        let obj = { age: 18 };
        if (obj == null) {

        }
        if (obj == null || obj ===undefined) {
            
        }
    </script>

2-4哪些是truly变量?哪些是flasely变量?

什么是truly变量?什么是falsely变量

  • truly变量:!!val === true
  • falsely变量:!!val === false

除了falsely变量,都是truly变量

  • 数字0
  • NaN
  • 空字符串
  • null
  • undefined

2-5说说原型和原型链是怎么回事?

复习class基础语法

  • class
  • construct
  • extends
  • super
  • instanceof

什么是原型

原型.png

什么是原型链

原型链.png

手写instanceof

出题目的

  • 考察原型链的理解

知识点

instanceof

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

参考答案

<script>
        class Person {
            constructor(name) {
                this.name = name;
            }
        }

        class Student extends Person {
            constructor(name,age) {
                super(name);

                this.age = age;
            }
        }
        const zhangsan = new Student('张三',18);

        console.log(myInstanceof(zhangsan,Student));
        console.log(myInstanceof(zhangsan,Person));
        function myInstanceof(obj1,obj2) {
            let obj1Proto = obj1.__proto__;
            while (true){
                if (obj1Proto === null) {
                    return false
                }
                if (obj1Proto === obj2.prototype) {
                    return true;
                }
                obj1Proto = obj1Proto.__proto__
            }
        }
    </script>

this不同场景,如何取值?

this 易混场景

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

测试题

    <button id="btn">test</button>
    <script>
        // 普通函数中的this
        // 非严格模式 this -> window
        // 严格模式下 this -> undefined 
        // 'use strict'//开启严格模式
        // function a() {
        //     console.log(this);
        // }

        // a();

        // call apply bind 中的this
        // 情况1:this -> window
        //     a.call()
        //     a.call(undefined)
        //     a.call(null)
        
        // 情况2:传什么,this就是什么

        // function a() {
        //     console.log(this);
        // }

        // a.call();
        // a.apply({x : 101});
        // a.bind({x : 101})()

        // 定时器中的this
        // 情况1:定时器+function this->window
        // 情况2:定时器+箭头函数 this->上层作用域的this

        // 情况1
        // setTimeout(function () {
        //     console.log(this);
        // },100);

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

        // const a={
        //     fn() {
        //         setTimeout(function () {
        //             console.log((this));
        //         })
        //     }
        // }
        // a.fn()

        // 情况2
        // class Obj{
        //     fn() {
        //         setTimeout(() => {
        //             console.log((this));
        //         },100)
        //     }
        // }

        // const o = new Obj();
        // o.fn()

        // function fn() {
        //     setTimeout(() => {
        //         console.log(this);
        //     },100)
        // }

        // fn.call({x : 101})

        // 箭头函数中的 this
        // 情况1:有function作用域的,this是上层作用域的this
        // 情况2:没有function作用域的,this是window

        // class Obj{
        //     say = () => {
        //         console.log(this);
        //     }
        // }

        // const obj1 = new Obj();
        // obj1.say()

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

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

        // oBtn.onclick = () => {
        //     console.log(this);
        // }

        
    </script>

手写bind函数

知识点

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

演示代码

<script>
        function fn(a,b,c) {
            console.log(this);
            console.log(a,b,c);

            return "this is return"
        }

        // const cb = fn.bind({x : 100},1,2,3);
        // console.log(cb());

        //手写bind
        Function.prototype.myBind = function () {
            const fn = this;

            // [{ x : 100 } ,1,2,3]
            //arguments是类数组
            // const _this = arguments[0];
            const arg =  Array.prototype.slice.call(arguments);
            const _this = arg.shift();

            return function () {
                return fn.apply(_this,arg);
            }

            console.log(this);
        }

        const cb = fn.myBind({ x : 100 },1,2,3)
        console.log(cb()); 


    </script>

谈谈闭包和闭包使用场景

什么是闭包

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

作用域

全局作用域、局部作用域

自由变量

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

触发闭包的情况

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

闭包的应用

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

演示代码

<script>
        // const a = 100 //全局变量

        // function fn() {


        //     console.log(a); //自由变量


        // }

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

        // const a = 5;
        // const cb = fn()
        // cb()

        // 情况2 :函数当做参数传递
        // function fn(cb) {
        //     const a = 100;
        //     cb()
        // }

        // const a = 500;
        // fn(function () {
        //     console.log(a);
        // })

        // 情况3 :自执行匿名函数
        // (function (index) {
        //     console.log(index);
        // })(10);


    </script>
    <button>0</button>
    <button>1</button>
    <button>2</button>
    <button>3</button>
    <button>4</button>
    <script>
        // const aBtn = document.querySelectorAll('button');

        // for(var i=0;i<aBtn.length;i++) {
        //     (function (index) {
        //         aBtn[i].onclick = function () {
        //             console.log(index);
        //         }
        //     })(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'));

    </script>

请描述event loop的机制

JavaScript是如何执行的?

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

Event loop 过程

  1. 同步代码,一行一行放在Call Stack 中执行
  2. 遇到异步,会先"记录"下,等待执行时机(setTimeout、Ajax) 。时机到了,将之前"记录"的代码放入Callback Queue
  3. 当 Call Stack为空 (同步代码执行完),Event Loop 开始工作
  4. Event Loop 轮询查找Callback Queue 中是否有可执行的代码。如果有,将代码移动到Call Stack 中执行
  5. Event Loop 如果没有找到可以执行代码,则会继续轮询查找
event loop流程图.png ## 演示代码
 <script>
    console.log('strat');
    setTimeout (() => {
        console.log('setTimeout');
    },2000)
    console.log('end');
 </script> 

什么是宏任务?什么是微任务?二者有什么区别?


宏任务有哪些?微任务有哪些?
请描述宏任务、微任务和DOM渲染的关系

什么是微任务?什么是宏任务?

微任务:Promise、async、await
宏任务:setTimeout、setInterval、Ajax、DOM事件

二者的区别

先执行微任务,后执行宏任务

宏任务、微任务、DOM渲染的关系

  1. Call Stack 清空,触发Event Loop
  2. 执行微任务
  3. DOM渲染
  4. 执行宏任务

完整Event Loop过程

宏任务和微任务.png

手写Promise加载图片

出题目的

  • 考察Image对象
  • 考察Promise
  • 考察async await

知识点

image 对象

  • new Image() —— 声明一个Image对象
  • onload —— 当图片加载成功时执行
  • onerror —— 当图片加载失败时执行
  • .src —— 设置图片路径

Promise

  • 作用:解决回调地狱问题
  • 语法:
new Promise().then().catch()

async await

  • 作用:Promise 的语法糖,可以增加代码可读性 (用同步的思维写代码)
  • 语法:
(async function () {
    try {
        const oImg = await loadImg("https://robohash.org.1");
        document.body.appendChild(oImg1);
        const oImg2 = await loadImg("https://robohash.org/2");
        document.body.appendChild(oImg2)
    } catch (msg) {
           console.log(msg);
       }
})()

Promise 阅读代码题

  • 考察Promise、async、await 的基础
  • 考察对Event Loop、宏任务、微任务的理解

知识点

  • JS执行顺序:自上而下、先同步再异步、先微任务后宏任务
  • new promise() -> Promise.resolve()
  • then 和 catch 内部没有 throw new Error 相当于 resolve
  • async function 相当于返回 Promise.resolve()
  • await 后面的代码都是异步的
  • 初始化Promise时,函数内部代码会被立即执行

for...in、for..of有什么区别

出题目的

  • 考察JavaScript基础

参考答案

image.png

是否可枚举:Object.getOwnPropertyDescriptors(obj) -> enumerable:true
是否可迭代:arrSymbol.iterator -> next()

拓展问题

for await ...of 有什么作用?

演示代码

<script>
        // const arr = [10,20,30];
        // for(let key in arr){
        //     console.log(key);
        // }

        // for(let key of arr){
        //     console.log(key);
        // }

        const obj = {
            name:'abc',
            age:18,
        }

        for (let key in obj) {
            console.log(key);
        }

        const set = new Set([10,20,30]);
        for (let val of set) {
            console.log(val);
        }

        const map = new Map([
            ["a",10],
            ["b",10],
            ["c",10]
        ])
        for (let val of map) {
            console.log(val);
        }
    </script>

for await..of有什么作用?

出题目的

  • 考察对异步的掌握情况

参考答案

for await .. of 用于遍历一组Promise

知识点

for await .. of
Promise.all

<script>
        function getPromise(num) {
            return new Promise((resolve,reject) => {
                setTimeout (() => {
                    resolve(num);
                },1000)
            })
        }

        const p1 = getPromise(10)
        const p2 = getPromise(20)
        const p3 = getPromise(30)
        const list = [p1,p2,p3];

        (async function () {
            const data = [10,20,30];
            for (let val of data) {
                const res = await getPromise(val)
                console.log(res);
            }
        })()

        // (async function() {
        //     for await (let res of list) {
        //         console.log(res);
        //     }
        // })()

        // Promise.all(list).then((res) => {
        //     console.log(res);
        // })
    </script>

var、let和const有什么区别?

出题目的

  • 考察JS基础
  • 暂时死区

参考答案

var、let和const的区别
1.块级作用域
2. 变量提升
3. 重名

手写flat方法拍平数组

出题目的

  • 考察Array相关API熟悉程度

知识点

Array.concat -Array.concat(1,2,3,[4,5])

判断是不是数组

  • xxx instanceof Array

递归

参考答案

    <script>
        function falt(arr) {
            const hasDeep = arr.some(item => item instanceof Array)
            if(!hasDeep) {
                return arr
            }
            const res = Array.prototype.concat.apply([],arr)
            return flat(res)
        }

        function flat(arr) {
            while(arr.some((item) => item instanceof Array)) {
                arr = Array.prototype.concat.apply([],arr)
            }
            return arr
        }
    </script>

数组去重

出题目的

  • 考察Set的使用

参考答案

    <script>
        const arr = [10,20,20,20,30,50,50,60];
        console.log(unique(arr));
        function unique (arr) {
            const set = new Set(arr);
            return [...set];
        }

        // function unique(arr) {
        //     const res = [];
        //     arr.foreach((item) => {
        //         if(res.indexOf(item) === -1) {
        //             res.push(item)
        //         }
        //     })
        //     return res
        // }
    </script>

求数组中的最大值?

出题目的

  • 考察数组API

知识点

  • Array.sort
  • Array.reduce
  • Math.max

参考答案

<script>
        let arr = [100,5,20,3,200,6]
        console.log(getMax(arr));

        // function getMax(arr) {
        //     // return Math.max.apply(null,arr);
        //     return Math.max(...arr)
        // }

        // function getMax(arr) {
        //     arr.sort((n1,n2) => {
        //         return n2 - n1
        //     })

        //     return arr[0]
        // }

        function getMax(arr) {
            return arr.reduce((n1,n2) => {
                return n1 > n2 ? n1:n2
            })
        }
    </script>

防抖和节流有什么区别? 手写防抖与节流

出题目的

  • 考察防抖与节流的掌握程度

知识点

防抖与节流

image.png

参考答案

<style>
    div {
        margin-top: 20px;
        width: 200px;
        height: 200px;
        background-color: #ccc;
    }
</style>
<h1>手写防抖与节流</h1>
<input type="text">
<div draggable="true"></div>
<body>
    <script>
        
        // 防抖

        // const oInp = document.querySelector("input");

        // oInp.addEventListener(
        //     "keyup",
        //     debounce(function () {
        //         console.log(oInp.value + "取后台请求");
        //     })
        // )

        // function debounce(fn) {
        //     let timer = null;

        //     return function() {
        //         if(timer) clearTimeout(timer);

        //         timer = setTimeout(() => {
        //             fn.call(this,arguments);
        //             timer = null;
        //         },1000);
        //     }
        // }

        //节流

        const oDiv = document.querySelector('div');

        oDiv.addEventListener(
            'drag',
            throttle(function (e) {
                console.log(e.clientX);
            })
        );
        function throttle(fn) {
            let timer = null
            return function () {
                if (timer) return;

                timer =  setTimeout(() => {
                    fn.apply(this,arguments);
                    timer = null
                },100)

            }
        }
    </script>