闭包、定时器与BOM

JavaScript变量作用域有两种,分别是全局的作用域和函数作用域,JavaScript函数的局部变量可以直接读取全局变量。而函数外部变量并不能读取函数内的局部变量,但是通过闭包,可以在函数外部访问到内部的变量。

JavaScript作用域链:子对象会一级一级的向上查找父对象的变量,父对象的所有变量对子对象都是可见的,反之则不成立。

1
2
3
4
5
6
7
8
9
function (x) {
var local = 1
return function() {
console.log(x + local)
}
}
var bar = foo(2)
bar()

如上,

1
2
3
4
var local = 1
return function() {
console.log(x + local)
}

这就是一个闭包

即闭包指的是函数和函数内部能访问到的变量的总和。

闭包的作用:

  1. 暴露一个可以访问局部变量的函数
  2. 隐藏一个变量,不让其他人直接访问这个变量

闭包的应用场景

写一个函数判断是否首次加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function firstLoad() {
//声明一个_list,外部无法直接访问与修改_list
var _arr = []
return function(id) {
if (_arr.indexOf(id) >= 0) {
return false
} else {
_arr.push(id)
return true
}
}
}
var isFirstLoad = firstLoad()
isFirstLoad(10)
isFirstLoad(10)

修改代码让fnArr[i]() 输出 i。使用两种以上的方法

1
2
3
4
5
6
7
var fnArr = [];
for (var i = 0; i < 10; i ++) {
fnArr[i] = function(){
return i;
};
}
console.log( fnArr[3]() ); // 输出10

代码的输出结果为10

1
2
3
4
5
6
7
8
9
10
// 方法一
var fnArr = [];
for (var i = 0; i < 10; i++) {
!function(i){
fnArr[i]=function(){
return i;
}
}(i)
}
console.log(fnArr[3]()); //

1
2
3
4
5
6
7
8
9
10
// 方法二
var fnArr = [];
for (var i = 0; i < 10; i++) {
fnArr[i] = (function(i){
return function(){
return i;
}
}(i));
}
console.log(fnArr[3]()); //
1
2
3
4
5
6
7
8
// 方法三
var fnArr = [];
for (let i = 0; i < 10; i++) { //使用ES6的let语法
fnArr[i] = function () {
return i;
};
}
console.log(fnArr[3]()); //

封装一个汽车对象,可以通过如下方式获取汽车状态

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
var Car = (function () {
var speed = 0
function setSpeed(s) {
speed = s
}
function getSpeed() {
console.log(speed)
}
function accelerate() {
speed += 10
}
function decelerate() {
speed > 0 ? speed -= 10 : speed
}
function getStatus() {
if (speed <= 0) {
console.log("stop");
speed = null
} else if (speed > 0) {
console.log("running")
}
}
return {
setSpeed: setSpeed,
getSpeed: getSpeed,
accelerate: accelerate,
decelerate: decelerate,
getStatus: getStatus
}
})()
Car.setSpeed(30);
Car.getSpeed(); //30
Car.accelerate();
Car.getSpeed(); //40;
Car.decelerate();
Car.decelerate();
Car.getSpeed(); //20
Car.getStatus(); // 'running';
Car.decelerate();
Car.decelerate();
Car.getStatus(); //'stop';
//Car.speed; //error

下面这段代码输出结果是? 为什么?

1
2
3
4
5
6
7
8
9
var a = 1;
var a ;
setTimeout(function(){
a = 2;
console.log(a); //2
}, 0);
console.log(a); //1
a = 3;
console.log(a); //3

输出结果为1,3,2 因为setTimeout()会在程序的最后执行,前面声明a并赋值,重复声明不会改变a的值, setTimeOut异步

下面这段代码输出结果是? 为什么?

1
2
3
4
5
6
var flag = true;
setTimeout(function(){
flag = false;
},0) //原本会在程序的末尾执行,但由于while是个死循环,程序不会往下执行,故setTimeout也不会执行
while(flag){} //flag永远是true,死循环
console.log(flag); //不会执行

下面这段代码输出?如何输出delayer: 0, delayer:1...(使用闭包来实现)

1
2
3
4
5
6
7
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log('delayer:' + i);
}, 0);
console.log(i);
}
// 输出 0,1,2,3,4 delayer: 5, delayer: 5...

修改:

1
2
3
4
5
6
7
8
9
for (var i = 0; i < 5; i++) {
(function (i) {
setTimeout(function () {
console.log('delayer:' + i);
}, 0);
console.log(i);
})(i)
}
// 输出0,1,2,3,4 delayer: 0, delayer: 1, delayer: 2, delayer: 3, delayer: 4,

如何获取元素的真实宽高

使用window.getComputedStyle()方法

1
2
3
var ele = doutment.getElementById("test")
window.getComputedStyle(ele).width //获取id为test元素的宽
window.getComputedStyle(ele).height //获取id为test元素的高

URL如何编码解码?为什么要编码?

JavaScript提供四个URL的编码/解码方法。

  1. decodeURI()
  2. decodeURIComponent()
  3. encodeURI()
  4. encodeURIComponent()
    区别
  • encodeURI方法不会对下列字符编码

    1. ASCII字母
    2. 数字
    3. [email protected]#$&*()=:/,;?+’
  • encodeURIComponent方法不会对下列字符编码

    1. ASCII字母
    2. 数字
    3. ~!*()’

所以encodeURIComponent比encodeURI编码的范围更大。
之所以要进行编码,是因为URL中有些字符会引起歧义。

补全如下函数,判断用户的浏览器类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function isAndroid() {
return /Android/.test(window.navigator.userAgent)
}
function isIphone() {
return /iPhone/.test(window.navigator.userAgent)
}
function isIpad() {
return /iPad/.test(window.navigator.userAgent)
}
function isIOS() {
return /iOS/i.test(window.navigator.userAgent)
}