闭包 this

执行上下文决定了变量作用域

闭包,它其实是一种决策,是一种模式,让我们可以灵活的改变变量作用域

按惯例,上栗子

var global = 'global';function outer(){    
var out = 'outer'; function middle(){
var mid = 'middle';
function inner(){
var in = 'inner';
console.log('globa : '+global, ',outer : '+out, ',middle : '+mid, ',inner : '+in); //globa : global outer : outer middle : middle inner : inner
}
inner();
console.log(in) //undefined
}
middle();
}
outer();
console.log(inner); //undefined console.log(middle); //undefined console.log(outer); //undefined console.log(global); //global

作用域

  • 抽象:不同的"函数调用"会产生不同的"执行上下文",不同的"执行上下文"划分出了不同的"变量作用域"。

  • 具体:咱们应该见过婚礼上的蛋糕,圆形的,一圈一圈的同心圆,中间最高,最外围最低。此处的"最高"和"最低"可以理解为访问权限,及里面能访问外面,而外面访问不了里面。

变量作用域

变量在inner函数中的作用域 = inner函数内部作用域 + 所有外层的作用域

变量在middle函数中的作用域 = middle函数内部作用域 + 所有外层的作用域 - inner函数内部

变量在outer函数中的作用域 = outer函数内部作用域 + 所有外层的作用域 - middle函数内部作用域

备注:以上前提是基于用var声明变量,省略var声明变量会导致变量提升!通过这个栗子可以初看出作用域的端倪

优点VS缺点

优点:

  • 合理的形成"管辖区",即"管辖区"内它能被访问到,"管辖区"外没这人

  • 不污染外层作用域

缺点

  • 因为受到了"管辖",导致有时需要访问它时却访问不到

闭包

引自阮一峰老师的博客 -- 学习Javascript闭包(Closure)

由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。

所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

只要咱们弄明白闭包,其中的this自然跑不掉。

上栗子

function constfuncs() {    
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs[i] = function () {
return i;
}
}
return funcs;
}
var funcs = constfuncs();
alert(funcs[1]());

这是最近的一个问题,对于funcs[1]()是几大家可以去试试

好吧,如果去试了可能会发现,无论你funcs[1]()中输入的时1还是9,它的都是10

这个就有意思了,为什么不论怎么输入,结果都是10呢?如果你发出了这个疑问,那么你的潜意识里肯定是弄错了件事:你认为

funcs[i] = function () {     
return i;
}

funcs[i]中的i会决定这个匿名函数中返回的i,其实不然。

for循环的过程中,会不停的创建函数

funcs[0] = function () {     
return i;
} //对象字面量被创建...
funcs[9] = function () {
return i;
} //对象字面量被创建

被创建的函数并没有被立刻执行,而是进入了等待队列,等待你的主动调用

于此同时,i在等于9后又执行了i++操作,现在i等于10

好的,现在咱们调用了funcs[1](),那么下一步函数会返回i,也就是10,所以无论你调用funcs[1]()还是funcs[9](),它都会返回10

现在改用闭包来解决这个问题了!

其实有一个值得玩味事情是:为什么遇到这样的问题,我们会用闭包解决?
换一种说法是:为什么闭包能解决这个应用场景的问题?

让我们在回顾一下那句话

在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

因为我们正好需要一座桥梁,将外部的i和内部的i关联起来。

上栗子

function constfuncs() {    

var funcs = [];    
for (var i = 0; i < 10; i++) {
funcs[i] = (function (i) { // 标记1
return function () { /
return i; // 标记2(上下三行)
}; /
})(i) // 标记3
} return funcs;
}var funcs = constfuncs();
console.log(funcs[1]()); - 标记2:我们在原本返回i的地方,返回了一个匿名函数,里面再返回了i
- 标记3:我们传入了i,架起了连接外部的桥梁
- 标记1:我们将标记3传入的i作为参数传入函数,架起了连接内部的桥梁

至此,每当一个for循环执行一次,i也会传入函数内部被保存/记忆下来。

再来一发

function constfuncs() {    
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs[i] = (function () {
return i;
}(i));
} return funcs;
}
var funcs = constfuncs();
console.log(funcs[1]); 在这个栗子中,由于我们改变了写法,导致最后的调用方法改变,但依旧是应用闭包的特性。

如果这个栗子懂了,那闭包应该懂了一大半了,如果还是有点晕,没关系,咱们继续往下看。

this

