刚刚面完美团小程序相关部门,自知凉,面试官很好,问题也不难,都是js基础和小程序机制。以前背过的八股文,忘的七七八八,只能怪自己学的不扎实。
JS基础
一、闭包Closure
关键字: 作用域、变量、内存泄漏、作用域链
1.什么是闭包
闭包是指有权访问另一个函数作用域中的变量的函数。
2.闭包的两种表现
- 函数作为参数被传递
- 函数作为返回值被返回
3.简单判断闭包的方式:
函数在定义时的词法作用域以外被调用。
function foo(){
var a = 2;
function bar(){
console.log(a); //2
}
bar();
}
foo();
这是闭包吗?技术上讲,是! 技术上讲,所有函数都是闭包。实践上讲,不是!因为bar()在定义时的作用域内被调用。
function foo(){
var a = 2;
function 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.什么是动态作用域和静态作用域
静态作用域(词法作用域):在写代码或者说定义时确定的,关注函数在何处声明。
动态作用域:在运行时确定的,关注函数从何处调用。
三、如何避免内存泄漏
- 尽可能少的创建全局变量
- 手动清除定时器
- 少用闭包
- 清除DOM引用
- 弱引用( WeakMap、WeakSet )
四、WeakMap
WeakMap与Map结构类似,也用于生成键值对的集合。
WeakMap与Map的区别有以下两点:
- WeakMap只接受对象作为键名(null除外)。
- 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'
}
六、事件循环(event Loop)
JS是单线程的,异步是基于回调来实现,event Loop就是异步回调的实现原理。
关键词:调用栈、(宏/微)任务队列
- 执行call stack(调用栈)中的代码
- 启动event Loop机制,轮询查找callback Queue,如有则一道call stack中执行
- 然后继续轮询查找
developer.mozilla.org/zh-CN/docs/…
七、任务类型(微任务、宏任务)
宏任务:setTimeout、setInterval、Ajax、DOM事件
微任务:Promis、async/await
微任务执行早于宏任务
| 宏任务 | 微任务 | |
|---|---|---|
| 包含 | setTimeout、setInterval、Ajax、DOM事件 | Promis、async/await |
| 定义 | 由浏览器规定 | ES6语法 |
| 执行时间 | 晚于DOM渲染 | 早于DOM渲染 |
- 执行call Stack中的代码
- 执行微任务
- 尝试DOM渲染
- 执行eventLoop机制
developer.mozilla.org/zh-CN/docs/…
微信小程序
一、 组成结构
- WXML:页面结构;
- WXSS: 页面样式;
- js: 页面逻辑;
- json:页面配置。
二. 数据通信-setData原理
1. setData 的流程
setData 的过程,大致可以分成几个阶段:
- 逻辑层虚拟 DOM 树的遍历和更新,触发组件生命周期和 observer 等;
- 将 data 从逻辑层传输到视图层;
- 视图层虚拟 DOM 树的更新、真实 DOM 元素的更新并触发页面渲染更新。
2.数据通信
对于第 2 步,由于小程序的逻辑层和视图层是两个独立的运行环境、分属不同的线程或进程,不能直接进行数据共享,需要进行数据的序列化、跨线程/进程的数据传输、数据的反序列化,因此数据传输过程是异步的、非实时的。
iOS/iPadOS/MacOS 上,数据传输是通过
evaluateJavascript实现的,还会有额外 JS 脚本解析和执行的耗时。
数据传输的耗时与数据量的大小正相关,如果对端线程处于繁忙状态,数据会在消息队列中等待。
三. 登录机制
code: 微信登录凭证
openid:用户在当前小程序的唯一标识
developers.weixin.qq.com/miniprogram…