前言

问题描述:

定义这样一个函数

function doRepeat(func, times, wait) {}

调用这个函数能返回一个新的函数,比如传入的是alert,这个函数的调用就是var repeatFunc = doRepeat(alert, 10, 5000);

调用返回的这个新函数,如:repeatFun(“hello world”);

那么hello world会显示10次,每次间隔5秒。

方法一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function doRepeat(func, times, wait) {
var count = 0;
var f = function(aa) {
setTimeout(function() {
func(aa);
count++;
if (count < times) {
f();
}
}, wait);
};
return f;
}

方法二:

1
2
3
4
5
6
7
8
9
10
11
12
13
function doRepeat(func, times, wait) {
var count = 0;
return function(aa) {
var id = setInterval(function() {
func(aa);
count++;
if (count == times) {
clearInterval(id);
}
}, wait);
};
}

分析:由于昨天刚看完闭包,所以第一眼看这题就想到用闭包的方式去做,之所以没答好的原因主要是因为,对setTimeout和setInterval这两个函数的使用不熟练,唉,js的东西实在太多了,后悔入坑。

变量提升

变量提升(Hoisting)被认为是,JavaScript中执行上下文工作方式的一种认识。从概念的字面意义上说,“变量提升”意味着变量和函数的声明会在物理层面移动到代码的最前面。实际上变量和函数的声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。

例:

1
2
3
num = 6;
num + 7;
var num

JavaScript仅提升声明,而不提升初始化。

1
2
3
var x = 1; // 声明 + 初始化 x
console.log(x + " " + y); // '1 undefined'
var y = 2; // 声明 + 初始化 y

ES6 : let 不存在 Hoisting。不过也不可以说不存在Hoisting,而是因为tmd的存在导致像是不存在Hoisting。

定义函数

函数声明方式:

1
2
3
function functionName(arg0, arg1, arg2) {
// 函数体
}

函数表达式方式:

1
2
3
4
5
var functionName = function(arg0, arg1, arg2) {
//函数体
}
var functionName = (function f(arg3) { // 函数体 });

匿名函数

上面的例子中函数表达式的第一种写法,这种情况下的函数叫做匿名函数。匿名函数有多重用法,上面把匿名函数赋值给了变量functionName(函数可以理解为指向函数对象的指针),这种情况下也叫做拉姆达表达式。

说明:

  • 使用函数表达式时注意函数声明提升。
  • 在匿名函数中定义的任何变量,都会在执行结束时被销毁。

匿名函数的典型用例:

帮助理解变量提升

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
写法一:
if (condition) {
function sayHi() {
alert("Hi!");
}
} else {
function sayHi() {
alert("Yo!");
}
}
写法二:
var sayHi;
if (condition) {
sayHi = function() {
alert("Hi!");
}
} else {
sayHi = function() {
alert("Yo!");
}
}

第一种写法下,声明式函数sayHi()会进行变量提升,导致第二个sayHi()函数覆盖第一个sayHi()函数。而第二个使用匿名函数的方式不会进行变量提升,可以很好的完成功能。

闭包

描述:闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createComparisonFunction(propertyName) {
return function(object1, object2) {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
};
}

要理解闭包的执行原理,就必须理解JavaScript中的执行环境、作用域链以及活动对象,全局变量对象,这里不表。

块级作用域

JavaScript没有块级作用域的概念(注意回想那个经典的面试例子变量i),不过可以使用闭包来实现块级作用域。

块级作用域的语法:

1
(function() {})();

一个例子:

1
2
3
4
5
6
7
8
9
function outputNumbers(count) {
(function () {
for (var i=0; i < count; i++) {
alert(i);
}
})();
alert(i);
}

运行时会报错,说明i在块级作用域中。

模块模式

所谓单例,指的就是只有一个实例的对象。JavaScript是以对象字面量的方式创建单例对象的。接下来是一段为单例创建私有变量和特权方法的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var singleton = function() {
var privateVariable = 10;
function privateFunction() {
return false;
}
return {
publicProperty: true,
publicMethod: function() {
privateVariable++;
return privateFunction();
}
};
}();

这种模式在需要对单例进行某些初始化,同时需要维护其私有变量时是非常有用的。

内容参考:
《JavaScript高级程序设计》
https://developer.mozilla.org/zh-CN/docs/Glossary/Hoisting