现在咱们说说闭包this之间的事

上栗子(浏览器/REPL中)

var name = 'outer'function Base(){}

Base.prototype.name = 'base';

Base.prototype.log = function () {    
var info = 'name is ';
console.log(this.name); // name is base function inner(){
console.log(info,this.name); // name is outer
};
inner();
};
var base = new Base();
base.log();

我们期望的是通过this访问原型对象中的name,可是最后却访问到全局对象中的name属性。

所以光有闭包还不够,我们需要借助点别的技巧,改写log函数

var name = 'outer'function Base(){}

Base.prototype.name = 'base';

Base.prototype.log = function () {    
var info = 'name is ';
var self = this; // 保存this function inner(){
console.log(info,self.name); };
inner();
};
var base = new Base();
base.log(); 注解:使用self或that变量来保存this是约定俗成

原因:
- 由于inner函数定义在了log函数内部,形成了闭包,导致内部this"泛滥"指向了全局对象,现在做的就是在this还没有"泛滥"的时候,保存它。

更常见的,是这样的改写log函数

var name = 'outer'function Base(){}

Base.prototype.name = 'base';

Base.prototype.log = function () {    
var info = 'name is ';
var self = this;
(function inner(){
console.log(info,self.name); })(self);
};
var base = new Base();
base.log(); 用一个"立即执行的函数表达式"代替函数创建和调用。

再来一枚经典栗子

var scope = "global";
var object = {
scope:"local",
getScope:function(){
return function(){
return this.scope;
}
}
}

相信大家对函数中的函数应该有一定的警惕性了,this.scope的值是谁大家应该也心中有值了,大家可以自己动手改一改,实践才是王道!

立即执行的函数表达式

最常见的版本大概是长这个样子:

var name = 'outer';

(function () {    
var name = 'inner';
console.log(name); // inner
console.log(this.name); // outer})();

相信大家看过上文后,应该都明白了为什么this.name会输出outer,下面来说说什么是立即执行的函数表达式

咱们分两步说:
- 立即执行
- 函数表达式

常见的创建函数有这两种

function Thing(){    
console.log('thing');
} //直接函数声明 Thing(); //函数调用 var thing = function () {
console.log('thing');
}; //函数字面量 thing(); //函数调用

不妨试试这样

thing()

你会发现函数神奇的执行了,也就是说函数名后面跟上一对小括号(),可以立刻调用函数。

那单独的那一行thing是什么呢?它是函数的名字,是一个指针,但是在这里被解析成了表达式,单独占了一行。

也就说我们通常执行函数都是这么搞的,那么万一这函数没有名字呢?我们可以这样

(function(){    console.log('no name');
})(); (function(){ console.log('no name')
}()); -function(){ console.log('no name');
}(); +function(){ console.log('no name');
}(); ~function(){ console.log('no name');
}(); !function(){ console.log('no name');
}();

除了最上面两个较常见外,其他的都挺怪异!但是他们都可以立即执行!

注意函数的前面都有一个符号,'+' , '-' , '~' , '!' , '()'这些符号告诉解析器强制把这些函数声明解析成函数表达式,最后的一对小括号()又让这函数表达式立即执行。

注意

如果要使用就请使用前两个,用小括号()的方式是最正规也是惯例,其他的方式容易导致自己或者他人误解,而且不符合编码规范,强烈不推荐使用,自己在练习的时候可以玩一玩,体会体会。

应用场景

1.你可以用立即执行的函数表达式暴露公开的成员方法

var cal = (function () {    
return { add: function (a,b) {
return a + b;
},
sub: function (a,b) {
return a - b;
}
}
})(); cal.add(5,2) // 7 cal.sub(4,1) // 3

或者

var cal = (function () {    
var way = {}; way.add = function (a,b) { return a + b;
};
way.sub = function (a,b) { return a - b;
}; return way;
})(); cal.add(3,6) // 9 cal.sub(8,5) // 3

2.续写子模块

cal.controller = (function () {    
var way = {};
var result;
way.set = function (args) {
result = args;
} way.get = function () {
return result;
}
return way;
})(); cal.controller.set(123);
cal.controller.get(); // 123

总结

  • 细说变量作用域,比较其优缺点

  • 举例说明闭包的概念,作用

  • 举例吐槽了闭包this之间的剧情,原因及解决方案

  • 细说了立即执行的函数表达式的概念及原理

  • 列举了其应用场景

  • javascript

  • 举报

JavaScript - this详解 (三)的更多相关文章

  1. JavaScript事件详解-jQuery的事件实现(三)

    正文 本文所涉及到的jQuery版本是3.1.1,可以在压缩包中找到event模块.该篇算是阅读笔记,jQuery代码太长.... Dean Edward的addEvent.js 相对于zepto的e ...

  2. JavaScript事件详解-Zepto的事件实现(二)【新增fastclick阅读笔记】

    正文 作者打字速度实在不咋地,源码部分就用图片代替了,都是截图,本文讲解的Zepto版本是1.2.0,在该版本中的event模块与1.1.6基本一致.此文的fastclick理解上在看过博客园各个大神 ...

  3. JavaScript正则表达式详解(一)正则表达式入门

    JavaScript正则表达式是很多JavaScript开发人员比较头疼的事情,也很多人不愿意学习,只是必要的时候上网查一下就可以啦~本文中详细的把JavaScript正则表达式的用法进行了列表,希望 ...

  4. JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

    二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...

  5. JavaScript事件详解-zepto的事件实现

    zepto的event 可以结合上一篇JavaScript事件详解-原生事件基础(一)综合考虑源码暂且不表,github里还有中文网站都能下到最新版的zepto.整个event模块不长,274行,我们 ...

  6. [原创]JavaScript继承详解

    原文链接:http://www.cnblogs.com/sanshi/archive/2009/07/08/1519036.html 面向对象与基于对象 几乎每个开发人员都有面向对象语言(比如C++. ...

  7. javaScript基础详解(1)

    javaScript基础详解 首先讲javaScript的摆放位置:<script> 与 </script> 可以放在head和body之间,也可以body中或者head中 J ...

  8. javascript设计模式详解之命令模式

    每种设计模式的出现都是为了弥补语言在某方面的不足,解决特定环境下的问题.思想是相通的.只不过不同的设计语言有其特定的实现.对javascript这种动态语言来说,弱类型的特性,与生俱来的多态性,导致某 ...

  9. javaScript笔记详解(1)

    javaScript基础详解 版权声明 本文原创作者:雨点的名字 作者博客地址:https://home.cnblogs.com/u/qdhxhz/ 首先讲javaScript的摆放位置:<sc ...

  10. 【HANA系列】【第二篇】SAP HANA XS使用JavaScript编程详解

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列][第二篇]SAP HANA XS ...

