关于js作用域链条this上下文闭包的概括以及想法
js的作用域
前端javascript的作用域的基础就是函数,可以说函数就是一个拥有独立作用域的单位。
作用域的解释:限制量可用的代码范围。
为什么设计出作用域的概念?
- 隔离代码块,减少变量冲突;
- 提高了程序逻辑的局部性;
- 模块化;
作用域链
作用域链的解释就牵扯到函数的嵌套,当函数一层一层嵌套很多层时,他们之间就形成了一个作用域链的关系。
在作用域链中内部作用域可以访问外层作用域的量,外层作用域只能访问本身作用域下的量。
作用域下的一些关键点
变量和函数的提升
- 变量声明提升:变量在声明的时候会被放在当前作用域的最上方;
- 函数声明提升:函数在声明的时候会被提升到当前作用域变量提升的下方;
- 变量和函数的声明提升都是有序的,先提升就会先被访问到,提升范围就是当前作用域下;
console.log(foo)
var foo = 'bar';
var function foo(){}
// 打印结果 ƒ foo(){}
// 结果可以看到函数被打印,说明变量的声明被函数给覆盖了,
// 因此变量被提升在第一位,函数在第二位
那么为什么会有变量和函数的提升呢(为什么这样设计呢)?
为了保证在作用域内的量都可以直接使用,只关注量是否存在,方便开发者去操作。总结就是便于使用哈
那么为什么函数的提升会被放在变量之后呢?
function foo (){if(a>10){...}}
var a = 10
// 如果foo先被提升 foo在解析的时候变量a还为声明
// 如果函数先被提升那么变量a是无法被访问的,此段代码就会报错
一句话,如果函数先提升了,函数内部有访问到一些变量,这时候就无法访问到了;
因为函数内部可能会有变量的访问,所以变量一定要先于函数去声明。
上下文(this指针)
- this是在执行(调用)时确定指向,只是过程中动态读取上下文获取指向;
- 函数调用函数中的this指向调用者(调用函数的对象),公共函数 | 全局调用直接指向windows;
- 隐式绑定的时候函数指向调用堆栈的上一级(函数附着在对象上);
- 显式绑定(call、apply、bind)看绑定的指向
顺便提及一下,面试的重点,手写apply、call、bind
如果要手写,首先要理解他们的作用和他们之间的区别。
call和apply都会调用函数,call传递参数时列表,apply时数组参数。
bind不会调用函数,传递的也是列表参数。
手动apply示例:
// An highlighted block
// 第一步找到挂载的地方,就是挂载到function上
Function.prototype.newApply = function (context,args){
// context 新的this指向
// args 参数组
context = context || window // 没有传递,指向window
// 判断context 的数据类型
switch (typeof context) {
case 'string':
context = new String(context)
break;
case 'number':
context = new Number(context)
break;
}
context._fn= this // 此时的this指向newApply的调用者
const result = args&&args.length > 0 ?
context._fn([...args]) :
context._fn()
// 调用函数接收结果
delete context.fn // 删除暂时挂载
return result // 输出结果
}
var _this = {name:'我是新的this'};;
function foo (){console.log(this)}
foo.newApply(_this)
// 结果 {name: '我是新的this', fn: ƒ}
这里还有一个关键点,js代码的执行顺序,先入栈的后出栈
闭包
闭包的概述:可以突破作用域链,将一个局部作用域的量以包裹的形式传递暴漏在另一个作用域,那么他们之间的关系就是闭包。
常规闭包的使用
- js模块化;
- 初始化数据结构的函数;
- 常规的一些过滤函数,通过输入的类型经过一定函数操作后返回数据;