Javascript动态作用域

雾绡析取
• 阅读 2220

本文是在看《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库。用法如下

_.bind(globalThis,'abc')

这样的操作也是可以的。如果说有很多个函数都需要绑定到同一个对象上去怎么办呢?underscore提供了bindAll(obj,methondName)

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是会随着调用对象不同而变化的,我们可以通过其他名字来保存this

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

可以看到,虽然我们重新把getThis绑定到其他地方去了,还是能够得到我们的“小花”。如果我们再一次利用captureThis()函数来创建一个新的函数,绑定新的值不会影响到原来的“小花”

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以及闭包是实现函数式编程的基础,而正如我们看到的,函数式编程是一种安全而优美的编程方式~

点赞
收藏
评论区
推荐文章
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Karen110 Karen110
4年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
Karen110 Karen110
4年前
一篇文章带你了解JavaScript作用域
在JavaScript中,对象和函数也是变量。在JavaScript中,作用域是你可以访问的变量、对象和函数的集合。JavaScript有函数作用域:这个作用域在函数内变化。一、本地JavaScript变量一个变量声明在JavaScript函数内部,成为函数的局部变量。局部变量有局部作用域:它们只能在函数中访问。JS://codeherecann
Symbol卢 Symbol卢
4年前
秒懂js作用域与作用域链
JavaScript中有一个被称为作用域(Scope)的特性。虽然对于许多新手开发者来说,作用域的概念并不是很容易理解,本文我会尽我所能用最简单的方式来解释作用域和作用域链,希望大家有所收获!好了下面开始我们的正文作用域常见的解释(什么是作用域)1.一段程序代码中所用到的名字并不总是有效,而限定它的可用性的范围就是这个名字的作用域;2.作用域规定了
Python进阶者 Python进阶者
4年前
Windows环境下轻松搭建NodeJs服务器
大家好,我是皮皮,今天给大家分享一些好玩的前言Nodejs是GoogleV8引擎的一个JavaScript脚本语言,实际上也就是相当于服务器一样,可以解析网页内容并产生效果。它的出现令JavaScript如虎添翼,而且Node比JavaScript执行更为快速,并且支持分布式,因为它使用了事件驱动型的非阻塞式的模型。说太多反而没意思,不如我们自己搭建一个。
菜园前端 菜园前端
2年前
为你解惑JS作用域和作用域链知识
原文链接:变量作用域一个变量的作用域(scope)是程序源代码中定义这个变量的区域。全局变量拥有全局作用域,在JavaScript代码中的任何地方都是可以访问的。然而在函数内声明的变量只能在函数体内访问,它们是局部变量,作用域是局部性的。函数参数也是局部变
Jacquelyn38 Jacquelyn38
4年前
重学JavaScript第1集|变量提升
变量提升就好比JavaScript引擎用一个很小的代码起重机将所有var声明和function函数声明都举起到所属作用域(所谓作用域,指的是可访问变量和函数的区域)的最高处。这句话的意思是:如果在函数体外定义函数或使用var声明变量。则变量和函数的作用域会提升到整个代码的最高处,此时任何地方访问这个变量和调用这个函数都不会报错;而在函数体内定义函数或使用va
Stella981 Stella981
4年前
PHP代码静态分析工具PHPStan
<blockquote最近发现自己写的PHP代码运行结果总跟自己预想的不一样,排查时发现大多是语法错误,在运行之前错误已经种下。可能是自己粗心大意,或者说<codephpl</code检测太简单,不过的确是有一些语法错误埋藏得太深(毕竟PHP是动态语言),那么有没有办法,在代码代码正式运行之前,把语法错误全找出来呢?</blockquote<p
Stella981 Stella981
4年前
JavaScript易错知识点整理
前言本文是我学习JavaScript过程中收集与整理的一些易错知识点,将分别从变量作用域,类型比较,this指向,函数参数,闭包问题及对象拷贝与赋值这6个方面进行由浅入深的介绍和讲解,其中也涉及了一些ES6的知识点。JavaScript知识点1.变量作用域vara1;functio
Stella981 Stella981
4年前
Exceptionless
<divid"cnblogs\_post\_body"class"blogpostbodycnblogsmarkdown"<h1id"exceptionless.netcore开源日志框架"Exceptionless.NetCore开源日志框架</h1<blockquote<p作者:markjiang7m2<b
Stella981 Stella981
4年前
JavaScript的入门简介
什么是JavaScriptJavaScript,我们一般简称为JS,是一种具有函数优先的轻量级,解释型或即时编译型的编程语言。JavaScript现在已经被用到了很多非浏览器环境中,JavaScript基于原型编程、多范式的动态脚本语言,并支持面向对象、命令式和声明式风格。HTML、CSS、JavaScript三者不同的功能: