JavaScript闭包和链式调用的那些事
下面都是干货,涉及JavaScript高级开发的基础知识。如果有理解上的错误,欢迎指出。
最初级的闭包
下面是一个简单的计数器。
function Counter(initNum){
var num = parseInt(initNum);
return function() { num += 1; return num; };
}
var counter = new Counter(12);
console.log(counter());//12 + 1 = 13
console.log(counter());//13 + 1 = 14
console.log(counter());//14 + 1 = 15
console.log(counter());//15 + 1 = 16
我们可以看到储存当前次数的变量藏在函数Counter
内;函数Counter
内部返回一个匿名函数
,每一次调用counter()
都会执行一次这个匿名函数,其作用域在Counter下,实现对计数器counter2数字加1的功能。
稍作改进的计数器
function Counter2(initNum){
var num = parseInt(initNum);
this.get = function() { return num; }; //获取当前次数
this.add = function(){
return function() { num += 1; return num; }}; //num++
}
var counter2 = new Counter2(0); //initial
console.log(counter2.add()()); //1
console.log(counter2.get()); //1
console.log(counter2.add()()); //2
console.log(counter2.add()()); //3
console.log(counter2.get()); //3
我们可以看到,Counter2下的方法add
返回一个匿名函数,匿名函数内部同第一个例子,num
为Counter2内的局部变量,匿名函数的作用域还是在counter2下,所以一样实现了对计数器counter2数字加1的功能。
计数器中作用域的差异
下面的内容涉及this
的差异。请看Rebecca Murphey关于this关键字
的阐述:
In JavaScript, as in most object-oriented programming languages, this is a special keyword that is used within methods to refer to the object on which a method is being invoked. The value of this is determined using a simple series of steps:
- If the function is invoked using Function.call or Function.apply, this will be set to the first argument passed to call/apply. If the first argument passed to call/apply is null or undefined, this will refer to the global object (which is the window object in Web browsers).
- If the function being invoked was created using Function.bind, this will be the first argument that was passed to bind at the time the function was created.
- If the function is being invoked as a method of an object, this will refer to that object.
- Otherwise, the function is being invoked as a standalone function not attached to any object, and this will refer to the global object.
简单来说,就是**this
与执行环境有关,跟它在哪里定义并没有什么关系**。
var num = -999;
var Counter3 = {
num: 0,
add: function(){
return function() { this.num += 1; return this.num; }
}
};
console.log(Counter3.add()());
var Counter4 = {
num: 0,
add: function(){
var that = this;
return function() { that.num += 1; return that.num; }
}
};
console.log(Counter4.add()());
同是计数器,在浏览器中看看为啥效果不一样?这就涉及this
的作用域问题。
Counter3
的add
里面的this
执行环境为浏览器的Window
对象。因为console.log(Counter3.add()());
相当于console.log(function() { this.num += 1; return this.num; });
。console
所在域为Window
,所以this
指向Window
,故访问到的是全局变量num
。简而言之,this<=>Window。得结果**-998**。
Counter4
的add
里面的that
指的是执行是Counter4
本身。因为执行Counter4.add()时先Counter4
为add
方法的作用域,定义that
与this
执行环境为Counter4
自身。返回的因为闭包的可以共用return时所处环境的局部变量,所以匿名函数中访问的that
同var that = this;
中的that
。简而言之,this<=>that<=>Counter4。故得结果1。
By the way,如果在Node.js中,Counter3
得不到这个结果,因为在这里this指的是global
对象,只需要把that
去掉就可以在Node.js里面用啦。说白了就是Node.js好JavaScript的this
规则并不完全一样造成的。
#搭配Timer的计数器
第一款
采用function
构造的对象的计数器。
function Counter1(initNum){
var num = parseInt(initNum);
this.get = function() { return num; };
this.add = function(){ num += 1; return num; };
}
var counter1 = new Counter1(0);
setInterval(function(){
console.log(counter1.add());
}, 1000);
第二款
采用{}
构造的对象的计数器,跟闭包没什么关系。
var counter2 = {
num: 0,
add: function(){ this.num += 1; return this.num; }
};
setInterval(function(){
console.log(counter2.add());
}, 1000);
第三款
采用{}
构造的闭包同款的对象的计数器
var counter3 = {
num: 0,
add: function(){
var that = this;
return function(){
that.num += 1; return that.num;
}
}
};
setInterval(function(){
console.log(counter3.add()());
}, 1000);
链式调用
看了JavaScript 中的函数式编程实践这篇五年前的文章有感而发。随便写了个计算器,符合函数式编程的要求。
function Num(x){
this.add = function (b){ x = x + b; return this; };
this.sub = function (b){ x = x - b; return this; };
this.mul = function (b){ x = x * b; return this; };
this.div = function (b){ x = x / b; return this; };
this.rem = function (b){ x = x % b; return this; };
this.inc = function (){ x = x + 1; return this; };
this.dec = function (){ x = x - 1; return this; };
this.get = function (){ return x; };
this.equal = function (b){ return x == b; };
this.great = function (b){ return x > b; };
this.less = function (b){ return x < b; };
}
//链式操作
// (10 + (24 / 8) - 23) / 6 * 3 - 1
console.log(new Num(10).add(new Num(24).div(8).get()).sub(23).mul(6).div(3).dec().get());
// 10 % (24 / 8) == 3
console.log(new Num(10).rem(new Num(24).div(8).get()).equal(3));
看上去是挺复杂。这里也涉及到this
的作用域问题,前面两个例子没有斟酌出来的话可以看看这个。
除非注明,麦麦小家文章均为原创,转载请以链接形式标明本文地址。