据说是一不注意就会做错的五道javascript面试题之一,我们来看看这道题长什么样

function Container( properties ) {
var objthis = this;
for ( var i in properties ) {
(function(){
var t = properties[i];
objthis[ "get" + i ] = function() {return t;};
objthis[ "set" + i ] = function(val) {t = val;};
})();
}
} var prop = {Name : "Jim", Age : 13};
var con = new Container(prop);
console.log(con.getName()); con.setName("Lucy");
console.log(con.getName());
console.log(prop.Name);

现在请写出每次console.log的输出结果:()

A: Jim,Lucy,Lucy  B: Jim,Jim,Jim C : Lucy ,Lucy,Lucy D: Jim,Lucy,Jim

如果你选了D,排除运气的成份,你可以不用往下看了,因为可以确定你是资深老手了。

如果是选的别的选项,请继续看下面的分析。

首先大概扫瞄一下整个Container函数:函数的作用应当是想要给传进来的对象的每一个属性都添加一个get和 set 的方法。

下面开始从上至下的逐行分析:

function Container( properties ) {
var objthis = this; // 用objthis 保存对this的引用
for ( var i in properties ) { //循环properties中的内容
(function(){
//这里就是闭包所创建的空间
// 闭包内声明一个局部变量保存存properties中的值
var t = properties[i];
//在objthis对象上添加两个方法
objthis[ "get" + i ] = function() {return t;};
objthis[ "set" + i ] = function(val) {t = val;};
})(); //创建一个立即执行函数,目的是形成一个闭包,因为我们知道,闭包的作用是即使外层函数调用后,由于闭包引用的关系,外层函数不会被立即回收,因此闭包函数还可以继续使用外层函数所声明的变量和方法。
}
}
var prop = {Name : "Jim", Age : 13};
var con = new Container(prop);
console.log(con.getName()); // Jim con.setName("Lucy");
console.log(con.getName()); // Lucy

初看起来,没有什么问题,那这个坑到底在哪里呢?

如果我们继续打印 console.log(prop.Name); // Jim

是不是感到很惊讶?为什么 con.getName() 得到Lucy,而prop.Name 得到Jim ?

===============================================================

我们先从这个立即执行函数看起,如果省略它,那么t始终都是循环之后的值。这个立即执行函数就是为了解决这个问题的,所以这也没有什么问题。

然后就是这个objthis,它是Container实例的一个内部指针,和 properties 没有什么关系,但是properties 和prop是有关系的。因为prop是对象

是引用类型的,所以properties 可以看成是prop引用的一个副本(关于javascript的传参,后面再细说)。所以在Container内部,能过修改properties

是可以改变prop本身的,当然也可以访问到prop的属性。

我来看看

var con = new Container(prop); 这个con到底有什么内容

  1. getAge: function () {return t;}
  2. setAge: function (val) {t = val;}
  3. getName: function () {return t;}
  4. setName: function (val) {return t;}

看来con是根据prop的属性名,生成了对应的set/get方法,但是方法体内找不到properties的痕迹。里边只有一个t,而

t=properties[i], 这不就是prop.Name或prop.Age的值吗?我第一反应就是这个set方法有问题。t = val; 根据对象属性赋值的常识

var t = prop.Name, t = 'xxx', prop.Name的值是肯定不会改变的。这改变的仅仅是t自己。

con.setName("Lucy");
console.log(con.getName()); // Lucy
但是为什么这里又更改生效了呢?

我在这个地方郁闷了很久,我自己没有想清楚,最后还是请教了一位资深前辈才搞清楚。

原因很简单,因为con上的set和get方法,访问的都是闭包内的t的值。与properties没有半毛钱关系了。

con.setName("Lucy"); 相当于 t = "Lucy" ,而con.getName相当于 return t; 这是再普通不过的道理了。

这样也就很好的解释了,为什么 con.getName() 的值是Lucy,而prop.Name还是jim.

为什么我会产生那样的困惑呢?原因就在于没有搞清楚这个t,没有弄明白闭包的用法。那现在要怎么修改,才能实现Container最初的意图呢?

既然明白了con.setName修改的是自身的t,con.getName访问的也是自身的t,那么只人把这个t换成prop.Name就好了。

下面是我修改后的代码:

function Container( properties ) {
var objthis = this;
for ( var i in properties ) {
(function(){
var name = i;
objthis[ "get" + i ] = function() {return properties[name];};
objthis[ "set" + i ] = function(val) {properties[name] = val;};
})();
}
}

con.setName('frog')
console.log(prop.Name,con.getName());

// frog frog

======================================================================

关于javascript中的参数的传递。

