没offer就一直写|美团一面凉

110 阅读7分钟

刚刚面完美团小程序相关部门,自知凉,面试官很好,问题也不难,都是js基础和小程序机制。以前背过的八股文,忘的七七八八,只能怪自己学的不扎实。

JS基础

一、闭包Closure

关键字: 作用域、变量、内存泄漏、作用域链

1.什么是闭包

闭包是指有权访问另一个函数作用域中的变量的函数。

2.闭包的两种表现
  • 函数作为参数被传递
  • 函数作为返回值被返回
3.简单判断闭包的方式:

函数在定义时的词法作用域以外被调用。

       function foo(){
           var a = 2;
           function bar(){
               console.log(a); //2
           }
           bar();
       }
       foo();

这是闭包吗?技术上讲,是! 技术上讲,所有函数都是闭包。实践上讲,不是!因为bar()在定义时的作用域内被调用。

    function foo(){
        var a = 2function bar(){
            console.log(a)
        }
        return bar;
    }
    var baz = foo();
    baz(); //2 ---这里是闭包的效果

bar()函数作为值被传递,他在自己定义时的作用域外被执行。

4.闭包的特性

闭包只能取得包含函数中任何变量的最后一个值。

    function createFunction(){
        var result = new Array();
        
        for(var i = 0; i < 3; i++){
            result[i] = function(){
                return i;
            }
        }
        
        return result;//[f,f,f]
    }
    

这个函数会返回一个函数数组,每个函数都返回3。因为每个函数的作用域链中都保存着createFunction()函数的活动对象,所以他们引用的是同一个变量i。createFunction()函数返回后,变量i的值是3。

但是,我们可以通过创建另一个匿名函数,使每个函数返回不同的索引值。

    function createFunction(){
        var result = new Array();
        
        for(var i = 0; i < 3; i++){
            result[i] = function(j){
                return function(){
                    return j
                };
            }(i);
        }
        return result;//[f,f,f]
    }

这里定义了一个匿名函数,并将立即执行的匿名函数的结果赋值给了数组。在调用每个匿名函数时,传入了变量i,因为函数参数是按值传递的,因此会将变量i当前值复制给参数j。在匿名函数内部又创建了一个能访问j的闭包,这样,result中每个函数都有自己j变量的副本。

5.面试题
    for(var i = 0; i < 5; i++){
        setTimeout(function timer(){
            cosnole.log(i)//5 5 5 5 5
        },1000)
    }
    for(var i = 0; i < 5; i++){
        (function(i){
            setTimeout(function timer(){
                cosnole.log(i)//0 1 2 3 4
            },1000)
        })(i)//值传递
        
    }
    //块作用域,使用let声明,每次循环i都会被声明一次
    for(let i = 0; i < 5; i++){
        setTimeout(function timer(){
            cosnole.log(i)//0 1 2 3 4
        },1000)
    }
6.闭包造成的问题

内存泄漏,因为闭包保存了包含函数的活动对象的引用,因此包含活动对象的内存不会被回收。

参考: developer.mozilla.org/zh-CN/docs/… 《JavaScript高级程序设计》、《你不知道的JavaScript》

二、作用域链

关键词:执行环境、变量对象、查找变量和函数

一 、是什么

是由执行环境变量对象组成的链表。

当查找变量的时候,会先从当前执行环境的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)包含(父级)执行环境的变量对象中查找,一直找到全局执行环境的变量对象。这样由多个执行环境的变量对象构成的链表就叫做作用域链。

二、作用:

保障执行环境对可访问的变量和函数的有序访问。

三、相关定义:

  • 执行环境:执行环境定义了变量和函数有权访问的其他数据,决定了他们各自的行为。
  • 变量对象:每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都存在这个对象中。我们编写的代码无法访问这个对象,解析器在处理数据时会使用它。
    活动对象:

某个环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数也随之销毁。
每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入环境栈中。在这个函数执行完后,栈将其弹出,把控制权返回给之前的执行环境。

  • 作用域链:当代码在一个环境中执行时,会创建变量对象的一个作用域链。

作用域链的前端,始终是当前执行代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象最开始只包含一个变量,即arguments对象(这个对象在全局环境中不存在)。作用域链中的下个变量对象来自包含环境,而下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终是作用域链中的最后一个对象。

四、面试题

1.作用域类型有哪些?
  • 全局作用域
  • 模块作用域
  • 函数作用域
  • 块级作用域

变量的声明应该距离使用的地方越近越好,块作用域是一个对最小授权原则进行扩展的工具。
with、try/catch中的catch、let、const

2.什么是作用域链?

当查找变量的时候,会先从当前执行环境的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)包含(父级)执行环境的变量对象中查找,一直找到全局执行环境的变量对象。这样由多个执行环境的变量对象构成的链表就叫做作用域链。

