前端面试js篇-执行上下文、闭包、原型、异步机制

一、预编译

在函数(JS)执行的前一刻,会创建一个叫做执行期上下文的(AO)对象这个创建执行期上下文的过程叫做预编译。
执行上下文栈(下文简称执行栈)也叫调用栈,执行栈用于存储代码执行期间创建的所有上下文
执行上下文创建分为创建阶段与执行阶段两个阶段:1)创建阶段; 2)执行阶段
创建阶段:
1.确定 this 的值,也被称为 This Binding

  • 全局执行上下文中,this 的值指向全局对象(window)
  • 函数执行上下文中,this 的值取决于函数的调用方式。具体有:默认绑定、隐式绑定、显式绑定(硬绑定)(this指针填坑)

2.创建词法环境

  • 环境记录:存储变量和函数声明的实际位置

  • 对外部环境的引用:可以访问其外部词法环境

    对全局数据进行预处理:

  • var 定义的全局变量设置为undefined,添加到window的属性中。

  • function声明的全局函数,赋值function,添加为window方法。

    对局部数据进行预处理:

  • 给形参变量赋值,添加为执行上下文属性。

  • arguments赋值,添加为执行上下文属性。

  • var 定义的局部变量设置为undefined,添加为执行上下文属性。

  • function声明的函数,赋值function,添加为执行上下文属性。

3.创建变量环境

变量环境也是一个词法环境,因此它具有上面定义的词法环境的所有属性。
在 ES6 中,词法环境 和 变量环境 的区别在于前者用于存储函数声明和变量( let 和 const )绑定,而后者仅用于存储变量( var )绑定。

预编译做的事:确定this指向,变量提升,函数声明整体提升(变量先提升,函数再提升)

二、作用域

分为 全局作用域、局部作用域(function内部)和块级作用域(let 、const)
作用域链:查找变量
1.在当前作用域下的执行上下文中查找对应的属性, 如果有直接返回, 否则进入2
2.在上一级作用域的执行上下文中查找对应的属性, 如果有直接返回, 否则进入3
3.再次执行2的相同操作, 直到全局作用域, 如果还找不到就抛出找不到的异常

作用域与执行上下文
作用域: 静态的, 编码时就确定了(不是在运行时), 一旦确定就不会变化了
执行上下文: 动态的, 执行代码时动态创建, 当执行结束消失
联系: 执行上下文环境是在对应的作用域中的
三、闭包

什么是闭包 为什么会有闭包
一个持有外部环境变量的函数就是闭包
闭包可以访问所在的词法作用域,且拥有更长的生命周期,保持对当前词法作用域的引用
词法作用域是由函数声明时所在的位置决定的,而闭包是词法作用域形成的自然结果

闭包解决了什么问题或者说有什么应用
1) 一个是可以读取函数内部的变量;
2) 另一个就是让这些变量的值始终保存在内存中。

基于js的垃圾回收机制,只有仍被引用的变量会继续保存在内存中

代码难以维护:闭包内部是可以访问上级作用域,而如果闭包又是异步执行的话,一定要清楚上级作用域都发生了什么,而这样就需要对代码的运行逻辑和JS运行机制相当了解才能弄明白究竟发生了什么。

使用闭包的注意点:
由于闭包会使得函数中的变量都保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄漏。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
内存泄漏:程序的运行需要内存。对于持续运行的服务进程,必须及时释放不再用到的内存,否则占用越来越高,轻则影响系统性能,重则导致进程崩溃。不再用到的内存,没有及时释放,就叫做内存泄漏。

四、js 的异步机制 event-loop

调用栈、执行栈
setTimeout 0 执行顺序
0 10ms time指的是什么

由于js 是单线程,为了避免主线程等待异步执行结果。js会将同步代码放到主线程排队执行,异步任务会进入消息队列(也叫任务队列 task queue),异步任务分宏任务macrotask(timeout)和微任务microtask(,then)。只有"消息队列"有返回结果,通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行

浏览器 会在一个(macrotask)执行结束后,在下一个(macrotask) 执行开始前,对页面进行重新渲染。

微任务会在当前同步任务执行完成后立即执行,不会等待。所以 then会比timeout 0 先执行

执行流程:

  • 整段脚本script作为宏任务开始执行
  • 遇到微任务将其推入微任务队列,宏任务推入宏任务队列
  • 宏任务执行完毕,检查有没有可执行的微任务
  • 发现有可执行的微任务,将所有微任务执行完毕
  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  • 渲染完毕后,JS线程继续接管,开始新的宏任务,反复如此直到所有任务执行完毕

主要宏任务:
整段脚本script setTimeout setInterval、I/O UI交互事件 postMessage MessageChannel setImmediate(Node.js 环境)
主要微任务:
promise.then catch finally process.nextTick(Node使用) MutationObserver(浏览器使用)

setTimeout有个要注意的地方,如上述例子延迟5s执行,不是严格意义上的5s,正确来说是至少5s以后会执行。因为Web API会设定一个5s的定时器,时间到期后将回调函数加到队列中,此时该回调函数还不一定会马上运行,因为队列中可能还有之前加入的其他回调函数,而且还必须等到Call Stack空了之后才会从队列中取一个回调执行。

所以常见的setTimeout(callback, 0) 的做法就是为了在常规的调用结束后马上运行回调函数。

console.log('Hi');
setTimeout(function() {
 console.log('callback');
}, 0);
console.log('Bye');
// 输出
// Hi
// Bye
// callback
五、原型

原型(对象属性)
Javascript规定,每一个函数都有一个prototype对象属性,指向另一个对象(原型链上面的)。
prototype(对象属性)的所有属性和方法,都会被构造函数的实例继承

实例对象与原型之间的连接,叫做原型链。proto( 隐式连接 )
JS在创建对象的时候,都有一个叫做proto的内置属性,用于指向创建它的函数对象的原型对象prototype。

内部原型(proto)和构造器的原型(prototype)
1、每个对象都有一个proto属性,原型链上的对象正是依靠这个属性连结在一起
2、作为一个对象,当你访问其中的一个属性或方法的时候,如果这个对象中没有这个 方法或属性,那么Javascript引擎将会访问这个对象的proto属性所指向上一个对 象,并在那个对象中查找指定的方法或属性,如果不能找到,那就会继续通过那个对象 的proto属性指向的对象进行向上查找,直到这个链表结束。

六、js柯里化

https://segmentfault.com/a/1190000012769779

函数式编程、函数作为一等公民
一等公民:一等公民可以作为函数参数,可以作为函数返回值,也可以赋值给变量。

没写完,之后有时间再整理

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值