有权访问其他函数内部作用域变量的函数
连接函数内部和外部的桥梁
闭包的严格定义:
延长作用域链【让一个变量一直保存在内存里,不污染全局变量】
function a(){ //在这个函数中,b依赖于a
var count = 0;
function b(){
count++
console.log(count)
}
return b;
}
var c= a() //c调用a函数,return的是b,所以c引用了b,但是a被间接引用,a不会被垃圾回收
c()//1
c()//2
c()//3
//每次调用c都会使count+1,因为a中的count一直保存咋内存中
以下:是三种概念的变化
ES3:
ES5:
ES2018
函数调用时,创建的活动记录。这个记录里包含了函数调用时需要的信息
当js引擎进入不同的运行环境就会创建对应的一个执行上下文
下面介绍一下执行上下文
var name = 'window';
outer();
function outer(){
var name = 'outer';
inner();
//函数内部的函数
function inner(){
var name = 'inner';
console.log(name);
}
}
在ECMAScript中,代码又三种类型:global、function、eval
每一种代码的执行都需要依赖自身的上下文【当然,global可能涵盖了更多的function和eval实例】
函数的每一次调用,都会进入函数执行中的上下文,来计算函数中变量的值
对于eval也是一样,也会进入eval的执行上下文,判断应该从何处获取变量的值
注意
一个函数可能产生无限的执行上下文,因为函数的调用(递归)产生了一个新的上下文环境
function foo(bar){
}
// 调用相同的function,每次都会产生3个不同的上下文
//(包含不同的状态,例如参数bar的值)
foo(10);
foo(20);
foo(30);
一个执行上下文可以激活另一个上下文,【函数调用】
在arguments中有介绍,
caller返回调用该函数的对象
calee返回正在被执行的函数
如下图,所有的 ECMAScript 的程序执行都可以看做是一个执行上下文堆栈 [execution context (EC) stack]。堆栈的顶部就是处于激活状态的上下文。
见下图,有一个函数上下文“EC1″和一个全局上下文“Global EC”,下图展现了从“Global EC”进入和退出“EC1″时栈的变化:
创建执行上下文时,会用一个变量对象,存储执行上下文中的变量/函数
分类
用于在何处以及如何查找变量的【规则】
var a = 2
1. var a //声明
2. a = 2 //赋值
变量提升:
console.log(a)//ReferenceError
let a = 1
暂时性死区:
//代码1
var a = 2
if(true){
console.log(a)//ReferenceError
let a = 3
}
我觉得大家看了上面两段代码,肯定跟我一样,第一眼就觉得这两个概念好像一样,但是其实不是,我们将下面的代码改一下
//代码2
var a = 2
if(true){
console.log(a)//2
//let a = 3 注释let声明后,就会输出a的值,原因【内部作用域可以访问全局作用域】
}
当我们改成let声明使,也会输出2,原因和上面一样【内部作用域可以访问全局作用域】
//代码3
let a = 2
if(true){
console.log(a)//2
//let a = 3
}
解答:
细心的朋友可以发现,代码1比下面两段代码多了一句就是在,if内部使用了let重新声明了变量a
也就是说,在块级作用域中,如果使用了let 声明,那么这个变量就会绑定这个区域,不受外部影响,如果变量在声明之前使用,则会报错【对于const也是一样】
let b = 5
{
console.log(b)//ReferenceError
let b = 10
}
注释let声明后,又对了
let b = 5
{
console.log(b)//5
// let b = 10
}
this指向上下文这个记录的属性,在函数执行过程中用到
取决于函数在哪里调用【函数调用时发生的绑定】
var name = 'hahaha'
function test(){
var name = 1
console.log(this.name)//hahaha
}
test()//全局调用了
console.log(this.name)//hahaha
var name = 'i am window'
var a = {
name: 'i am an apple',
fn: function(){
console.log(this.name)
}
}
var f = a.fn//定义了一个全局变量,并赋值了一个方法
f();//i am window
下面这段代码是将a.fn()的调用后的结果赋给f
var name = 'i am window'
var a = {
name: 'i am an apple',
fn: function(){
console.log(this.name)
}
}
var f = a.fn()//i am an apple
下面这段代码,最后调用是在全局中的test(),所以还是window
var name = 'window'
function test(){
var name = 'apple'
innerFun();
function innerFun(){
console.log(this.name)
}
}
test()//window
call
var name = 'i am window'
var a = {
name: 'i am an apple',
fn: function (a, b) {
console.log(this.name)
console.log(a + b)
}
}
var f = a.fn
f.call(a,1,9)
//i am an apple
// 10
apply
var name = 'i am window'
var a = {
name: 'i am an apple',
fn: function (a, b) {
console.log(this.name)
console.log(a + b)
}
}
var f = a.fn
f.apply(a,[1,9])
//i am an apple
// 10
var name = 'i am window'
var a = {
name: 'i am an apple',
fn: function (a, b) {
console.log(this.name)
console.log(a + b)
}
}
var f = a.fn
f.bind(a,1,9)//没有输出
f.bind(a,1,9)()//i am an apple 10
//或者用下面的方式输出【切记,test指向a】
var test = f.bind(a,1,9)
test() //i am an apple 10
参考文献:
https://www.cnblogs.com/TomXu/archive/2012/01/12/2308594.html
https://www.cnblogs.com/TomXu/archive/2012/01/12/2308594.html