3.什么是动态作用域和静态作用域

静态作用域(词法作用域):在写代码或者说定义时确定的,关注函数在何处声明。
动态作用域:在运行时确定的,关注函数从何处调用。

juejin.cn/post/684490…

三、如何避免内存泄漏

  1. 尽可能少的创建全局变量
  2. 手动清除定时器
  3. 少用闭包
  4. 清除DOM引用
  5. 弱引用( WeakMap、WeakSet )

四、WeakMap

WeakMap与Map结构类似,也用于生成键值对的集合。

WeakMap与Map的区别有以下两点:
  1. WeakMap只接受对象作为键名(null除外)。
  2. WeakMap的键名所指向的对象不计入垃圾回收机制。
    WeakMap设计的目的在于:

    有时我们想在某个对象上存放一些数据,但是会形成这个对象的引用,一旦不需要这个对象,就要手动删除这个引用,否则垃圾回收机制就不会释放这个对象占用的内存,一旦忘记删除,就会造成内存泄漏。
    WeakMap就是为了解决这个问题而诞生的。它的键名引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内。因此,只要所引用对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。

面试题:
  • weakMap可以使用foreach吗

不可以,weaKMap没有遍历操作(即没有keys()、values()、entries()方法),也没有size属性。因为没有办法列出所有键名,某个键名是否存在完全不可预测,和垃圾回收机制是否运行有关。这一刻可以取到键名,下一刻垃圾回收机制突然运行,这个键名就消失了,为了防止出现不确定性,因此统一规定不能取到键名。

  • foreach可以被中断吗?

除非抛出异常,否则没有办法停止或者中断循环。因为本身设计该函数的初衷是为遍历每一个元素。
developer.mozilla.org/zh-CN/docs/…

五、for in 和for of

for-in(ES5)for-of(ES6)
使用用于可枚举的数据,比如对象、数组、字符串用于可迭代的对象,比如数组、字符串、Map、Set
输出属性名属性
顺序无序遍历按顺序遍历
原型会遍历原型上的属性和方法只能遍历当前数据本身的元素值
    var arr = ['a','b','c'];
    
    for(var i in arr){
        console.log(i);// 0 1 2
    }
    
    for(var i of arr){
        console.log(i);// 'a' 'b' 'c'
    }

blog.csdn.net/weixin_4050…

六、事件循环(event Loop)

JS是单线程的,异步是基于回调来实现,event Loop就是异步回调的实现原理。
关键词:调用栈、(宏/微)任务队列

  1. 执行call stack(调用栈)中的代码
  2. 启动event Loop机制,轮询查找callback Queue,如有则一道call stack中执行
  3. 然后继续轮询查找

developer.mozilla.org/zh-CN/docs/…

七、任务类型(微任务、宏任务)

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

微任务执行早于宏任务

宏任务微任务
包含setTimeout、setInterval、Ajax、DOM事件Promis、async/await
定义由浏览器规定ES6语法
执行时间晚于DOM渲染早于DOM渲染
  1. 执行call Stack中的代码
  2. 执行微任务
  3. 尝试DOM渲染
  4. 执行eventLoop机制

developer.mozilla.org/zh-CN/docs/…

zhuanlan.zhihu.com/p/33058983

微信小程序

一、 组成结构

  • WXML:页面结构;
  • WXSS: 页面样式;
  • js: 页面逻辑;
  • json:页面配置。

二. 数据通信-setData原理

1. setData 的流程

setData 的过程,大致可以分成几个阶段:

  • 逻辑层虚拟 DOM 树的遍历和更新,触发组件生命周期和 observer 等;
  • 将 data 从逻辑层传输到视图层;
  • 视图层虚拟 DOM 树的更新、真实 DOM 元素的更新并触发页面渲染更新。

2.数据通信

对于第 2 步,由于小程序的逻辑层和视图层是两个独立的运行环境、分属不同的线程或进程,不能直接进行数据共享,需要进行数据的序列化、跨线程/进程的数据传输、数据的反序列化,因此数据传输过程是异步的、非实时的

iOS/iPadOS/MacOS 上,数据传输是通过 evaluateJavascript 实现的,还会有额外 JS 脚本解析和执行的耗时。

数据传输的耗时与数据量的大小正相关,如果对端线程处于繁忙状态,数据会在消息队列中等待

image.png

《小程序底层架构剖析》
《合理使用setData》

三. 登录机制

code: 微信登录凭证
openid:用户在当前小程序的唯一标识

image.png developers.weixin.qq.com/miniprogram…

四. 更新迭代

juejin.cn/post/699143…

五. 做过哪些小程序的优化

《性能与体验》