执行上下文与作用域
前言:function也是对象类型,对象的有些属性我们无法访问(JS引擎内部的固有的隐式属性),[[ scope ]]就是一个隐式属性:① 函数创建时生成的JS内部的隐式属性
② 函数存储作用域链的容器
例子
1 | function a(){ |
- 首先看一下这段代码预编译生成GO,AO的结果

- 再看一下作用域链

a函数执行

b被定义时

b执行时

b函数执行完后,b的AO被销毁

a函数执行完后,a的AO被销毁

总结:
- 在全局执行的前一刻生成GO
- 全局代码执行时,进行赋值
- 函数被定义时,生成[[ scope ]],scope chain
- 函数执行前一刻生成自己的AO(这也就说明了为什么在函数a中访问不到b函数中的变量,因为在a生成AO的时候,b还没有创建自己的AO)
立即执行函数表达式 :IIFE(immediately invoke function express)
立即执行函数的两种基本写法
- (function(){})();
- (function(){}()); W3C建议
立即执行函数传参与接收返回值
1
2
3
4// 传参
(function(a,b){})(2,3);
// 接收返回值
var num = (function(){})();立即执行函数执行完后AO立即销毁
1
2
3
4
5(function test(){})();
console.log(test);// 报错:test is not defined
/*
* 这就说明立即执行函数写不写都一样,因为执行后被释放了AO
*/如何让函数变为立即执行函数
一定是表达式的形式的函数,这样在其后加上()才能立即执行
1
2
3
4
5
6
7
8// 反例
function test(){}();
/*
* 报错,无法执行。是因为这个函数不是表达式的形式,不可以直接加 (),
* Javascript引擎看到function关键字之后,认为后面跟的是函数定义语句,不应* 该以圆括号结尾
*/
var test = function(){ return 1; }(); // 可以执行,且test为1。是因为函数声明放到了等号的右边变成了一个表达式,就可以加()立即执行把函数变成表达式的形式
1
2
3
4
5
6+function(){}();
-function(){}();
!function(){}();
true && function(){}();
false || function(){}();
1,function(){}(); // 逗号运算符:总是返回最后一个变量立即执行函数代码示例
1
2
3
4
5
6
7
8function test(){}(6); // 输出6,不报错,为什么?
/*
* JS引擎发现()里有数字6,就把(6)看做了一个单独的表达式,而不是立即执
* 行。
* JS解析成:
* function test(){}
* (6);
*/1
2
3
4
5
6
7var fn = (function test1(){ return 1; } , function test2(){ return 2; })();
console.log(fn); // 2
/*
* 逗号运算符只返回最后一个值,所以相当于
* var fn = function test2(){ return 2; }();
* fn 为 2
*/1
2
3
4
5var a = 10;
if(function b(){}){ // 为真
a += typeof(b); // b是一个为被定义的变量,所以typeof b 为 'undefined'
}
console.log(a); // '10undefined'1
2
3
4(function(){})() // 应加上 ;
/* 或在这里加 ;*/(function(){})()
// 会报错:Uncaught TypeError: (intermediate value)(...) is not a function
// 所以一定不要忘记加 ; 来分割语句表达式函数忽略了函数名
1
2
3if(function a(){}){
console.log(a); // 报错 a is not defined,说明函数名a被忽略
}立即执行函数总结:
- 使用立即执行函数不必为函数命名,避免了污染全局变量
- IIFE内部形成了一个独立的作用域,可以封装一些外部无法读取的私有变量,同时也避免了变量污染
闭包
闭包是一种现象,内部函数可以访问外部函数变脸,但是外部却无法访问内部函数的变量
闭包的应用
用闭包做数据缓存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22function test(){
var n = 100;
function add(){
n++;
console.log(n)
}
function reduce(){
n--;
console.log(n);
}
return [ add, reduce ]
/*
* 或return{
* add:add,
* reduce:reduce
*}
*/
}
var fn = test();
fn[0](); // add 101
fn[0](); // add 102
fn[1](); // reduce 101用window 代替 return 也可以实现闭包
- 写一个插件,任意传两个数字,调用插件内部方法进行加减乘除功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26(function(){
function add(a,b){
return a + b;
}
function subtract(a,b){
return a - b;
}
function devide(a,b){
return a / b;
}
function multiply(a,b){
return a * b;
}
function CalculatePuglin(){
this.add = add;
this.subtract = subtract;
this.devide = devide;
this.multiply = multiply;
}
window.Calculate = CalculatePuglin;
})();
var c = new Calculate();
c.add(1,2);// 3
c.subtract(1,2); // -1
c.devide(2,1); // 2
c.multiply(1,2); // 2
- 写一个插件,任意传两个数字,调用插件内部方法进行加减乘除功能