Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript闭包以及常见用法 #9

Open
gnipbao opened this issue Apr 15, 2017 · 1 comment
Open

JavaScript闭包以及常见用法 #9

gnipbao opened this issue Apr 15, 2017 · 1 comment

Comments

@gnipbao
Copy link
Owner

gnipbao commented Apr 15, 2017

说明

学过JavaScript这门语言的同学想必都听说过闭包一词, 闭包在日常开发中用的也是非常广泛的。可是当面试问你闭包是什么的时候,总感觉说不到点上。究竟什么是闭包呢?它是怎么起作用的?如何使用闭包去简化开发?如何用闭包提高性能以及如何用闭包解决作用域的问题?本文就为大家解开闭包的真面纱。

闭包

首先要知道什么是闭包就得知道闭包是怎么产生的。在javascript中只有函数存在作用域,并且函数可以被嵌套使用,当我们使用函数套用的时候就产生了一个有趣的现象,我们在子函数中可以任意访问外部函数中定义的变量和方法,但是外面的函数却得不到里面函数中定义的变量。也就是子作用域被封闭起来。那么问题来了,如何拿到子作用域里的变量呢?因此闭包出现了。也就是说闭包的唯一目的就是获取子作用域。
知道了闭包产生的目的后,我们来看看MDN对闭包的解释。

闭包是指能够访问自由变量的函数。换句话说,定义在闭包中的函数可以“记忆”它被创建时候的环境。

MDN解释的这么晦涩难懂,摆明不想让大家学会闭包。
来看看jQuery作者对闭包的定义吧:

a closure is the scope created when a function is declared that allows the
function to access and manipulate variables that are external to that function.
Put another way, closures allow a function to access all the variables, as well as other functions, that are in scope when the function itself is declared.

翻译成汉语其实就是闭包可以获取,操作内部作用域中定义的变量。
看完这段英语相信你已经对闭包有一定的感性理解。那么我们来从语法的角度分析一下闭包的组成。
它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。一说环境可能有点高深,我用数学表达来表述就是:闭包=函数(可匿名)+环境(作用域+局部变量+入参) 这下是不是很清晰了。

闭包的常见应用

  1. 模拟私有变量 (Private variable)

JavaScript 并不提供原生的支持,但是可以使用闭包模拟私有方法。私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分。这种方式也称为 模块模式

module import variant

 (function(window){
    //私有属性
    var privateThing;
    function privateMethod(){
    //私有方法
    }
    window.api = {
     //暴露公共接口
    }
 })(window)

module pattern

 var api = (function(){
    // Private and in-place!
    var local = 0;  
    //私有作用域
    function counter () {
      return ++local;
    }
    //暴露给api
    return {
      counter: counter
    };
  })();
  api.counter();
  // <- 1
  api.counter();
  // <- 2 

2.回调和计时器 (Callback and Timer)
在处理回调和计时器的时候我们可以使用闭包,这两个函数都需要异步被调用而且会很频繁的获取外部的数据.
异步回调(cb)最常见的是Ajax请求,例子就不举了用过jquery的同学都知道.

Timer

//这是一个60秒倒计时的计时器
var step = 0;
var timer = setInterval(function(){
   if(step<60){
      step++;
   }else{
     clearInterval(timer);
   }
},1000);

3.绑定函数作用域(Binding function contexts)

function bind(context,name){
return function(){
return context[name].apply(context,arguments);
};
}

4.参数归并技术以及函数柯里化 (Argument-merging technique and currying)
柯里化是指我们可以部分调用函数(返回我们预定义参数的函数),这个函数可以被以后调用.简单来说就是我们可以首先填充函数的一部分参数(并返回新的函数)这种技术通常被叫做函数柯里化。因为参数开始被填充了一部分到函数,后面一边通过返回新的函数里引用剩余的参数.所以形象的叫做参数归并技术。其本质是一样的。
举个例子:

Function.prototype.curry = function() {
var fn = this,
args = Array.prototype.slice.call(arguments);
return function() {
return fn.apply(this, args.concat(
Array.prototype.slice.call(arguments)));
};
};
Function.prototype.partial = function() {
var fn = this, args = Array.prototype.slice.call(arguments);
return function() {
var arg = 0;
for (var i = 0; i < args.length && arg < arguments.length; i++) {
if (args[i] === undefined) {
args[i] = arguments[arg++];
}
}
return fn.apply(this, args);
};
};

5.记忆和包裹函数(Memoization and Function wrapping)

Function.prototype.memoized = function(key){
this._values = this._values || {};
return this._values[key] !== undefined ?
this._values[key] :
this._values[key] = this.apply(this, arguments);
};
function isPrime(num) {
var prime = num != 1;
for (var i = 2; i < num; i++) {
if (num % i == 0) {
prime = false;
break;
on behavior 107
}
}
return prime;
}
//记忆
function memoizer(memo,formula){
 var recur = function(n){
    var result = memo[n];
    if (typeof result !== 'number'){
       result = formula(recur, n);
	memo[n] = result;
	}
     return result;
  };
  return recur;
}
//阶乘
var factorial = memoizer([1,1],function(recur,n){
 return n*recur(n-1);
})
//斐波那契数列
var fibonacci = memozier([0,1],function(recur,n){
 return recur(n-1)+recur(n-2);
})

wrap function

function wrap(object, method, wrapper) {
var fn = object[method];
return object[method] = function() {
return wrapper.apply(this, [fn.bind(this)].concat(
Array.prototype.slice.call(arguments)));
};
}

6.立即执行函数(IIFE)

几种立即执行的写法

(function() {
     // child scope
 })();
 
 !function () {
     // child scope
 }();
 
 +function () {
     // child scope
 }();
 
 -function () {
     // child scope
 }();
 
 ~function () {
     // child scope
 }();
 
 void function () {
     // child scope
 }();
 
 1^function () {
     // child scope
 }();
 
 1&function () {
     // child scope
 }();
@gnipbao gnipbao changed the title Javascript闭包以及常见用法 JavaScript闭包以及常见用法 Sep 19, 2017
@plh97
Copy link

plh97 commented Dec 13, 2018

闭包就是函数作用域链的延长

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants