向函数添加自定义属性
由于存在许多与我的关键字相关的其他问题,搜索合适的答案很困难,所以我会在这里问这个问题。
众所周知,JavaScript 中的函数是对象,它们有自己的属性和方法(更准确地说,是函数实例,继承自 Function.prototype)。
我正在考虑为一个函数(方法)添加自定义属性,让我们跳过“为什么?”部分并直接进入代码:
var something = {
myMethod: function () {
if (something.myMethod.someProperty === undefined) {
something.myMethod.someProperty = "test";
}
console.log(something.myMethod);
}
}
当使用 Firebug 的 DOM 浏览器检查时,该属性已按预期定义。然而,由于我不认为自己是 JavaScript 专家,所以我有以下问题:
- 这种方法可以被认为是“正确的”并且符合标准吗?它可以在 Firefox 中运行,但有很多东西在 Web 浏览器中可以按预期运行,但无论如何都不是标准。
- 这种通过向对象添加新属性来改变对象是一种好的做法吗?
Searching for appropriate answer proved difficult because of the existence of many other problems related to my keywords, so I'll ask this here.
As we know, functions in JavaScript are objects and they have their own properties and methods (more properly, function instances, inherited from Function.prototype).
I was considering adding custom properties for one function (method), let's skip the "why?" part and go straight to the code:
var something = {
myMethod: function () {
if (something.myMethod.someProperty === undefined) {
something.myMethod.someProperty = "test";
}
console.log(something.myMethod);
}
}
When inspected with Firebug's DOM explorer, the property is defined as expected. However, as I don't consider myself a JavaScript expert, I have the following questions:
- Can this method be considered "proper" and standards compliant? It works in Firefox but there are many things working as expected in web browsers and aren't by any means standards.
- Is this kind of altering objects by adding new properties to them a good practice?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
首先,重要的是要认识到标准函数属性(参数、名称、调用者和长度)不能被覆盖。因此,忘记添加具有该名称的属性吧。
可以通过不同的方式将您自己的自定义属性添加到函数中,这些方式应该适用于每个浏览器。
将您自己的自定义属性添加到函数
方法 1:在运行函数时添加属性:
方式 1(替代语法):
方式 1(第二种替代语法):
此策略的一个问题是您需要至少运行一次函数来分配属性。对于许多功能来说,这显然不是您想要的。因此,让我们考虑其他选项。
方式 2: 在定义函数后添加属性:
现在,您无需先运行函数即可访问属性。然而,一个缺点是你的属性感觉与你的功能脱节。
方式 3: 将函数包装在匿名函数中:
将函数包装在匿名函数中,您可以将属性收集到对象中,并使用循环在匿名函数中一一添加这些属性。这样,你的属性就会感觉与你的功能更加相关。当需要从现有对象复制属性时,此技术也非常有用。然而,缺点是您在定义函数时只能同时添加多个属性。此外,如果您经常想要向函数添加属性,那么它并不完全会产生 DRY 代码。
方法 4: 在函数中添加一个“扩展”函数,将对象的属性一一添加到自身:
这样,您可以随时扩展多个属性和/或从另一个项目复制属性。但是,如果您经常这样做,那么您的代码就不是 DRY。
方法 5: 创建一个通用的“扩展”函数:
遗传扩展功能允许采用更 DRY 的方法,允许您将对象或任何项目添加到任何其他对象。
方式6:创建一个extendableFunction对象并使用它来将扩展函数附加到函数上:
该技术允许您生成附加有“扩展”方法的函数,而不是使用通用的“扩展”函数。
方法 7: 将“扩展”函数添加到 Function 原型:
这种技术的一个很大的优点是,它使得向函数添加新属性变得非常容易、DRY 以及完全 OO。而且,它对记忆非常友好。然而,它的缺点是它不太适合未来。如果未来的浏览器向函数原型添加本机“扩展”函数,这可能会破坏您的代码。
方式 8: 递归运行一次函数,然后返回它:
运行一个函数一次并让它测试其属性之一是否已设置。如果未设置,则设置属性并返回自身。如果设置,则执行该函数。如果您包含“扩展”函数作为属性之一,则可以稍后执行该函数来添加新属性。
将您自己的自定义属性添加到对象
尽管有所有这些选项,我仍然建议不要向函数添加属性。给对象添加属性要好得多!
就我个人而言,我更喜欢具有以下语法的单例类。
这种语法的一个优点是它允许公共和私有变量。例如,这就是将“data”变量设置为 private 的方法:
但您说您想要多个数据存储实例?没问题!
最后,您可以分离实例和单例属性,并为实例的公共方法使用原型。这会产生以下语法:
使用这种语法,您可以拥有:
First of all, it's important to realise that standard function properties (arguments, name, caller & length) cannot be overwritten. So, forget about adding a property with that name.
Adding your own custom properties to a function can be done in different ways that should work in every browser.
Adding your own custom properties to a function
Way 1 : adding properties while running the function :
Way 1 (alternate syntax) :
Way 1 (second alternate syntax) :
A problem with this strategy is that you need to run your function at least once to assign the properties. For many functions, that's obviously not what you want. So let's consider the other options.
Way 2 : adding properties after defining the function :
Now, you don't need to run your function first before you're able to access your properties. However, a disadvantage is that your properties feel disconnected from your function.
Way 3 : wrap your function in anonymous function :
Wrapping your function in an anonymous function, you can collect your attributes into an object and use a loop to add those attributes one-by-one within the anonymous function. That way, your attributes feel more connected to your function. This technique is also very useful for when your attributes need to be copied from an existing object. A disadvantage, however, is that you can only add multiple attributes at the same time when you define your function. Also, it doesn't exactly result in DRY code if adding properties to a function is something you want to do often.
Way 4 : add an 'extend' function to your function, that adds the properties of an object to itself one by one :
This way, you can extend multiple properties and/or copy properties from another project at any time. Again, however, your code isn't DRY if this is something you do more often.
Way 5 : Make a generic 'extend' function :
A genetic extend function allows for a more DRY approach, allowing you to add the object or any project to any other object.
Way 6 : Create an extendableFunction object and use it to attach an extend function to a function :
Rather than using a generic 'extend' function, this technique allows you to generate functions that have an 'extend' method attached to it.
Way 7 : Add an 'extend' function to the Function prototype :
A great advantage to this technique is that it makes adding new properties to a function very easy and DRY as well as completely OO. Also, it's pretty memory friendly. A downside, however, is that it's not very future proof. In case future browsers ever add a native 'extend' function to the Function prototype, this that could break your code.
Way 8 : Run a function recursively once and then return it :
Run a function once and have it test whether one of its properties is set. If not set, set the properties and return itself. If set, execute the function. If you include an 'extend' function as one of the properties, you can later execute that to add new properties.
Adding your own custom properties to an object
In spite of all these options, I would nevertheless recommend against adding properties to a function. It's much better to add properties to objects!
Personally, I prefer the singleton classes with the following syntax.
An advantage to this syntax is that it allows for both public and private variables. For example, this is how you make the 'data' variable private :
But you want multiple datastore instances, you say? No problem!
Finally, you can seperate the instance and singleton properties and use a prototype for the instance's public methods. That results in the following syntax :
With this syntax, you can have :
对你的问题给出一个非常有意义的答案有点困难,因为你已经说过“这是我的解决方案,可以吗?”没有解释您要解决的问题(您甚至明确表示您不会解释“为什么”)。您的代码看起来是可以运行的有效 JavaScript,但它看起来也不是最佳的执行方式。
如果你解释你真正想要实现的目标,你可能会得到一些关于更好的方法来构建代码的好建议。不过,我还是会给你一些答案:
函数是对象(正如您所说),因此可以向它们添加属性。这实际上并不是一个标准问题,因为它是所有浏览器都支持的 JavaScript 的核心部分。
这是你的对象,你可以添加任何你喜欢的属性。对象的全部要点在于它们具有可以操作的属性。我真的无法想象一种使用对象而不涉及更改对象的方法,包括添加、删除和更新属性和方法。
话虽如此,对我来说,向
myMethod
函数添加属性并没有什么意义,更常见的是向something
对象添加其他属性(您的如果调用正确,myMethod
函数将可以通过this
关键字访问something
的其他属性。如果您将函数用作构造函数,则通常可以将方法添加到关联的原型并向每个实例添加(非方法)属性,但您可以这样做适当时采用其中一种或两种方式。 (请注意,“方法”本质上只是一个碰巧引用函数的属性。)
您显示的特定代码不会添加属性,它会测试
someProperty
属性是否已经 /em> 存在,如果存在则为其分配一个新值。您可能会受益于阅读 MDN 上的一些文章:
It's a little bit difficult to give a very meaningful answer to your question, because you've sort of said "Here is my solution, is it OK?" without explaining what problem you are trying to solve (you even said explicitly that you are not going to explain the "why"). Your code looks to be valid JavaScript that will run, but it also looks like a less than optimal way of doing things.
If you explain what you actually want to achieve you may get some good suggestions on better ways to structure your code. Still, I'll give you some kind of answer:
Functions are objects (as you've said), and thus it is possible to add properties to them. This isn't really a standards issue as such in that it is a core part of JavaScript that all browsers support.
It's your object, you can add whatever properties you like. The whole point of objects is that they have properties that you can manipulate. I can't really envisage a way of using objects that doesn't involve altering them, including adding, deleting and updating properties and methods.
Having said that, to me it doesn't really make sense to add properties to the
myMethod
function, it would be more usual to add other properties to yoursomething
object (yourmyMethod
function would, if called correctly, have access to the other properties ofsomething
via thethis
keyword).If you are using a function as a constructor it typically makes sense to add methods to the associated prototype and add (non-method) properties to each instance, but you can do either or both the other way when appropriate. (Noting that a "method" is essentially just a property that happens to reference a function.)
The specific code you have shown doesn't add properties, it tests whether the
someProperty
property already exists and if so assigns it a new value.You might benefit from reading some articles such as these at MDN:
“necromancing”在这里,但我认为每个伟大的问题都需要简单的答案:
是和是*
通过将属性附加到函数,您可以清理范围,改进可读性并增加逻辑凝聚力。另一个好处是您可以记录函数和变量之间的关系。我认为这是一个优秀的设计,比在作用域上添加变量要好得多
在这里和这里创建了一些有趣的示例。
此处
这里
* 我认为值得注意的是,您可能不会经常看到这种情况。大多数开发人员可能没有意识到这是可能的。有些人对每一点性能下降都着迷... “JavaScript 引擎基于以下内容进行优化物体的‘形状’……” 等等等等......
但我认为你可以遵循对象的规则,你会做得很好。
"necromancing" here, but I think every great question needs simple answers:
Yes and Yes*
By attaching the properties to the function you clean up the scope, improve readability and add logical cohesion. An added benefit is that you document the relationship between the function and the variables. I think that's a superior design, much better than adding variables on the scope
Created some fun examples here and here.
HERE
AND HERE
* I think it's worth noting that you probably won't see this very often. most developers probably don't realize it's possible. Some people are crazy about every drop of performance... "JavaScript engines optimize based on the 'shape' of an object'..." blah blah blah...
ut I think you can follow the rule you have for Objects and you'll do fine.
将属性附加到函数是一种重载
()
运算符的漂亮(可以说是缓慢/hack-ish)方式,而该方式通常用于实现 函子:具有一项非常重要的工作的对象类型,而其所有其他功能(如果有的话)只是一堆的帮手。您还可以将这些函子基本上解释为“有状态”函数,其中状态是公共的(例如,大多数内联函数具有私有状态,即来自本地范围的状态)。这个 JSFiddle 演示了如何将具有自定义属性的函数用于
translator
函数附加实用程序:如您所见,这对于以翻译为唯一目的的翻译人员来说是完美的。当然,这些类型的对象的例子还有很多,但到目前为止,它们并不像功能多样化的类型那么常见,例如经典的
User
、Animal
汽车
等类型。对于这些类型,您只想在极少数情况下添加自定义属性。通常,您希望将它们定义为更完整的类,并通过this
及其prototype
访问它们的公共属性。Attaching properties to functions is a beautiful (arguably sluggish/hack-ish) way of overloading the
()
operator, which in turn is usually used to implement functors: Object types that have one really important job, and all its other functionality (if there is any) is just a bunch of helpers. You could also interpret these functors as, basically, a "stateful" function where the state is public (most inline functions for example, have private state, that is state from the local scope).This JSFiddle demonstrates how we can use a function with custom properties for a
translator
function with additional utilities:As you can see, this is perfect for a translator whose sole purpose is to translate. Of course there are many more examples of theses types of objects, but they are by far not as common as types with diversified functionality, such as the classic
User
,Animal
Car
etc. types. To those sort of types, you only want to add custom properties in very few cases. Usually, you want to define those as more complete classes, and have their public properties reachable throughthis
and it'sprototype
.我意识到我已经晚了好几年了,但我想我应该添加这个例子——requirejs在define()函数上设置了一个名为“amd”的属性,这非常方便,因为UMD模式使用它来检测define () 作用域内的函数实际上是 AMD Define() 函数。
RequireJS 来源: http://requirejs.org/docs/release/2.1 .9/comments/require.js
显示此用法的 UMD 模式: https://github.com/umdjs/umd/blob/master/templates/amdWeb.js
I realize I'm years late to this, but thought I'd add this example--requirejs sets a property called "amd" on the define() function, which is quite handy as the UMD pattern uses it to detect that the define() function that's in scope is in fact an AMD define() function.
RequireJS source: http://requirejs.org/docs/release/2.1.9/comments/require.js
UMD pattern showing this usage: https://github.com/umdjs/umd/blob/master/templates/amdWeb.js
如果您只想向函数添加自定义属性,则只需将这些属性添加到 Function.prototype 即可。例如:
If you just want to add custom properties to a function then you only need to add those properties to Function.prototype. For example:
向函数对象添加属性或方法是完全可以接受的。这种事经常发生。 jQuery/$ 对象就是一个例子。这是一个附加了很多方法的函数。
当属性添加到构造函数时,它们被称为“静态”属性,并且可以在没有类实例的情况下调用。例如对象.create。
我没有足够的代表来写评论,所以我会在这里说:扩展内置对象的原型通常被认为是不好的做法,特别是当您的代码必须与其他人的代码一起使用时。它可能会产生难以预测且难以追踪的后果。
It's perfectly acceptable to add properties or methods to a function object. It's done quite often. The jQuery/$ object is an example of this. It's a function with quite a few methods attached.
When properties are added to a constructor they are called 'static' properties and can be invoked without an an instance of the class. e.g. Object.create.
I don't have enough rep to write a comment so I will say here: It generally considered bad practice to extend the prototypes of built in objects, especially if your code has to play with other people's code. It can have unpredictable consequences that are hard to to track.
我同意这是一个可能有多个答案的难题,所以我更喜欢做一个不同的例子:
让我们假设有一个由生成器填充的 JavaScript
Array
:那就是
现在我们想要映射这到一个新数组 - 相同的长度,应用一些函数,因此我们可以使用本机
map
函数属性:我们刚刚完成了
value=value+1
并返回,所以现在数组看起来像好的,现在应该有一个 JavaScript像这样的
Object
的定义与前面的数组类似(出于某些疯狂的原因):
即
此时我们无法应用相同的
map
方法,因为它不是为定义的Object
因此我们必须将其定义为Object
的新prototype
:此时我们可以像之前的数组一样进行操作:
这样我们就有:
您可以看到我们只更新了值:
然后我们可以转回输出数组:
它正在工作:
I agree that this is a difficult question that could have multiple answers, so I prefer to make an different example:
Let's suppose to have an JavaScript
Array
, populated by a generator:that is
Now we want to map this to a new array - same length, applying some function, so we could use the native
map
function property:We have just done a
value=value+1
and return, so now the array will look likeOk, now supposed to have a JavaScript
Object
likethat was defined like the previous array (for some crazy reason):
i.e.
At this point we cannot apply the same
map
method since it's not defined for anObject
so we have to define it as a newprototype
of anObject
:At this point we could do as for the array before:
so that we have:
You can see that we have updated the values only:
and we can turn back then into the output array:
Here it is at work:
约翰·斯莱格斯的可能补充很好的答案
在约翰·斯莱格斯之后是否有可能:
添加方式2.5
为了完整起见,直接在函数声明中放入“变量”属性和函数属性。从而避免它被“断开”。保留函数的内部默认工作方式(一个简单的循环)以表明它仍然有效。不?
Possible addition to John Slegers great answer
Isnt it possible that after John Slegers:
Adding a Way 2.5
Putting in both a "variable" property and a function property for completeness sake, straight in the function declaration. Thus avoiding it to be "disconnected". Left the inner default workings of the function (a simple loop) to show that it still works. No?
或者你必须使用 getter 和 setter
Alternatively you have to use getters and setters