前言
问题描述:
定义这样一个函数
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; console.log(x + " " + y); var y = 2;
|
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 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