随机推荐

  1. opennebula onenebula

    http://www.eucalyptus.com/blog/2013/01/07/opennebula-38-%E2%80%94-%E7%9B%91%E6%8E%A7 [云监控] http://ww ...

  2. SynchronizationContext

    /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : ...

  3. Linux实战教学笔记28:企业级LNMP环境应用实践

    一,LNMP应用环境 1.1 LNMP介绍 大约在2010年以前,互联网公司最常用的经典Web服务环境组合就是LAMP(即Linux,Apache,MySQL,PHP),近几年随着Nginx Web服 ...

  4. Fog

    [Fog] Fog parameters are controlled with Fog command. Fogging blends the color of the generated pixe ...

  5. 前向渲染路径细节 Forward Rendering Path Details

    正向渲染路径细节 Forward Rendering Path Details Forward Rendering path renders each object in one or more pa ...

  6. 获取时间【NSDate】

    [Objective-C]NSDate详解及获取当前时间等常用操作 博客分类: Objective-C objective-cnsdate  NSDate类用于保存时间值,同时提供了一些方法来处理一些 ...

  7. Hbase-1.1.1-java API

    1.工具类 package com.lixin.stuty.hbase; import java.io.IOException; import org.apache.commons.configura ...

  8. Weblogic10.3.6部署解决CXF webService 调用报错: “Cannot create a secure XMLInputFactory”

    一,解决步骤 1.添加jar包 stax2-api-3.1.4.jar woodstox-core-asl-4.4.1.jar 2.编写监听器 package com.neusoft.cxf.list ...

  9. 集合_java集合框架

    转载自http://blog.csdn.net/zsw101259/article/details/7570033 Java集合框架图 简化图: Java平台提供了一个全新的集合框架.“集合框架”主要 ...

  10. 我理解的MVCC内部实现原理

    MySQL InnoDB存储引擎,实现的是基于多版本的并发控制协议——MVCC (Multi-Version Concurrency Control) (注:与MVCC相对的,是基于锁的并发控制,Lo ...