JavaScript作用域、立即执行函数、闭包、this

作用域

定义:所有变量(包括基本类型和引用类型)都存在于一个执行环境中(也称为作用域)当中,这个执行环境决定了变量的生命周期,以及哪一部分代码可以访问其中的变量。

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
27
28
29
30
31
function a() {
function b() {
function c() {
//something
}
c();
}
b();
}
a();

a defined a.[[scope]] -- > 0 : GO

a doing a.[[scope]] -- > 0 : aAO
1 : GO

b defined b.[[scope]] -- > 0 : aAO
1 : GO

b doing b.[[scope]] -- > 0 : bAO
1 : aAO
2 : GO

c defined c.[[scope]] -- > 0 : bAO
1 : aAO
2 : GO

c doing c.[[scope]] -- > 0 : cAO
1 : bAO
2 : aAO
3 : GO

立即执行函数

立即执行函数,执行完后立即销毁(释放)即针对初始化功能的函数。

对于函数,只有表达式才能被执行,外面有一层括号使之成为表达式。

1
2
3
4
let num = (function (a, b, c) {
let d = a + b + c;
return d;
}(1, 2, 3));

考查知识点

1
2
3
4
5
6
7
let x = 1;
if (function f() {}) {
x += typeof f;
}
console.log(x); // 1undefined
// f函数外加括号, 已经变成函数表达式,
// 不再是函数声明, f已经成为undefined;

compare

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 报错语法错误,只有函数表达式才可以被执行
function foo(x) {
console.log(arguments);
}()

// 下面的写法可以理解为分家了,像下面一样 不执行也不报错
function foo(x) {
console.log(arguments);
}(1, 2, 3, 4, 5)

function foo(x) {
console.log(arguments);
}

(1, 2, 3, 4, 5)

注意理解

1
2
3
4
let test = function () {
console.log('a');
}();
// 在控制台访问test 输出 undefined,执行完后立即销毁

闭包

函数累加器(可以不依赖于全局变量)

1
2
3
4
5
6
7
8
9
10
11
12
function add() {
let count = 0;

function demo() {
count++;
console.log(count);
}
return demo;
}
var counter = add();
counter();
counter();

可以做缓存(存储结构)

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 1. 用对象返回两个函数
function test1() {
let food = "apple";
let obj = {
eat() {
if (food != "") {
console.log("I am eating " + food);
food = "";
} else {
console.log("There is nothing!");
}
},
pushFood(x) {
food = x;
}
}
return obj;
}
//用对象的形式返回出来,简化返回两个函数
let person = test1();

person.eat();
person.eat();
person.pushFood('banana');
person.eat();


// 2.用数组返回两个函数
function test2() {
let num = 100;

function a() {
num++;
console.log(num);
}

function b() {
num--;
console.log(num);
}
return [a, b];
}
let myArr = test2();
myArr[0]();
myArr[1]();

可以实现封装,属性私有化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Deng(name, wife) {
let prepareWife = 'xiaozhang';
this.name = name;
this.wife = wife;
this.divorce = function () {
this.wife = prepareWife;
}
this.sayPrepareWife = function () {
console.log(prepareWife);
}
}

let deng = new Deng('deng', 'xiaoliu');

console.log(deng.prepareWife); // undefined
console.log(deng.sayPrepareWife()); //xiaozhang

模块化开发,防止污染全局变量

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
let name = "xyz";

let init = (function () {
let name = "abc";

function callName() {
console.log(name);
}
return function () {
callName();
}
}());

let initDeng = (function () {
let name = "def";

function callName() {
console.log(name);
}
return function () {
callName();
}
}());

init();
initDeng();

闭包的问题

实例一

下面这段代码会打印出十个10

1
2
3
4
5
6
7
8
9
10
11
12
13
function test() {
let arr = [];
for (var i = 0; i < 10; i++) {
arr[i] = function () {
console.log(i);
}
}
return arr;
}
let myArr = test();
for (let j = 0; j < 10; j++) {
myArr[j]();
}

解决方法(将 var 改成 let 或者改成下面这段代码),会打印出 0 - 9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function test() {
let arr = [];
for (var i = 0; i < 10; i++) {
(function (x) {
arr[x] = function () {
console.log(x);
}
})(i)
}
return arr;
}
let myArr = test();
for (let j = 0; j < 10; j++) {
myArr[j]();
}

实例二

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
* {
margin: 0;
padding: 0;
}

ul {
list-style: none;
}

li:nth-of-type(2n) {
background-color: red;
}

li:nth-of-type(2n + 1) {
background-color: green;
}
</style>
</head>
<body>
<ul>
<li>a</li>
<li>a</li>
<li>a</li>
<li>a</li>
</ul>
<script type="text/javascript">
// 1. 闭包产生的索引皆为4

function test1() {
let liCollection = document.getElementsByTagName('li');

for (var i = 0; i < liCollection.length; i++) {
liCollection[i].onclick = function () {
console.log(i);
}
}
}

test1();

// 2. 解决后索引为0 1 2 3
function test2() {
let liCollection = document.getElementsByTagName('li');

for (var i = 0; i < liCollection.length; i++) {
(function (x) {
liCollection[x].onclick = function () {
console.log(x);
}
}(i))
}
}

test2();
</script>
</body>
</html>

this的经典题目

代码第一行 var 改成 let,运行情况不同……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var name = 222;
let a = {
name: 111,
say() {
console.log(this.name);
}
}

let fun = a.say;
fun(); //222
a.say(); //111

let b = {
name: "333",
say(fun) {
fun();
}
}

b.say(a.say); //222
b.say = a.say;
b.say(); //333
1
2
3
4
5
6
7
8
9
10
let foo = 123;

function print() {
// let this = Object.create(print.prototype)
this.foo = 234;
console.log(foo); // 123
console.log(this.foo); // 234
}
new print();
// foo此时指的是全局的foo,函数中的this不是指的全局