CNode

一个小小的JavaScript题目

分享
MMrErHu发布于10 年前最后回复9 年前18 回复6914 浏览0 收藏

今天前Leader在下班前发给我一道JavaScript的题目,看到感觉很不错,而且我开始的时候确实也理解错了,觉得有必要拿出来讲讲,并且为此我也坐错了地铁,哈哈哈~   题目是这样的:

var length = 10;
function fn() {
    console.log(this.length)
};
var obj = {
    length: 5, 
    method: function (fn) {
        fn();
        arguments[0]();
        fn.call(obj, 12);
    }
};
obj.method(fn, 1);

  请问输出是什么,当时那手机看的时候给出了答案

10 10 5

  实际上并不是如此,答案是:

10 2 5

  仔细分析了一下才知道原因,首先介绍一下四种this的类型:

  1. 默认绑定
  2. 隐式绑定
  3. 显示绑定
  4. new绑定

其中,默认绑定就是什么都匹配不到的情况下,非严格模式this绑定到全局对象window或者global,严格模式绑定到undefined;隐式绑定就是函数作为对象的属性,通过对象属性的方式调用,这个时候this绑定到对象;显示绑定就是通过applycall调用的方式;new绑定就是通过new操作符时将this绑定到当前新创建的对象中,它们的匹配有限是是从小到大的。   那么现在来解释一下:

obj.method(fn, 1);

  上述执行其实对应的是下面三条语句:

fn(); 
arguments[0]();
fn.call(obj, 12);

  通过将函数赋值给函数参数(fn),然后调用fn,这个时候能匹配到的this类型就是第一条默认绑定,因为实在非严格模式下,所以this绑定的是window,当然首先输出的是10。   先解释一下第三个,这个也很简单,因为用了call,所以实际匹配了显示绑定,所以当前this绑定了obj,那么输出的肯定是2。   下面着重解释一下第二个,这个我当时理解成和第一次完全一样,但实际并不是的,其实在JavaScript中数组算是一种特殊的对象(关于JavaScript对象的部分,我现在还在写,下一篇就会出的),arguments[0]其实就是通过对象的属性去调用(数组的默认属性类型是数值而普通对象的属性类型是字符串),那么现在其实运用的是规则2,this被绑定到arguments上,而arguments确实存在一个length属性,并且值为2(这个别告诉我你看不出来),所以输出的肯定就是2啦。   可见《你不知道的JavaScript》这本书说的很对,之所以会在ES6出现箭头函数,实质就像用词法作用域代替this,因为这个真的特别特别容易误用和让人误解。  欢迎大家到我的个人博客看看,https://mrerhu.github.io 😄

查看回复

回复 (18)

L
libook#1·10 年前

这就是为什么不要用var。。。

Y
yongningfu#2·10 年前

其实这道题解释起来并没有那么复杂,虽然一开始我也做错了,arguments对象为 {"0": fn, "1": 1, length: 2} 调用 arguments0 < ==> arguments["0"] () 即对象调用它的属性而已 自然 this就指向了arguments 其实跟 数组算是一种特殊的对象 没什么关系

注: 由于数字属性不能用.调用 不然的话, 上面可以写成 arguments.0() 这样的话,大家就比较好理解了 this的指向了

F
fruit-memory#4·9 年前

第二个的二哪来的?

D
dbit-xia#5·9 年前
引用 fruit-memory第二个的二哪来的?

@fruit-memory 应该是 arguments.length===2

X
xcatliu#6·10 年前

学习了。不过 arguments[0]() 大概永远不会有人这么写代码吧。

Z
zbinlin#7·10 年前

如果可以写成这样:arguments.0() 相信就不会做错了 :)

B
bajian#8·10 年前

好文章,谢谢分享!

M
MrErHu#9·10 年前
引用 xcatliu学习了。不过 arguments0 大概永远不会有人这么写代码吧。

@xcatliu 哈哈,确实是的,但是这个确实也是JavaScript容易让人忽视的一个点。面试说不定会考到的,😁

M
MrErHu#10·10 年前
引用 zbinlin如果可以写成这样:arguments.0() 相信就不会做错了 :)

@zbinlin 对,而且那个length属性也是具有迷惑性的

M
MrErHu#11·10 年前
S
SinalVee#12·10 年前

先解释一下第三个,这个也很简单,因为用了call,所以实际匹配了显示绑定,所以当前this绑定了obj,那么输出的肯定是2。

写错啦,输出是5

Y
youth7#13·10 年前

楼主说错了1个地方 arguments并不是数组,而是一个类似数组的对象(MDN上面的解释:The arguments object is not an Array. It is similar to an Array, but does not have any Array properties except length. )。 通过下标访问与通过.访问其实本质是等价的 var a = [function(){console.log(this.length)}] a[0] ()

这里输出1,让我迷惑了一下,仔细一想又是合理的,js真是奇妙。习惯了静态强类型语言(例如c和java)的人,一时真没反应过来。

M
magicdawn#14·10 年前
  • pls 'use strict' , use strict修复了 this 默认指向 global 的问题, 使行为更加 consistant
  • 这种题得先写执行环境, 浏览器devtool / node ? 不一样的, node 会将内容包在 module wrapper 里
M
MrErHu#15·10 年前
引用 youth7楼主说错了1个地方 arguments并不是数组,而是一个类似数组的对象(MDN上面的解释:The arguments object is not an Array. It is simila...

@youth7 是的,确实arguments是一个类数组,不是真实的数组,当时主要是为了表达是对象的一种而说的。

H
huruji#17·10 年前

mark,学习了

M
MrErHu#18·9 年前

??

参与回复
登录后即可参与回复。登录