根据我们c语言的知识,参数的传递有两种方式,按引用传递和按值传递。

但是javascript中所以的参数都是按值传递的。即便参数是对像也是如此。

function fun(a){

}

var o = {name:'frog'}

fun(o);

fun(o/*这里的o其实是复制了o对象的一个引用*/)

这不是本篇的重点,只是提一下。不明白的私信我或查阅相关资料。

从一道面试题分析javascript闭包的更多相关文章

  1. 写出float x 与“零值”比较的if语句——一道面试题分析

    写出float  x 与“零值”比较的if语句 请写出 float  x 与“零值”比较的 if 语句: const float EPSINON = 0.00001; if ((x >= - E ...

  2. 你不一定能做对的JavaScript闭包面试题

    由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) ...

  3. JavaScript 闭包 面试题

    <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8&quo ...

  4. Javascript闭包和C#匿名函数对比分析

    C#中引入匿名函数,多少都是受到Javascript的闭包语法和面向函数编程语言的影响.人们发现,在表达式中直接编写函数代码是一种普遍存在的需求,这种语法将比那种必须在某个特定地方定义函数的方式灵活和 ...

  5. JavaScript闭包(Closure)学习笔记

    闭包(closure)是JavaScript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. 下面就是我的学习笔记,对于JavaScript初学者应该是很有用的. 一.变量的作用域 要理解 ...

  6. 90%会搞错的JavaScript闭包问题

    由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) ...

  7. Javascript闭包深入解析及实现方法

    1.什么是闭包 闭包,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.闭包的特点:1. 作为一个函数变量的一个引用,当函数返回时 ...

  8. 全面理解Javascript闭包和闭包的几种写法及用途

    好久没有写博客了,过了一个十一长假都变懒了,今天总算是恢复状态了.好了,进入正题,今天来说一说javascript里面的闭包吧!本篇博客主要讲一些实用的东西,主要将闭包的写法.用法和用途.  一.什么 ...

  9. 一道面试题比较synchronized和读写锁

    一.科普定义 这篇博文的两个主角“synchronized”和“读写锁” 1)synchronized 这个同步关键字相信大家都用得比较多,在上一篇“多个线程之间共享数据的方式”中也详细列举他的应用, ...

随机推荐

  1. tf-idf知多少?

    1.最完整的解释 TF-IDF是一种统计方法,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度.字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反 ...

  2. python 通用 修饰器

    import functools def log(option): def dec(func): def swapper(*arg, **karg): functools.update_wrapper ...

  3. C#时间戳转换

    ,,)).ToUniversalTime ().Ticks ) / ;//先取得当前的UTC时间,然后转换成计算用的周期数(简称计时周期数),每个周期为100纳钞(ns)=0.1微秒(us)=0.00 ...

  4. 一个基于jQuery的移动端条件选择查询插件(原创)

    下载插件 目前给出的下载是混淆了后的代码 愿意一起探讨的可以找我要源码 使用方式: var ConditionsChoose = $("#Screening").Condition ...

  5. asp.net 使用DroDownList来实现二级联动

    今天做新闻发布系统的时候,用到了二级联动,我把使用方法记录下来,以便日后查阅以及帮助新手朋友们.下面是效果图: 下面来讲解一下实现的方法: 1.在.aspx页面中,拖入两个DroDownList控件. ...

  6. weblogic 12c web部署注意的问题

    废话不多说下面讲介绍他的具体部署web应用,应该注意哪些问题. 准备工作: Java JDK 安装及环境配置 http://jingyan.baidu.com/article/ff41162596a7 ...

  7. 在 Azure 上使用 Docker运行 Mono

    Docker 是最近相当热门的一个名词,它是一个基于 Linux Container 的轻量化的虚拟技术,而微软也相当积极与 Docker 合作,在 Azure 上支持这个火热的技术,并且提供简单的方 ...

  8. .NET面试题系列[12] - C# 3.0 LINQ的准备工作

    "为了使LINQ能够正常工作,代码必须简化到它要求的程度." - Jon Skeet 为了提高园子中诸位兄弟的英语水平,我将重要的术语后面配备了对应的英文. .NET面试题系列目录 ...

  9. ABP理论学习之内嵌资源文件

    返回总目录 本篇目录 介绍 创建内嵌文件 暴露内嵌文件 使用内嵌文件 介绍 在一个web应用中,有供客户端使用的javascript,css,xml等文件.它们一般是作为分离的文件被添加到web项目中 ...

  10. ReactJS入门(二)—— 组件的生命周期

    如果你熟悉avalon,使用过 data-include-rendered 和 data-include-loaded 等回调方法,那么你会很好地理解React组件的各个生命周期. 说白了其实就是Re ...