面试题:为什么要用javascript闭包面试题

由工作中演变而来的面试题
这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧。
先看题目代码:
function fun(n,o) {
console.log(o)
fun:function(m){
return fun(m,n);
var a = fun(0);
a.fun(3);//undefined,?,?,?
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
var c = fun(0).fun(1);
c.fun(3);//undefined,?,?,?
//问:三行a,b,c的输出分别是什么?
这是一道非常典型的JS闭包问题。其中嵌套了三层fun函数,搞清楚每层fun的函数是那个fun函数尤为重要。
可以先在纸上或其他地方写下你认为的结果,然后展开看看正确答案是什么?
//a: undefined,0,0,0
//b: undefined,0,1,2
//c: undefined,0,1,1
都答对了么?如果都答对了恭喜你在js闭包问题当中几乎没什么可以难住你了;如果没有答对,继续往下分析。
JS中有几种函数
首先,在此之前需要了解的是,在JS中函数可以分为两种,具名函数(命名函数)和匿名函数。
区分这两种函数的方法非常简单,可以通过输出&fn.name&来判断,有name的就是具名函数,没有name的就是匿名函数
注意:在低版本IE上无法获取具名函数的name,会返回undefined,建议在火狐或是谷歌浏览器上测试
或是采用兼容IE的获取函数name方法来获取函数名称:
* 获取指定函数的函数名称(用于兼容IE)
* @param {Function} fun 任意函数
function getFunctionName(fun) {
if (fun.name !== undefined)
return fun.
var ret = fun.toString();
ret = ret.substr('function '.length);
ret = ret.substr(0, ret.indexOf('('));
遂用上述函数测试是否为匿名函数:
可以得知变量fn1是具名函数,fn2是匿名函数
创建函数的几种方式
说完函数的类型,还需要了解JS中创建函数都有几种创建方法。
1、声明函数
最普通最标准的声明函数方法,包括函数名及函数体。
function fn1(){}
2、创建匿名函数表达式
创建一个变量,这个变量的内容为一个函数
var fn1=function (){}
注意采用这种方法创建的函数为匿名函数,即没有函数name
var fn1=function (){};
getFunctionName(fn1).//0
3、创建具名函数表达式
创建一个变量,内容为一个带有名称的函数
var fn1=function xxcanghai(){};
注意:具名函数表达式的函数名只能在创建函数内部使用
即采用此种方法创建的函数在函数外层只能使用fn1不能使用xxcanghai的函数名。xxcanghai的命名只能在创建的函数内部使用
var fn1=function xxcanghai(){
console.log("in:fn1&",typeof fn1,"&xxcanghai:&",typeof xxcanghai,"&");
console.log("out:fn1&",typeof fn1,"&xxcanghai:&",typeof xxcanghai,"&");
//out:fn1& function &xxcanghai:& undefined &
//in:fn1& function &xxcanghai:& function &
可以看到在函数外部(out)无法使用xxcanghai的函数名,为undefined。
注意:在对象内定义函数如var o={ fn : function (){&} },也属于函数表达式
4、Function构造函数
可以给&Function&构造函数传一个函数字符串,返回包含这个字符串命令的函数,此种方法创建的是匿名函数。
5、自执行函数
(function(){alert(1);})();
(function fn1(){alert(1);})();
自执行函数属于上述的&函数表达式&,规则相同
6、其他创建函数的方法
当然还有其他创建函数或执行函数的方法,这里不再多说,比如采用&eval&,&setTimeout&,&setInterval&等非常用方法,这里不做过多介绍,属于非标准方法,这里不做过多展开
三个fun函数的关系是什么?
说完函数类型与创建函数的方法后,就可以回归主题,看这道面试题。
这段代码中出现了三个fun函数,所以第一步先搞清楚,这三个fun函数的关系,哪个函数与哪个函数是相同的。
function fun(n,o) {
console.log(o)
fun:function(m){
先看第一个fun函数,属于标准具名函数声明,是新创建的函数,他的返回值是一个对象字面量表达式,属于一个新的object。
这个新的对象内部包含一个也叫fun的属性,通过上述介绍可得知,属于匿名函数表达式,即fun这个属性中存放的是一个新创建匿名函数表达式。
注意:所有声明的匿名函数都是一个新函数。
所以第一个fun函数与第二个fun函数不相同,均为新创建的函数。
函数作用域链的问题
再说第三个fun函数之前需要先说下,在函数表达式内部能不能访问存放当前函数的变量。
测试1,对象内部的函数表达式:
fn:function (){
console.log(fn);
o.fn();//ERROR报错
测试2,非对象内部的函数表达式:
var fn=function (){
console.log(fn);
fn();//function (){console.log(fn);};正确
结论是:使用var或是非对象内部的函数表达式内,可以访问到存放当前函数的变量;在对象内部的不能访问到。
原因也非常简单,因为函数作用域链的问题,采用var的是在外部创建了一个fn变量,函数内部当然可以在内部寻找不到fn后向上册作用域查找fn,而在创建对象内部时,因为没有在函数作用域内创建fn,所以无法访问。
所以综上所述,可以得知,最内层的return出去的fun函数不是第二层fun函数,是最外层的fun函数。
所以,三个fun函数的关系也理清楚了,第一个等于第三个,他们都不等于第二个。
到底在调用哪个函数?
再看下原题,现在知道了程序中有两个fun函数(第一个和第三个相同),遂接下来的问题是搞清楚,运行时他执行的是哪个fun函数?
function fun(n,o) {
console.log(o)
fun:function(m){
return fun(m,n);
var a = fun(0);
a.fun(3);//undefined,?,?,?
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
var c = fun(0).fun(1);
c.fun(3);//undefined,?,?,?
//问:三行a,b,c的输出分别是什么?
1、第一行a
var a = fun(0);
可以得知,第一个fun(0)是在调用第一层fun函数。第二个fun(1)是在调用前一个fun的返回值的fun函数,所以:
第后面几个fun(1),fun(2),fun(3),函数都是在调用第二层fun函数。
在第一次调用fun(0)时,o为undefined;
第二次调用fun(1)时m为1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1,n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;
第三次调用fun(2)时m为2,但依然是调用a.fun,所以还是闭包了第一次调用时的n,所以内部调用第一层的fun(2,0);所以o为0
第四次同理;
即:最终答案为undefined,0,0,0
2、第二行b
var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,?
先从fun(0)开始看,肯定是调用的第一层fun函数;而他的返回值是一个对象,所以第二个fun(1)调用的是第二层fun函数,后面几个也是调用的第二层fun函数。
在第一次调用第一层fun(0)时,o为undefined;
第二次调用 .fun(1)时m为1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1,n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;
第三次调用 .fun(2)时m为2,此时当前的fun函数不是第一次执行的返回对象,而是第二次执行的返回对象。而在第二次执行第一层fun函数时时(1,0)所以n=1,o=0,返回时闭包了第二次的n,遂在第三次调用第三层fun函数时m=2,n=1,即调用第一层fun函数fun(2,1),所以o为1;
第四次调用 .fun(3)时m为3,闭包了第三次调用的n,同理,最终调用第一层fun函数为fun(3,2);所以o为2;
即最终答案:undefined,0,1,2
3、第三行c
var c = fun(0).fun(1);
c.fun(3);//undefined,?,?,?
根据前面两个例子,可以得知:
fun(0)为执行第一层fun函数,.fun(1)执行的是fun(0)返回的第二层fun函数,这里语句结束,遂c存放的是fun(1)的返回值,而不是fun(0)的返回值,所以c中闭包的也是fun(1)第二次执行的n的值。c.fun(2)执行的是fun(1)返回的第二层fun函数,c.fun(3)执行的也是fun(1)返回的第二层fun函数。
在第一次调用第一层fun(0)时,o为undefined;
第二次调用 .fun(1)时m为1,此时fun闭包了外层函数的n,也就是第一次调用的n=0,即m=1,n=0,并在内部调用第一层fun函数fun(1,0);所以o为0;
第三次调用 .fun(2)时m为2,此时fun闭包的是第二次调用的n=1,即m=2,n=1,并在内部调用第一层fun函数fun(2,1);所以o为1;
第四次.fun(3)时同理,但依然是调用的第二次的返回值,遂最终调用第一层fun函数fun(3,1),所以o还为1
即最终答案:undefined,0,1,1
这段代码原本是在做一个将异步回调改写为同步调用的组件时的代码,发现了这个坑,对JS的闭包有了更深入的了解。
关于什么是闭包,网上的文章数不胜数,但理解什么是闭包还是要在代码中自己去发现与领悟。
如果要我说什么是闭包,我认为,广义上的闭包就是指一个变量在他自身作用域外被使用了,就叫发生了闭包。
希望读者能通过本文对闭包现象有进一步的了解,如有其它见解或看法,欢迎指正或留言讨论。
原文地址-/xxcanghai/p/4991870.html
阅读(...) 评论()你有必要知道的25个JavaScript面试题
作者:会飞的Pikachu
字体:[ ] 类型:转载 时间:
这篇文章主要为大家分享了我们有必要知道的25个JavaScript面试题。想要顺利通过面试的朋友可以参考一下
1、使用 typeof bar === "object" 判断 bar 是不是一个对象有神马潜在的弊端?如何避免这种弊端?
使用 typeof 的弊端是显而易见的(这种弊端同使用 instanceof):
let obj = {};
let arr = [];
console.log(typeof obj === 'object'); //true
console.log(typeof arr === 'object'); //true
console.log(typeof null === 'object'); //true
从上面的输出结果可知,typeof bar === "object" 并不能准确判断 bar 就是一个 Object。可以通过 Object.prototype.toString.call(bar) === "[object Object]" 来避免这种弊端:
let obj = {};
let arr = [];
console.log(Object.prototype.toString.call(obj)); //[object Object]
console.log(Object.prototype.toString.call(arr)); //[object Array]
console.log(Object.prototype.toString.call(null)); //[object Null]
另外,为了珍爱生命,请远离 ==:
而 [] === false 是返回 false 的。
2、下面的代码会在 console 输出神马?为什么?
(function(){
var a = b = 3;
console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));
这跟变量作用域有关,输出换成下面的:
console.log(b); //3
console,log(typeof a); //undefined
拆解一下自执行函数中的变量赋值:
所以 b 成了全局变量,而 a 是自执行函数的一个局部变量。
3、下面的代码会在 console 输出神马?为什么?
var myObject = {
foo: "bar",
func: function() {
var self =
console.log("outer func: this.foo = " + this.foo);
console.log("outer func: self.foo = " + self.foo);
(function() {
console.log("inner func: this.foo = " + this.foo);
console.log("inner func: self.foo = " + self.foo);
myObject.func();
第一个和第二个的输出不难判断,在 ES6 之前,JavaScript 只有函数作用域,所以 func 中的 IIFE 有自己的独立作用域,并且它能访问到外部作用域中的 self,所以第三个输出会报错,因为 this 在可访问到的作用域内是 undefined,第四个输出是 bar。如果你知道闭包,也很容易解决的:
(function(test) {
console.log("inner func: this.foo = " + test.foo); //'bar'
console.log("inner func: self.foo = " + self.foo);
如果对闭包不熟悉,可以参考本文:
4、将 JavaScript 代码包含在一个函数块中有神马意思呢?为什么要这么做?
换句话说,为什么要用立即执行函数表达式(Immediately-Invoked Function Expression)。
IIFE 有两个比较经典的使用场景,一是类似于在循环中定时输出数据项,二是类似于 JQuery/Node 的插件和模块开发。
for(var i = 0; i & 5; i++) {
setTimeout(function() {
console.log(i);
上面的输出并不是你以为的0,1,2,3,4,而输出的全部是5,这时 IIFE 就能有用了:
for(var i = 0; i & 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
而在 JQuery/Node 的插件和模块开发中,为避免变量污染,也是一个大大的 IIFE:
(function($) {
} )(jQuery);
5、在严格模式('use strict')下进行 JavaScript 开发有神马好处?
消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
消除代码运行的一些不安全之处,保证代码运行的安全;
提高编译器效率,增加运行速度;
为未来新版本的Javascript做好铺垫。
6、下面两个函数的返回值是一样的吗?为什么?
function foo1()
bar: "hello"
function foo2()
bar: "hello"
在编程语言中,基本都是使用分号(;)将语句分隔开,这可以增加代码的可读性和整洁性。而在JS中,如若语句各占独立一行,通常可以省略语句间的分号(;),JS 解析器会根据能否正常编译来决定是否自动填充分号:
var test = 1 +
console.log(test); //3
在上述情况下,为了正确解析代码,就不会自动填充分号了,但是对于 return 、break、continue 等语句,如果后面紧跟换行,解析器一定会自动在后面填充分号(;),所以上面的第二个函数就变成了这样:
function foo2()
bar: "hello"
所以第二个函数是返回 undefined。
7、神马是 NaN,它的类型是神马?怎么测试一个值是否等于 NaN?
NaN 是 Not a Number 的缩写,JavaScript 的一种特殊数值,其类型是 Number,可以通过 isNaN(param) 来判断一个值是否是 NaN:
console.log(isNaN(NaN)); //true
console.log(isNaN(23)); //false
console.log(isNaN('ds')); //true
console.log(isNaN('32131sdasd')); //true
console.log(NaN === NaN); //false
console.log(NaN === undefined); //false
console.log(undefined === undefined); //false
console.log(typeof NaN); //number
console.log(Object.prototype.toString.call(NaN)); //[object Number]
ES6 中,isNaN() 成为了 Number 的静态方法:Number.isNaN().
8、解释一下下面代码的输出
console.log(0.1 + 0.2); //0.00004
console.log(0.1 + 0.2 == 0.3); //false
JavaScript 中的 number 类型就是浮点型,JavaScript 中的浮点数采用IEEE-754 格式的规定,这是一种二进制表示法,可以精确地表示分数,比如1/2,1/8,1/1024,每个浮点数占64位。但是,二进制浮点数表示法并不能精确的表示类似0.1这样 的简单的数字,会有舍入误差。
由于采用二进制,JavaScript 也不能有限表示 1/10、1/2 等这样的分数。在二进制中,1/10(0.1)被表示为 0.…… 注意 0011 是无限重复的,这是舍入误差造成的,所以对于 0.1 + 0.2 这样的运算,操作数会先被转成二进制,然后再计算:
0.1 =& 0.01 1001…(无限循环)
0.2 =& 0.11 0011…(无限循环)
双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 0.1100…因浮点数小数位的限制而截断的二进制数字,这时候,再把它转换为十进制,就成了 0.00004。
对于保证浮点数计算的正确性,有两种常见方式。
一是先升幂再降幂:
function add(num1, num2){
let r1, r2,
r1 = (''+num1).split('.')[1].
r2 = (''+num2).split('.')[1].
m = Math.pow(10,Math.max(r1,r2));
return (num1 * m + num2 * m) /
console.log(add(0.1,0.2)); //0.3
console.log(add(0.15,0.2256)); //0.3756
二是是使用内置的 toPrecision() 和 toFixed() 方法,注意,方法的返回值字符串。
function add(x, y) {
return x.toPrecision() + y.toPrecision()
console.log(add(0.1,0.2)); //"0.10.2"
9、实现函数 isInteger(x) 来判断 x 是否是整数
可以将 x 转换成10进制,判断和本身是不是相等即可:
function isInteger(x) {
return parseInt(x, 10) ===
ES6 对数值进行了扩展,提供了静态方法 isInteger() 来判断参数是否是整数:
Number.isInteger(25) // true
Number.isInteger(25.0) // true
Number.isInteger(25.1) // false
Number.isInteger("15") // false
Number.isInteger(true) // false
JavaScript能够准确表示的整数范围在 -2^53 到 2^53 之间(不含两个端点),超过这个范围,无法精确表示这个值。ES6 引入了Number.MAX_SAFE_INTEGER 和 Number.MIN_SAFE_INTEGER这两个常量,用来表示这个范围的上下限,并提供了 Number.isSafeInteger() 来判断整数是否是安全型整数。
10、在下面的代码中,数字 1-4 会以什么顺序输出?为什么会这样输出?
(function() {
console.log(1);
setTimeout(function(){console.log(2)}, 1000);
setTimeout(function(){console.log(3)}, 0);
console.log(4);
这个就不多解释了,主要是 JavaScript 的定时机制和时间循环,不要忘了,JavaScript 是单线程的。详解可以参考 从setTimeout谈JavaScript运行机制。
11、写一个少于 80 字符的函数,判断一个字符串是不是回文字符串
function isPalindrome(str) {
str = str.replace(/\W/g, '').toLowerCase();
return (str == str.split('').reverse().join(''));
这个题我在 codewars 上碰到过,并收录了一些不错的解决方式,可以戳这里:Palindrome For Your Dome
12、写一个按照下面方式调用都能正常工作的 sum 方法
console.log(sum(2,3)); // Outputs 5
console.log(sum(2)(3)); // Outputs 5
针对这个题,可以判断参数个数来实现:
function sum() {
var fir = arguments[0];
if(arguments.length === 2) {
return arguments[0] + arguments[1]
return function(sec) {
return fir +
13、根据下面的代码片段回答后面的问题
for (var i = 0; i & 5; i++) {
var btn = document.createElement('button');
btn.appendChild(document.createTextNode('Button ' + i));
btn.addEventListener('click', function(){ console.log(i); });
document.body.appendChild(btn);
1、点击 Button 4,会在控制台输出什么?
2、给出一种符合预期的实现方式
<span style="color: #、点击5个按钮中的任意一个,都是输出5
<span style="color: #、参考 IIFE。
14、下面的代码会输出什么?为什么?
var arr1 = "john".split(''); j o h n
var arr2 = arr1.reverse(); n h o j
var arr3 = "jones".split(''); j o n e s
arr2.push(arr3);
console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1));
console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));
会输出什么呢?你运行下就知道了,可能会在你的意料之外。
&reverse() 会改变数组本身,并返回原数组的引用。
slice 的用法请参考:slice
15、下面的代码会输出什么?为什么?
console.log(1 + "2" + "2");
console.log(1 + +"2" + "2");
console.log(1 + -"1" + "2");
console.log(+"1" + "1" + "2");
console.log( "A" - "B" + "2");
console.log( "A" - "B" + 2);
输出什么,自己去运行吧,需要注意三个点:
多个数字和数字字符串混合运算时,跟操作数的位置有关
console.log(2 + 1 + '3'); / /‘33'
console.log('3' + 2 + 1); //'321'
数字字符串之前存在数字中的正负号(+/-)时,会被转换成数字
console.log(typeof '3'); // string
console.log(typeof +'3'); //number
同样,可以在数字前添加 '',将数字转为字符串
console.log(typeof 3); // number
console.log(typeof (''+3)); //string
对于运算结果不能转换成数字的,将返回 NaN
console.log('a' * 'sd'); //NaN
console.log('A' - 'B'); // NaN
这张图是运算转换的规则
如果 list 很大,下面的这段递归代码会造成堆栈溢出。如果在不改变递归模式的前提下修善这段代码?
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
nextListItem();
原文上的解决方式是加个定时器:
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
setTimeout( nextListItem, 0);
解决方式的原理请参考第10题。
17、什么是闭包?举例说明
可以参考此篇:从作用域链谈闭包
18、下面的代码会输出什么?为啥?
for (var i = 0; i & 5; i++) {
setTimeout(function() { console.log(i); }, i * 1000 );
请往前面翻,参考第4题,解决方式已经在上面了
19、解释下列代码的输出
console.log("0 || 1 = "+(0 || 1));
console.log("1 || 2 = "+(1 || 2));
console.log("0 && 1 = "+(0 && 1));
console.log("1 && 2 = "+(1 && 2));
逻辑与和逻辑或运算符会返回一个值,并且二者都是短路运算符:
逻辑与返回第一个是 false 的操作数 或者 最后一个是 true的操作数
console.log(1 && 2 && 0); //0
console.log(1 && 0 && 1); //0
console.log(1 && 2 && 3); //3
如果某个操作数为 false,则该操作数之后的操作数都不会被计算
逻辑或返回第一个是 true 的操作数 或者 最后一个是 false的操作数
console.log(1 || 2 || 0); //1
console.log(0 || 2 || 1); //2
console.log(0 || 0 || false); //false
如果某个操作数为 true,则该操作数之后的操作数都不会被计算
如果逻辑与和逻辑或作混合运算,则逻辑与的优先级高:
console.log(1 && 2 || 0); //2
console.log(0 || 2 && 1); //1
console.log(0 && 2 || 1); //1
在 JavaScript,常见的 false 值:
0, '0', +0, -0, false, '',null,undefined,null,NaN
要注意空数组([])和空对象({}):
console.log([] == false) //true
console.log({} == false) //false
console.log(Boolean([])) //true
console.log(Boolean({})) //true
所以在 if 中,[] 和 {} 都表现为 true:
20、解释下面代码的输出
console.log(false == '0')
console.log(false === '0')
请参考前面第14题运算符转换规则的图。
21、解释下面代码的输出
b={key:'b'},
c={key:'c'};
console.log(a[b]);
输出是456。
22、解释下面代码的输出
console.log((function f(n){return ((n & 1) &#63; n * f(n-1) : n)})(10));
结果是10的阶乘。这是一个递归调用,为了简化,我初始化 n=5,则调用链和返回链如下:
23、解释下面代码的输出
(function(x) {
return (function(y) {
console.log(x);
输出1,闭包能够访问外部作用域的变量或参数。
24、解释下面代码的输出,并修复存在的问题
var hero = {
_name: 'John Doe',
getSecretIdentity: function (){
return this._
var stoleSecretIdentity = hero.getSecretI
console.log(stoleSecretIdentity());
console.log(hero.getSecretIdentity());
将 getSecretIdentity 赋给 stoleSecretIdentity,等价于定义了 stoleSecretIdentity 函数:
var stoleSecretIdentity = function (){
return this._
stoleSecretIdentity
的上下文是全局环境,所以第一个输出 undefined。若要输出 John Doe,则要通过 call 、apply 和 bind 等方式改变 stoleSecretIdentity 的this 指向(hero)。
第二个是调用对象的方法,输出 John Doe。
25、给你一个 DOM 元素,创建一个能访问该元素所有子元素的函数,并且要将每个子元素传递给指定的回调函数。
函数接受两个参数:
指定的回调函数
原文利用 深度优先搜索(Depth-First-Search) 给了一个实现:
function Traverse(p_element,p_callback) {
p_callback(p_element);
var list = p_element.
for (var i = 0; i & list. i++) {
Traverse(list[i],p_callback); // recursive call
以上就是为大家分享的25个JavaScript面试题,希望对大家参加面试有所帮助。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具兴化yj 的BLOG
用户名:兴化yj
文章数:48
访问量:5866
注册日期:
阅读量:5863
阅读量:12276
阅读量:364595
阅读量:1059813
51CTO推荐博文
&id=&test&&& &&&&&&&这是第一条&& &&&&&&&这是第二条&& &&&&&&&这是第三条&& &&&&
点击每条li节点,弹出单签li节点的下标index
&script&type=&text/javascript&&&& &&&&&&lis&=&document.getElementsByTagName('li'); &&&&& &&&&&for(var&i=0;&i&lis.&i++)&{ &&&&&&&&&lis[i].onclick&=&function(num)&{ &&&&&&&&&&&&&return&function()&{ &//这里是闭包&&&&&&&&&&&&&&&&alert(num); &&&&&&&&&&&&&}; &&&&&&&&&}(i); &&&&&} &&&&/script&&&
如果直接写成:
&script&type=&text/javascript&&&& &&&&&&lis&=&document.getElementsByTagName('li'); &&&&& &&&&&for(var&i=0;&i&lis.&i++)&{ &&&&&&&&&lis[i].onclick&=&function()&{ &&&&&&&&&&&&&return&alert(i); &&&&&&&&&}; &&&&&} &&&&/script&&&
那么获取到的i是变量最后的一个值,也就是3.
了这篇文章
类别:┆阅读(0)┆评论(0)JavaScript(2)
&&&&&&& 你可能置身其中,但你真的懂得如何开发一系列JavaScript应用吗?你真的知道这个应用如何运行的吗?我不相信。不了解这个问题在面试中是很危险的一个信号。
&&&&&&& 你不仅应该知道闭包的实现原理,你还应了解为什么它很重要,并且你要能简单的回答几个使用闭包的用例。
&&&&&&& 在JavaScript中,闭包常用于数据隐私、事件处理、回调函数以及偏函数应用、柯里化和其他函数式编程范例。
&&&&&&& 面试官并不在乎你是否懂得“闭包”这个词或者它的定义。他想要知道面试者是否了解其原理。如果他们不了解,那么智能说明作为开发者,在实际开发JavaScript应用中缺少编程经验。
&&&&&&& 如果你无法回答此问题,你只是一个初级开发者。我并不会在意你编程了多久,哪怕10年。
&&&&&&& 快速跟进准备回答:你能说出闭包的两个常见用法吗?
什么是闭包?
&&&&&&& 简而言之,闭包是将一个函数和对周围状态的引用捆绑(enclosed)在一起的组合。在JavaScript中,闭包在每次一个函数创建时被创建,函数创建时。
&&&&&&& 这些是基本原理,但我们为什么要关心它?在实际中,因为闭包用括号“()”捆绑,它能让你从函数内部访问函数外部域。
&&&&&&& 要使用闭包,只需在另一个函数中定义一个函数,然后将它暴露给外部。要暴露一个函数,则返回它或将它传递给另一个函数。
&&&&&&& 函数内部有权限访问函数外部作用域中的变量,即使在函数外部已经return了。
使用闭包(示例)
&&&&&&& 之前说了,闭包通常用于对象的数据隐私。数据隐私是一个重要的属性,有助于我们编写接口,而不是一个实现。这是一个很重要的概念,有助于我们建立更强大更鲁棒的软件,因为实现细节比接口协议更容易改变。
&&&&&&& 在JavaScript中,闭包是用于数据隐私的主要机制。当你将闭包用于数据隐私时,所包含的变量只在包含(外)函数的范围内。除了通过对象的特权方法外,你不能从外部域中获得里面的数据。在JavaScript中,定义在闭包域内的任何暴露的方法是享有特权的。例如:
interface Obj {
get() =& A
o(secret: Any) =& Obj
const o = (secret) =& {
get: () =& secret
test(&#39;Closure for object privacy.&#39;,assert =& {
const msg = &#39;.get() should have access to the closure.&#39;;
const expected = 1;
const obj = o(1);
const actual = obj.get();
assert.ok(secret, &#39;This throws an error.&#39;);
} catch (e) {
assert.ok(true, `The secret var is only available
to privileged methods.`);
assert.equal(actual, expected, msg);
assert.end();
&&&&&&& 在上面的例子中,“.get()”方法被定义在“o()”域里面,并允许访问“o()”中的任何变量,它就是一个特权方法。例子中的参数为“secret”。
&&&&&&& 并不只有对象会产生数据隐私。闭包也可以用来创建有状态的函数,可以通过内部状态来影响其返回&#20540;。例如:
// secret(msg: String) =&getSecret() =& msg: String
const secret = (msg) =& () =&
test(&#39;secret&#39;, assert =& {
const msg = &#39;secret() should return a function that returns the passedsecret.&#39;;
const theSecret = &#39;Closures are easy.&#39;;
const mySecret = secret(theSecret);
const actual = mySecret();
const expected = theS
assert.equal(actual, expected, msg);
assert.end();
&&&&&&& 在函数式编程中,闭包常用于偏函数应用和柯里化。我们需要一些定义:
&&&&&&& 应用:将函数应用于其参数以产生返回&#20540;的过程。
&&&&&&& 偏函数应用:将函数应用于其参数的过程。其返回&#20540;为一个函数,并将其作为以后使用。换句话说,一个函数接收多个参数并返回一个带有参数的函数。偏函数在函数内部修正要返回函数的一个或多个输入参数,而返回函数使用这些修正后的参数作为自己的参数来完成整个函数的功能。
&&&&&&& 偏函数应用建立在闭包域内修正参数的方便之上。你可以写一个通用函数将参数部分应用到目标函数。这将具有以下特征:
partialApply(targetFunction: Function,...fixedArgs: Any[]) =&
functionWithFewerParams(...remainingArgs: Any[])
&&&&&&& 一个函数,需要任意数量的参数,而我们需要将部分参数应用于函数,并返回一个带有剩余参数的函数。
&&&&&&& 下面的例子可能有帮助理解:
&&&&&&& 你有一个两个&#20540;相加的函数。
const add = (a, b) =& a +
&&&&&&& 现在你想要在任意&#20540;上加10.我们命名这个函数为“add10()”。“add10(5)”的结果应该是“15”。
&&&&&&& 我们的“partialApply()”将让它实现:
const add10 = partialApply(add, 10);
&&&&&&& 在上面例子中,参数“10”是一个修正参数,它在“add10()”闭包域内被使用。
&&&&&&& 让我们来看下“partialApply()”的实现吧:
const partialApply = (fn, ...fixedArgs)=& {
return function (...remainingArgs) {
return fn.apply(this, fixedArgs.concat(remainingArgs));
&&&&&&& 正如你所见,它简单的返回一个带有能访问“fixedArgs”参数的函数,此函数我们通过“partialApply()”传入参数fn来实现。
现在轮到你了
&&&&&&& 你会怎么使用闭包呢?
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:10916次
排名:千里之外
原创:22篇
(1)(2)(1)(6)(3)(3)(5)(4)(4)

我要回帖

更多关于 经典js闭包面试题 的文章

 

随机推荐