Javascript设计模式与开发实践(关于this/call/apply)

<!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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值