Javascript動態做用域

本文是在看《Javascript函數式》編程一書寫下的一些記錄。和你們分享。不足之處還望你們指正。編程

關於this的討論

首先來看這麼幾段代碼安全

function globalThis(){return this;}

globalThis();
//=>window or global object

globalThis.call('haha');
//=>'haha'

globalThis.apply('abc',[]);
//=>'abc

能夠看到,this的通常就是由調用他的對象決定的,若是進行綁定了的話,至關於說這個函數的調用對象只可以是你綁定的那個對象,是不可以更改的。閉包

var bindThis = globalThis.bind('abc')

bindThis();
//=>'abc'

bindThis.call('x')
//=>'abc'

bindThis.apply('y',[]);
//=>'abc

固然,若是我看到這本書推薦你們使用underscore庫。用法以下app

_.bind(globalThis,'abc')

這樣的操做也是能夠的。若是說有不少個函數都須要綁定到同一個對象上去怎麼辦呢?underscore提供了bindAll(obj,methondName)ide

var buttonView = {
  label  : 'underscore',
  onClick: function(){ alert('clicked: ' + this.label); },
  onHover: function(){ console.log('hovering: ' + this.label); }
};
_.bindAll(buttonView, 'onClick', 'onHover');

函數的閉包

相信你們都或多或少踩過閉包這個坑吧,確實一開始接觸感受很不能理解。我我的粗淺理解是閉包是一個函數執行事後返回一個內部函數,這個內部函數將保留包含這個內部函數的函數的做用域鏈。也就是裏面把外面包住了,簡稱閉包2333。函數式編程

function captureOut(){
    var a = 123;
    return function(){
        console.log("a:"+a);
    }
}
var getA = captureOut();//獲取captureOut返回的匿名函數
getA();//這個匿名函數會保留本來的做用域鏈
//=>a:123

有意思的是函數參數也是能夠被咱們捕獲到的函數

function capturePara(PARA){
    return function(){
        console.log(PARA);
    }
}
var getP = capturePara("I'm the parameters");
getP();//I'm the parameters

這就給咱們靈活的創造一些函數提供了便利,好比咱們須要創造一個函數工廠,這個工廠能夠根據咱們提供的參數生產出不一樣的函數。見下面代碼測試

function createDivider(divFactor){
    return function(num){
        return num/divFactor
    }
}

var div9 = createDivider(9);//創造一個能夠用來除以9的函數
var div3 = createDivider(3);//除以3的
div9(81);//=>9
_.map([9,18,27],div3);//=>[3,6,9]

既然能夠訪問函數內部變量,那麼天然也能夠訪問this咯,但是this是會隨着調用對象不一樣而變化的,咱們能夠經過其餘名字來保存thisthis

function captureThis(NAME){
    this.name = NAME;
    var that = this;
    return function(){
        return that.name;
    }
}
var getThis = captureThis("小花");
getThis.call({});
//=>小花

能夠看到,雖然咱們從新把getThis綁定到其餘地方去了,仍是可以獲得咱們的「小花」。若是咱們再一次利用captureThis()函數來建立一個新的函數,綁定新的值不會影響到原來的「小花」code

var getHong = captureThis("小紅");
getHong.call({});
//=>小紅

剛剛咱們討論的都是內部變量和外部變量名字不一樣的狀況,若是相同會出現什麼現象呢?繼續往下看吧

var name = "大黃";
function captureName(name){
    return function(){
        return name;
    }
}
var getName = captureName("阿狗");
getName();//?

會領養到阿狗仍是大黃呢?其實這個還算簡單,返回的閉包就是返回原來的做用域鏈,首先訪問到的固然是最近的name,所以正確答案是「阿狗」(直接拷貝代碼到console就能夠測試)
再來看下面的例子

function captureName(name){
    return function(name){
        return name;
    }
}
var getName = captureName("阿黃");
getName("大狗");//=>?

這一次其實也差很少,相同的變量同時存在於外包函數參數和內部匿名函數的參數中,咱們仍是按照就近原則,最近的固然是內部匿名函數的參數,所以此次拿到的是「大狗」。

注意,只要拿到了閉包的返回函數,即使是修改原來外部的函數也不會對現有接收到的返回函數形成影響。好比說把captureName改成null,那麼照樣可使用getName。

不過下面的代碼可能讓你有些困惑

function showObj(obj){
    return function(){
        return obj
    }
}
var a = 10;
var showA = showObj(a);
showA();//=>10;
a=20;
showA();//=>10;


var b = {name:"daming"}
var showB = showObj(b);
showB();//{name:"daming"}
b.age =12;
showB();//{name:"daming",age:12}
b=null;
showB();//{name:"daming",age:12}

是否是以爲有點暈,我也有點暈。書上是說,「因爲引用對象同時存在於閉包內部和閉包外部,它的變化能夠跨越看似私有的界限,很容易致使混亂,因此一般都儘可能減小暴露捕獲變量的風險,把捕獲的對象做爲私有數據。」

var pingpong = (function(){
    var private=0;
    
    return{
        inc:function(n){
            return private+=n;
        },
        dec:function(n){
            return private-=n;
        }
    }
})();

pingpong.inc(3)//=>3

這樣對象是很安全的,甚至能夠禁止往閉包裏添加函數!

pingpong.showP = function(){return PRIVATE;}
pingpong.showP();//notdefined

總結:靈活利用this以及閉包是實現函數式編程的基礎,而正如咱們看到的,函數式編程是一種安全而優美的編程方式~

相關文章
相關標籤/搜索