<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>throttle</title>
</head>
<body>
<div id="content" class="draggable"></div>
<script>
var getId=document.getElementById;
getId("content");
</script>
</body>
</html>
获取不到div对象:许多引擎的document.getElementById方法的内部实现中需要用到this,当getElementById方法作为document对象的属性调用时,方法内部的this确实指向document,但是引用document.getElementById后,就变成了普通函数调用,函数内部的this指向的window。
解决方法
document.getElementById=(function (func) {
return function () {
return func.apply(document,arguments);
}
})(document.getElementById);
var getId=document.getElementById;
getId("content");
当使用call或者apply的时候,如果我们传入的第一个参数为null,函数体内的this会指向默认的宿主对象,在浏览器中为this
变量生存周期
对于在函数内用var关键字声明的局部变量来说,当退出函数时,这些局部变量就失去了他们的价值,他们会随着函数调用的结束而被销毁。
var func=function () {
var a=1;
return function () {
a++;
console.log(a);
}
};
var f=func();
f();//2
f();//3
f();//4
f();//5
执行var f=func()时,f返回一个匿名函数的引用,它可以访问到func()被调用时产生的环境,而局部变量一直存在这个环境里。所以a的生命得以延续。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>throttle</title>
</head>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<script>
var nodes=document.getElementsByTagName("div");
for(var i=0,len=nodes.length;i<len;i++){
nodes[i].onclick=function () {
console.log(i);
}
}
</script>
</body>
</html>
无论点击哪个div,最后弹出的结果都是4,这是因为div节点的onclick事件是被异步触发的,当事件被触发的时候,for循环已经结束了。
解决办法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>throttle</title>
</head>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<script>
var nodes=document.getElementsByTagName("div");
for(var i=0,len=nodes.length;i<len;i++){
(function (i) {
nodes[i].onclick=function () {
console.log(i);
}
})(i);
}
</script>
</body>
</html>
闭包的应用
封装变量
延长局部变量的寿命
var extent=function () {
var value=0;
return {
call:function () {
value++;
console.log(value);
}
}
}
var extent=extent();
extent.call();
var extent={
value:0,
call:function () {
this.value++;
console.log(this.value);
}
}
extent.call();
var Extent=function () {
this.value=0;
}
Extent.prototype.call=function () {
this.value++;
console.log(this.value);
}
var extent=new Extent();
extent.call();
以上三种方式实现闭包。
高阶函数
高阶函数是指满足下列条件之一的函数
1. 函数可以作为参数被传递
2. 函数可以作为返回值输出
高阶函数的应用
函数柯里化:
currying又称部分求值,一个currying的函数首先会接受一些参数,接受这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。真正需要求值的时候,之前传入的参数一次性用于求值。
var monthlycost=0;
var cost=function (money) {
monthlycost+=money;
}
cost(100);
cost(100);
cost(100);
cost(100);
cost(100);
console.log(monthlycost);
var cost=(function () {
var args=[];
return function () {
if(arguments.length===0){
var money=0;
for(var i=0,l=args.length;i<l;i++){
money+=args[i];
}
return money;
} else {
[].push.apply(args,arguments);
}
}
})();
cost(100);
cost(200);
cost(300);
console.log(cost());
var currying=function (fn) {
var args=[];
return function () {
if(arguments.length===0){
return fn.apply(this,args);
} else {
[].push.apply(args,arguments);
return arguments.callee;
}
}
};
var cost=(function () {
var money=0;
return function () {
for(var i=0,l=arguments.length;i<l;i++){
money+=arguments[i];
}
return money;
}
})();
var cost=currying(cost);
cost(100);
cost(100);
cost(100);
console.log(cost());
闭包
当函数可以记住并访问所在的词法作用域时,就产生了闭包。即使函数是在当前词法作用域之外执行。
for(var i=1;i<=5;i++){
setTimeout(function timer() {
console.log(i);
},i*1000);
}
延迟函数的回调是在循环结束时才执行。当定时器在运行时即使每个迭代中执行的是setTimeout(…,0),所有的回调函数依然是在循环后才会被执行。因此每次都输出6.尽管循环中的五个函数是在各个迭代中分别定义的,但是他们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。
for(var i=1;i<=5;i++){
(function () {
setTimeout(function timer() {
console.log(i);
},i*1000);
})();
}
解决方法:
for(var i=1;i<=5;i++){
(function () {
var j=i;
setTimeout(function timer() {
console.log(j);
},j*1000);
})();
}
for(var i=1;i<=5;i++){
(function (j) {
setTimeout(function timer() {
console.log(j);
},j*1000);
})(i);
}