JavaScript Functions are also objects and as such, they also contain properties as name/value pairs. Since all JavaScript objects (with the only exception of Global object, "Object") have a prototype, all functions come with a prototype as well: Function.prototype.
To explore the object-side of functions, we first look at the name/value pairs that are associated with a given function. Next, we look at property attributes and object-attribute and see the default state of these property attributes and the object's extensible attribute.
We start by comparing a simple function as an object with Function.prototype. We provide an example to take a closer look at t he name/value pairs that is held by a function and the Function.prototype. This example uses Object.getOwnPropertyNames() to list all the properties that are owned by an object -- it excludes those properties that are inherited. The reason why we use Object.getOwnPropertyNames() is because it lists even non-enumerable properties; a simple for/in loop would skip any property that is non-enumerable. We will shortly in the next example that all of properties of a function and Function.prototype are non-enumerable!
<!doctype html> <html> <div id="idDiv"></div> <script type="text/javascript"> // Get a handle of the div element. var elem = document.getElementById("idDiv"); // Trivial function to print properties of an object. function printObject (obj) { var prop = Object.getOwnPropertyNames(obj); for (var item in prop) { elem.innerHTML += prop[item] + " -- " + obj[prop[item]] + "<br>"; } } // Define a dummy function that does very little! function funcCanDoMagic(doer) {return doer;} elem.innerHTML += "Printing Properties for funcCanDoMagic function object: <br>"; printObject(funcCanDoMagic); elem.innerHTML += "<br>Printing Properties for Function.prototype object: <br>"; printObject(Function.prototype); </script> </html>
We provide the output below. Note that the first two properties (arguments and length) characterize arguments passed to the function; the arguments property represents a list of all the arguments and the length property represents the number of arguments. In the above case, we have a single argument to this function viz. "doer" and therefore, the value of length is 1.
Next, let us now look at the property attributes and the object extensible attribute of a function and the Function.prototype. These attributes control various aspects of objects.
Each property of a JavaScript object has five elements: name, value, writable attribute, enumerable attribute, and configurable attribute. When writable attribute is true, then we can update the value of the property. When enumerable attribute is set to true, then the property becomes enumerable; for example, the for/in loop skips non-enumerable properties and so if we set the enumerable attribute to false and run the for/in loop, then we would not see that property in the loop. When configurable attribute is set to true, then we can configure the attributes of that property; if it is set to false, then we cannot change the writable or enumerable attributes.
Besides property attributes, the object also has an object-level attribute of extensible. This extensible attribute determines if we can add new properties to the object.
We provide an example that explores the value of these attributes for both functions and Function.prototype object. This will give us an insight into how tightly these properties are shipped as default.
<!doctype html> <html> <div id="idDiv"></div> <script type="text/javascript"> // Get a handle of the div element. var elem = document.getElementById("idDiv"); // Return property descriptors of the passed property. function getPropAttributes (obj, prop) { var x = "", i = 0; var attr = Object.getOwnPropertyDescriptor(obj, prop); for (var item in attr) { if (item == "value") {continue;} if (i++ != 0) {x += ", ";} // For Formatting x += (item + ": " + attr[item]); } return x; } // Trivial function to print an object. function printObject (obj) { var prop = Object.getOwnPropertyNames(obj); for (var item in prop) { elem.innerHTML += (prop[item] + " -- " + getPropAttributes(obj, prop[item]) + "<br>"); } elem.innerHTML += "Extensible attribute: " + Object.isExtensible(obj) + "<br>"; elem.innerHTML += "Object IsSealed: " + Object.isSealed(obj); elem.innerHTML += "<br>Object IsFrozen: " + Object.isFrozen(obj) + "<br>"; } // Define a dummy function that does nothing. function funcCanDoMagic() {} elem.innerHTML += "Printing Attributes for funcCanDoMagic function object: <br>"; printObject(funcCanDoMagic); elem.innerHTML += "<br>Printing Attributes for Function.prototype object: <br>"; printObject(Function.prototype); </script> </html>
Here is the output. Since all of these properties have enumerable set to "false", running a for/in loop would simply return no properties! Also, some of these properties have writable set to "false", we cannot modify their values. Lastly for for Function.prototype all of these properties have configurable set to "false" and that means, we cannot configure any of the properties of these properties. With all of these properties being false, the only reason Function.prototype is not sealed or frozen is because the extensible attribute of the object is set to true -- this means that both of these objects can be extended.
The output shows that the enumerable attributes of all the properties are false. Therefore, using a for/in loop to iterate through properties of a function or Function.prototype would list none of the above properties.
The output also shows that the extensible attribute for both are true and so if needed, we can easily add newer properties to a function or even the Function.prototype. Having said that, since Function.prototype is the prototype for all functions, we should avoid adding newer properties to this object.