面试 之 作用域链、预编译、闭包

一、作用域

1、js中作用域:

全局作用域、函数作用域、块级作用域(es6增加)

全局作用域:

1、页面打开创建、关闭销毁;
2、全局变量,window对象;
3、不小心声明的未定义好的变量,会变成全局变量,易造成全局污染。

函数作用域:

1、函数创建时产生,执行完销毁;
2、每次调用创建新的作用域,互相之间不重复;

块级作用域:

1、块作用域由 { } 包括
2、执行时创建,执行完销毁

1.1、作用域链 vs 执行上下文

js解释型语言,执行分为:解释、执行两个阶段。分别处理事情不一样:

解释阶段:

词法分析;
语法分析;
作用域规则确定;

执行阶段:

创建执行上下文;
执行函数代码;
垃圾回收

因此,作用域和执行上下文不是一个概念。作用域在定义时就确定,不会改变;执行上下文运行时确定,随时会改变。一个作用域下可能有多个或没有(函数未调用)上下文环境。

2、函数

2.1、函数定义方式

函数式声明、变量声明、function构造函数

function xx(){} // 函数声明在预编译的时候会提前
var xx = function(){} 
var xx = new Function(); // 具备顶级函数作用域

2.2、调用方式

this指向:无论在何处定义创建,谁调用指向谁,无代表window。

1 、作为对象的方法调用:this -> 对象
2 、函数调用:this -> window
3 、构造器调用prototype:必须实例化对象才能调用;this -> 调用对象;
4 、call\apply\bind:this ->传入的this对象
call\apply\bind函数作用:重定义this指向,参数不限定类型

函数名参数生效
apply参数数组形式放入立即生效
call参数按顺序放入立即生效
bind参数按顺序放入返回一个函数,调用时生效

forEach\map的回调函数后都接受第二个参数,调整this指向。

二、预编译

预编译是某作用域在执行解释之前将该作用域下的变量声明、函数声明的数据进行初始化的过程。

全局作用域会生成对象GO、函数AO。

1、全局GO:

先创建GO空对象;
找变量声明,将之作为属性名,值为undefined
找函数声明,值赋予函数体

2、函数AO

先创建GO空对象;
找变量声明,将之作为属性名,值为undefined
实参形参统一;
找函数声明,值赋予函数体

三、作用域链

作用域链就是创建时在不同作用域对应的对象连接起来的链式结构。

1、闭包

表现形式:A中return B函数;函数嵌套;函数自调用

原理:函数调用执行时,其作用域才开始创建,可追溯到父级作用域链。B调用时,作用域链的关系A作用域的内容依旧可以访问,来达到B可以操作其创建之前A的作用域的内容。

经典例题:for循环里的定时器、获取多个元素添加点击事件

js执行时,先执行主线程,异步相关会存到异步队列里,当主线程执行完毕开始执行异步队列,循环里的值已执行到最后

好处:

函数局部变量长存内存中,避免全局变量污染,私有成员存在;

坏处:

内存泄露导致内存溢出;长存内容过多,性能问题。

使用场景:

1、迭代器
2、getter\setter
3、循环赋值
4、函数赋值、函数参数、返回值、自执行函数
5、首次区分:防抖、节流、缓存、单例模式

防抖:n秒后执行一次;节流:n秒内执行一次

2、内存管理

内存的生命周期

内存分配:声明变量、函数、对象等的时候,系统自动分配内存
内存使用:即读写内存,也就是使用变量、函数等
内存回收:使用完,由垃圾回收机制自动回收不在使用的内存

常见内存泄露

意外的全局变量
被遗忘的计时器或回调函数
脱离DOM的引用(DOM清除,事件还在)
闭包

如何避免内存泄露

减少不必要的全局变量。使用严格模式避免意外创建全局变量
使用完数据后,及时解除引用(闭包中的变量、dom引用、定时器清除;赋值null)
组织好代码逻辑,避免死循环

垃圾回收机制

原理:垃圾回收算法主要依赖于“引用”概念,原型(隐式)引用、属性(显式)引用;
方法
1、引用计数垃圾回收

是否有指向该对象的引用;
问题:循环引用(两个对象互相引用)。不会回收

2、标记清除算法

从根部(js即全局变量)出发定时扫描内存中的对象;不能到达即稍后回收
工作步骤:运行时给所有变量加上标记;从根部触发,能触及对象去掉标记;清除待标记的对象并回收他们占用的空间

应用:sizeOf函数,计算传入对象所占的Bytes数值

研究原因:线上使用了需要计算从服务端传输数据的大小

浏览器查看:chrome 开发者工具 -> memory -> Take snapshot -> 点击快照,在列表中寻找你的变量
实现 npm包:object-sizeof

function sizeof (object) {

  if (Buffer.isBuffer(object)) {

    return object.length

  }



  var objectType = typeof (object)

  switch (objectType) {

    case 'string':

      return object.length * ECMA_SIZES.STRING

    case 'boolean': 

      return ECMA_SIZES.BOOLEAN // 4

    case 'number':

      return ECMA_SIZES.NUMBER //8

    case 'object':

      if (Array.isArray(object)) {

        return object.map(sizeof).reduce((acc, curr) => acc + curr, 0)

      } else {

        return sizeOfObject(object)

      }

    default:

      return 0

  }

}



function sizeOfObject (object) {

  if (object == null) {

    return 0

  }



  var bytes = 0

  for (var key in object) {

    if (!Object.hasOwnProperty.call(object, key)) {

      continue

    }



    bytes += sizeof(key)

    try {

      bytes += sizeof(object[key])

    } catch (ex) {

      if (ex instanceof RangeError) {

        // circular reference detected, final result might be incorrect

        // let's be nice and not throw an exception

        bytes = 0

      }

    }

  }



  return bytes

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值