前端【基础知识】 ②作用域和闭包(场景题)

2,623 阅读5分钟

前言

    根据慕课网的《快速搞定前端技术一面 匹配大厂面试要求》课程所整理的题目,陆续更新

常考面试题

场景题

    先直接看一下下面这些基础面试场景题,你都会写吗?场景题的答案请拖动下方滚动条,然后查看注释参考对比自己的答案哦

①闭包场景题一

  function print(fn) {
            const a = 200
            fn()
        }
        // 函数作为参数被传递
        
        const a = 100

        function fn() {
            console.log(a) // 在定义的地方往上查找,找到外面全局定义的 a 变量,所以是打印 100
        }

        print(fn)
                                         // 100

②闭包场景题二

  function create() {
            const a = 100
            return function () {
                console.log(a)
            }
        }
        // 函数作为返回值

        const fn = create()
        const a = 200
        fn()
                                       // 打印 100 

③this、call、bind

  function fn1() {
            console.log(this)
        }

        fn1()                             // window

        fn1.call(  { x:100 }  ) // call直接就改变指向执行
                                    // {x:100}
        const fn2 = fn1.bind(  { x:200 }  ) // 返回一个新的函数执行,所以需要赋值
        fn2()
                                          // {x:200}

④this无箭头

 const zhangsan = {
            name: '张三',
            sayHi() {
                                    // this 即当前对象
                console.log(this)
            },
            wait() {
                setTimeout(
                    function() {
                        console.log(this)
                                            // this === window
                        // 本身触发的函数,不是通过对象触发的函数,可以当作这个函数在外部,所以this指向window
                    }
                )
            }
        }
        zhangsan.sayHi()                    // 当前对象
        zhangsan.wait()                     // window

④this有箭头

  const zhangsan = {
            name: '张三',
            sayHi() {
                                    // this 即当前对象
                console.log(this)
            },
            wait() {
                setTimeout(
                    ()  =>  {
                        console.log(this)
                                            // this 即当前对象
                        // 箭头函数取值取其上级作用域的值
                    }
                )
            }
        }
        zhangsan.sayHi()                     // 当前对象
        zhangsan.wait()                     // 当前对象

⑤class、this

  class People {
            constructor(name) {
                this.name = name
                this.age = 20
            }
            sayHi() {
                console.log(this)
            }
        }

        const zhangsan = new People('张三')
        zhangsan.sayHi()                     // 张三对象

⑥作用域面试场景题一

  let i
        for( i = 1 ; i<=3 ; i++ ){
            setTimeout( function() {
                console.log(i)
            } , 0 )
        }
        
                                      // 打印3次4

⑦作用域面试场景题一

  let a = 100

        function test () {
            alert(a)
            a = 10
            alert(a)
        }

        test()
        alert(a)
        
                                      // 100 10 10

1.typeof能判断哪些类型

  • ①识别所有值类型
  • ②识别出函数
  • ③判断是否是引用类型(不可再细分)

2.何时使用===,何时使用==

  • 除了‘== null’,其他情况一律用===
  • if( obj.a == null )相当于if(obj.a === null || obj.a === undifined )
  • ==会发生类型转换

3.值类型和引用类型区别

常见值类型:

  • undifined
  • String 字符串
  • Number 数值
  • Boolean 布尔
  • Symbol (ES6新增)

常见引用类型

  • 对象object
  • Array 数组
  • function 函数
  • null 特殊引用类型

4.手写深拷贝

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

    let result
    if( obj instanceof Array ){
     result = []
    }else {
     result = {}
    }
    
    
    for( let key in obj ){
      if( obj.hasOwnProperty(key) ){
          result[key] = deepClone( obj[key] )
      }
    }
    
    return result
}

5.如何准确判断一个变量是不是数组?

    直接instanceof Array,原因参考原型、原型链

6.手写一个简易的JQuery,考虑插件和拓展性

    写不写得出是一回事,首先要搞懂题目,需要考虑插件和拓展性,意思就是要用到class即可,可以继承,可以通过原型来操作

  • 插件,在原class上添加各种拓展
  • 拓展性,继承写一个自己的class
class JQuery {
        constructor (selector) {
            const result = document.querySelectorAll(selector)
            const length = result.length
            for( let i = 0 ; i<length ; i++ ){
                this[i] = result[i]
            }
            this.length = length
        }
        get ( index ) {
            return this[index]
        }
        each (fn) {
            for ( let i = 0 ; i<this.length ; i++ ){
                const elem = this[i]
                fn(elem)
            }
        }
        on ( type, fn ) {
            return this.each( elem => {
                elem.addEvenListener( type, fn , false )
            } )
        }
        // 可拓展很多 DOM API
    }

7.class的原型本质,怎么理解?

本质

  • class 是 ES6 的新特性,可以用来定义一个类,实际上,class 只是一种语法糖,它是构造函数的另一种写法。(什么是语法糖?是一种为避免编码出错和提高效率编码而生的语法层面的优雅解决方案,简单说就是,一种便携写法。)

  • class有constructor、属性、方法

  • class继承extends、super

原型、原型链

  • 实例.__proto__ 隐式原型
  • class名.prototype 显式原型(全等于上者,引用地址一样)

8.this的不同应用场景,如何取值?

this取什么值,是在函数执行的时候确认的,不是在定义的时候确认的

  • ①当作普通函数调用,返回window
  • ②使用call,apply,bind,传入什么,this绑定什么
  • ③作为方法对象调用
  • ④在class的方法中调用
  • ⑤箭头函数

闭包使用条件:

  • ①函数作为返回值
  • ②函数作为参数传递

9.手写bind函数

// 模拟bind
        Function.prototype.bind1 = function() {
            // 将参数拆解为数组
            const args = Array.prototype.slice.call(arguments)

            // 获取 this (数组第一项)
            const t = args.shift()

            // fn1.bind(...) 中的 fn1
            const self = this

            return function() {
                return self.apply(t,args)
            }
        }

10.实际开发中闭包的应用场景,举例说明

  • ①闭包应用场景之setTimeout:原生的setTimeout传递的第一个函数不能带参数,通过闭包可以实现传参效果
  • ②函数防抖
  • ③使用闭包设计单例模式
  • ④设置私有变量
  • ⑤拿到正确的值

11.手写一个简单的cache工具

  function createCache() {
            const data = {}
            //  闭包中的数据被隐藏,不被外界访问
            return {
                set ( key , val ) {
                    data[key] = val
                },
                get (key) {
                    return data[key]
                }
            }
        }

12.自由变量

自由变量的值:在方法定义的地方往上找(向上级作用域,一层一层依次寻找,直到找到为止,如果到全局作用域都没找到,则报错 xxx is not defined)

写在文末

    如果你觉得我写得还不错的话,可以给我点个赞哦^^,如果哪里写错了、写得不好的地方,也请大家评论指出,以供我纠正。

其它文章

本文使用 mdnice 排版