JavaScript函数是对象吗?

16

我一直在苦恼一个 JavaScript 问题,但是我在网上找不到解释。我猜这是因为我没有输入正确的关键词,这也可能是我为什么一直苦恼的原因。

我的基本假设是可以修改对象:

> var x = {'n': 2};
> x['n']
2
> x['n'] = 3;
3

哎呀,这个可行。但是要记住(函数也是对象):

> var addn = function(a) {
    var n = 2;
    return n + a;
}

> addn(3);
5
> addn['n'] = 3;
3
> addn(3);
5
这一次我无法改变 'n'。有没有办法在保持函数式风格的同时修复这个问题?而不是完全使用面向对象编程。我还有一个相关的问题,如何维护函数的依赖关系,例如测试,但不使用面向对象方式?当然,我正在寻找解决方案,但如果可能的话,我也想了解JavaScript中哪个机制使我感到困扰。
谢谢,
马克
免责声明: 提及面向对象编程时,我并不打算反对面向对象编程。我也不打算反对VI或Emacs。如果我有些话使你不舒服,请跳过此内容。

1
你总是可以将n作为参数传递,但我猜这不是你想问的。 - Brandon Frohbieter
2
你是指什么意思_"这次我不能改变'n'"_?addn['n']返回的是3吗? - D.Shawley
关于你的OO评论:你正在尝试重新发明OO,这已经做得足够多了,甚至在JavaScript中也是如此。 ;) - Rosh Oxymoron
哇,感谢大家!不到一小时我们就完成了对这个主题的全面讨论,我非常印象深刻。这真的很有帮助。太棒了! - moin moin
8个回答

17

在函数作用域中,私有变量和对象属性是两个截然不同的概念。函数内部的 var n 变量是无法从函数外部访问的。

因此,在代码运行后,addn.n == 3,但每次函数运行时都会初始化一个不同的值给 var n,由于 JavaScript 的一些怪异特性,函数很难访问自己的属性。相反,更好的做法是通过传入参数 function(n, a) 来实现这种模式。

或者使用对象来实现类似的功能。

var adder = {
  n: 0,
  addn: function(a) {
    return this.n + a;
  }
};

adder.n = 5;
adder.addn(2); // 7

9

如果我理解你的问题正确的话,你可以给你的匿名函数起一个名字,并通过这个名字访问该函数对象的属性:

var addn = function func(a) {
  return func.n + a;
};

addn['n'] = 3;
addn(3); // returns 6

你可以通过在括号中将其包含起来来访问匿名函数的单个属性,而无需保留对它的引用,例如(function () { }).prototype - Cory Gross

2

对象属性和局部变量在很大程度上是无关的:

  • var n 声明了一个变量,它的作用域仅限于所在的函数内部(即在该函数之外不可见(除非通过闭包))。

  • addn['n'] 添加了一个名为n的属性到addn中,等同于addn.n


1
由于JavaScript具有函数作用域,您可以使用函数来存储n的值,如下所示:
var addn = (function(n) {
  return function(x) {
    n += x;
    return n;
  }
}(2);

addn(3) // 5
addn(3) // 8

1

首先,通过这样做你不会改变函数变量:

addn['n'] = 3;

你定义的函数没有名称(也称为“匿名函数”)。你只是将该函数赋值给一个名为addn的变量。变量没有任何属性-它只是一个容器(除非变量引用一个数组)。所以addn['n']返回空。

正如用户casablanca所指出的,你可以将函数命名为func,然后通过func.<propertyname>访问(并修改)其属性。


1

在Javascript的世界里,最好完全忘记传统的面向对象的“对象”概念,而是用闭包来思考。我强烈推荐阅读jQuery的创始人John Resig所写的本教程


从你提到的教程中: ` var num = 10;function addNum(myNum){ return num + myNum; }num = 15;assert( addNum(5) == 20, "Add two numbers together, one from a closure." ); ` 我认为这是一种方法,但有一件事我不喜欢,那就是 'num' 不再是 'addn' 对象的一部分。 - moin moin

1
每个函数都有一个作用域,基本上是函数被调用的地方/方式。当函数被调用时,它会创建一个新的函数执行上下文并将其推入调用栈;在语言读取该行/语句之前,该上下文中不存在任何内容,这只有在函数被调用时才会发生。
你所做的是给函数的属性赋值,而不是访问该作用域中的变量n。
不能从外部访问内部作用域,因为内部作用域对于外部作用域不存在但是你可以从内部作用域访问外部作用域,因为外部作用域对于内部作用域存在。
此外,这里有一些FYI。
JavaScript是基于原型的而不是基于类的。JavaScript中的所有东西都是对象,包括函数。阅读这篇文章以了解更多关于这个问题的原因(这是Quora上的一篇好文章) - https://www.quora.com/Why-is-function-an-object-in-Javascript

0

基本上,Javascript 中的所有东西都是对象。如果你说

var a=3;
a['n']=4;

对 'a' 的引用仍将返回 3,但 a 还有一个成员 'n',其值为 4。因此,当您说 addn['n'] = 3 时,您正在向 addn 添加一个新成员,并且不会以任何方式影响函数。

我强烈建议阅读 如何通过良好的 C 习惯来鼓励不良的 JavaScript 习惯。 在描述所有可能出错的事情时,它是了解 JavaScript 中对象工作方式的绝佳介绍。


为什么会被踩:并不是 JavaScript 中的所有东西都是对象。例如,在您自己的示例中,a 是一个“数字”,它是一种“原始”数据类型,而不是对象。你能够给它添加属性的原因是什么?因为当你执行 a['n'] 时,JavaScript 会在幕后为你创建一个临时的“Number”(“number”的包装类),然后在该行完成后立即将其处理掉。这对于能够在原始值上使用包装类方法可能很有用,但这并不意味着原始值是对象。请参见 https://dev59.com/GGox5IYBdhLWcg3wl1T4#56541867 和 https://dev59.com/GGox5IYBdhLWcg3wl1T4#9110389。 - aderchox

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接