I'm learning how one can achieve OOP patterns in JavaScript. I'd like to know, which one is a correct way to assign a DOM element to a object literal member in pure JavaScript and what are the differences between those examples.
I am doing this so that I could reuse that DOM element in object literal functions and if I change any id, name or class names, I only have to update in one place.
MyObject = {
// Version 1
member: document.getElementByName('elementName'),
// Version 2
member2: function() {
return document.getElementByName('elementName');
},
// Version 3
member3: function() {
document.getElementByName('elementName');
}
};
MyObject2 = {
// Is this member in a different namespace
member: document.getElementByName('element2Name'),
};
// Is this member in a different namespace
member: document.getElementByName('element2Name'),
MyObject2.member and MyObject1.member are different. Javascript doesn't natively support namespace like in other languages but the same can be achieved using the Object literals.
Regarding the 3 different versions for the member assignment, the deciding factor is what kind of access do you need for your variable and how you want to consume that.
// Version 1
member: document.getElementsByName('elementName'),
This assigns the member property the result of the method getElementsByName, The result is a NodeList Collection. To access the property member you need to write it like MyObject.member.
// Version 2
member2: function() {
return document.getElementByName('elementName');
},
The member2 is a function, whereas member wasn't a function and hence how the invocation is done is different. In this case you can get the same result as the version1 by calling MyObject.member2().
// Version 3
member3: function() {
document.getElementByName('elementName');
}
Version 3 doesn't return anything and is useless if you need to consume the result. Basically, this version never stores the result of the function call document.getElementByName('elementName'); and hence the return value is undefined. Like member2, member3 is also a function. But, invoking the function MyObject.member3() returns undefined.
Which version to choose
version 3 is useless as it doesn't return anything.
I do not have enough information of your application to suggest you best match. Based on limited information available, I would prefer version 2 because of the following reasons
Since the value of the result which is defined by document.getElementByName('elementName'); changes and is dependent on DOM. So, i will go ahead with the member2 or version2. i generally prefer function whenever it's not simple and involve some computation. Also, the result of the function call gives the caller information that the result of function call can change. Properties are good when you can define simple data attribute.
Note: A property's value can be a function, in which case the property is known as a method.
Related
When instance function are called in a class each instance of object get own copy of function but in prototype method and static method no copy is created , they belong to class, so if they both doesn't create a copy of their function, so why do we have static function if we can simply use prototype method if we don't want to copy??
I am a little bit confuse, if anyone can explain it will be a great help
In order to use a prototype/instance method, you need to either have an instance of an object or specifically access the type's .prototype. For methods that don't require an instance, a static method provides simpler syntax. Think of the String.fromCharCode() method as an example. It wouldn't make sense to say:
let str = "dummy string".fromCharCode(127);
The extra string instance there is just a distraction from what you're really trying to do:
let str = String.fromCharCode(127);
This applies good programming practices of reduced coupling (not requiring an instance in order to invoke a method that doesn't need it) and information hiding (by not exposing a method on instances of objects which doesn't pertain to those specific objects).
A static method does not exist on instances. Prototype methods do. So, if you want to call someArr.filter(x => x > 5) that would be an instance method that works on the given array.
An example of astatic method is Array.isArray(someArr). It makes very little sense to make the static method an instance method because you'd need an instance before calling it. That would lead to code like someArr.isArray(someArr) which is illogical - you need an array to check if something is an array. And that can very easily be fail spectacularly if someArr is not in fact an array:
const someArr = {
isArray() { return true; },
filter() { return "I am not an array"; },
};
console.log(someArr.isArray(someArr));
console.log(someArr.filter(x => x > 5));
Yes, that example is indeed highly illogical in order to highlight why it is weird. Assuming .isArray() was an instance method, you could create a new array in order to use it to call [].isArray(someArr). But that method does not require any instance data. The object created exists only to give you access to the method and is discarded immediately afterwards. That design is still not sensible.
Both static methods and prototype methods exist independent from any instances. The difference is that a prototype method expects to be called on an instance, i.e. to have an instance passed as the this argument, whereas the static method does not require an instance and expects none.
Even without placing them anywhere on the class, we can see this distinction in the following example:
function myStaticMethod(arg) {
console.log('Doing something with '+arg);
}
function myMethod(arg) {
console.log('Doing something with '+this+' and '+arg);
}
const myInstance = new MyClass();
myStaticMethod('value');
myMethod.call(myInstance, 'value');
Now the .call() syntax is not very ergonomic, we prefer myInstance.myMethod('value'), so that is why we place the method on the prototype object of the class, having it inherited by all instances.
For static methods, this is not necessary. They don't need an instance, we don't want to call them on an instance, we want to call them as MyClass.myStaticMethod('value') so that is where we place them. We could put them on the prototype as well, but that would lead to confusion (myInstance.myStaticMethod()), name collisions, and unncessarily long invocations (MyClass.prototype.myStaticMethod()). It's imaginable to write new MyClass().myStaticMethod(), but there you would unnecessarily create an instance that is not required (and it might not even be possible to create).
Object.prototype.getB = function() {
// how to get the current value a
return a.b;
};
const a = {b: 'c'};
a.getB();
As you can see, I want to make a function to all Object value. And I need to get the object value in this function then do something.
Monkey Patching
What you want to do is called monkey patching — you mutate a built-in prototype.
There are many wrong ways to do it, and it should be avoided entirely, because it has negative Web compatibility impacts.
I will, however, demonstrate how this can be done in a way that matches existing prototype features most closely.
In your case, the function body should return this.b.
In functions called as methods, you can get the object itself with the this keyword.
See How does the "this" keyword work? (section 4: “Entering function code”, subsection “Function properties”) for more details.
You correctly added the method to Object.prototype.
See Inheritance and the prototype chain for more details.
The tools
There is a number of tools involved when reasoning about monkey-patching:
Checking own property existence
Using property descriptors
Using the correct function kind
Getters and setters
Let’s assume you’re trying to implement theMethod on TheClass.
1. Checking own property existence
Depending on your use case you may want to check if the method you want to introduce already exists.
You can do that with Object.hasOwn; this is quite a new method, but in older environments it can simply be replaced by Object.prototype.hasOwnProperty.call.
Alternatively, use hasOwnProperty normally, but be aware that if you or someone else monkey-patched the hasOwnProperty method itself, this may lead to incorrect results.
Note that in does not check for own properties, exclusively, but for inherited properties as well, which isn’t (necessarily) what you want when you’re about to create an own property on an object.
Also, note that if(TheClass.prototype.theMethod) is not a property existence check; it’s a truthiness check.
Code samples
if(Object.hasOwn(TheClass.prototype, "theMethod")){
// Define the method.
}
if(Object.prototype.hasOwnProperty.call(TheClass.prototype, "theMethod")){
// Define the method.
}
if(TheClass.prototype.hasOwnProperty("theMethod")){
// Define the method.
}
2. Using property descriptors
You can choose the property descriptor however you like, but existing methods are writable, configurable, and non-enumerable (the last of which is the default when using defineProperty).
defineProperties can be used to define multiple properties in one go.
When simply assigning a property using =, the property becomes writable, configurable, and enumerable.
Code samples
// Define the method:
Object.defineProperty(TheClass.prototype, "theMethod", {
writable: true,
configurable: true,
value: function(){}
});
// Define the method:
Object.defineProperties(TheClass.prototype, {
theMethod: {
writable: true,
configurable: true,
value: function(){}
}
});
3. Using the correct function kind
JavaScript has four major kinds of functions which have different use cases.
“Is invokable” means that it can be called without new and “Is constructable” means that it can be called with new.
Function kind
Example
Is invokable
Is constructable
Has this binding
Arrow function
() => {}
Yes
No
No
Method
({ method(){} }).method
Yes
No
Yes
Class
(class{})
No
Yes
Yes
function function
(function(){})
Yes
Yes
Yes
What we’re looking for is a method that can be called (without new) and has its own this binding.
We could use functions, but, looking at existing methods, not only is new "HELLO".charAt(); strange, it also doesn’t work!
So the method should also not be constructable.
Therefore proper Methods are what we’re looking for.
Note that this obviously depends on your use case.
For example, if you want a constructable function, by all means, use a class instead.
Code sample
We go back to the previous code sample and instead of function(){} use a method definition.
// Define the method:
Object.defineProperty(TheClass.prototype, "theMethod", {
writable: true,
configurable: true,
value: {
theMethod(){
// Do the thing.
}
}.theMethod
});
Why bother with 2. and 3.?
The goal of the enumerability and constructability considerations is to create something that has the same “look and feel” as existing, built-in methods.
The difference between those and a naive implementation can be demonstrated using this snippet:
class TheClass{}
TheClass.prototype.theMethod = function(){};
Let’s compare this to a different, built-in method, like String.prototype.charAt:
Code snippet
Naive example
Built-in example
thePrototype
Is TheClass.prototype
Is String.prototype
theMethod
Is TheClass.prototype.theMethod
Is String.prototype.charAt
for(const p in thePrototype){ console.log(p); }
"theMethod" will be logged at some point.
"charAt" will never be logged.
new theMethod
Creates an instance of theMethod.
TypeError: theMethod is not a constructor.
Using the tools from subsections 2 and 3 make it possible to create methods that behave more like built-in methods.
4. Getters and setters
An alternative is to use a getter. Consider this:
const arr = [
"a",
"b",
"c",
];
console.log(arr.indexOfB); // 1
How would an indexOfB getter on the Array prototype look like?
We can’t use the above approach and replace value by get, or else we’ll get:
TypeError: property descriptors must not specify a value or be writable when a getter or setter has been specified
The property writable needs to be removed entirely from the descriptor.
Now value can be replaced by get:
Object.defineProperty(Array.prototype, "indexOfB", {
configurable: true,
get: {
indexOfB(){
return this.indexOf("b");
}
}.indexOfB
});
A setter can also be specified by adding a set property to the descriptor:
Object.defineProperty(Array.prototype, "indexOfB", {
configurable: true,
get: {
indexOfB(){
return this.indexOf("b");
}
}.indexOfB,
set: {
indexOfB(newValue){
// `newValue` is the assigned value.
// Use `this` for the current Array instance.
// No `return` necessary.
}
}.indexOfB
});
Web compatibility impact
There are a few reasons why anyone would want to extend built-in prototypes:
You got a brilliant idea yourself for a new feature to be added to all instances of whatever class you’re extending, or
You want to backport an existing, specified feature to older browsers.
If you extend the target object, there are two options to consider:
If the property doesn’t exist, supply it, otherwise, leave the existing property in place, or
Always replace the property with your own implementation, no matter if it exists or not.
All approaches mostly have disadvantages:
If you or someone else invented their own feature, but a standard method comes along which has the same name, then these features are almost guaranteed to be incompatible.
If you or someone else try to implement a standard feature in order to backport it to browsers that don’t support it, but don’t read the specification and “guess” how it works, then the polyfilled feature is almost guaranteed to be incompatible with the standard implementation.
Even if the spec is followed closely, who will make sure to keep up with spec changes?
Who will account for possible errors in the implementation?
If you or someone else choose to check if the feature exists before overriding it, then there’s a chance that as soon as someone with a browser which supports the feature visits your page, suddenly everything breaks because the implementations turn out to be incompatible.
If you or someone else choose to override the feature regardless, then at least the implementations are consistent, but then migration to the standard feature may be difficult.
If you write a library that is used a lot in other software, then the migration cost becomes so large that the standard itself has to change; this is why Array.prototype.contains had to be renamed to Array.prototype.includes[Reddit] [ESDiscuss] [Bugzilla] [MooTools], and Array.prototype.flatten could not be used and had to be named Array.prototype.flat instead[Pull request 1] [Pull request 2] [Bugzilla].
In other words, this is the reason we can’t have nice things.
Also, your library may not be interoperable with other libraries.
Alternatives
The simplest alternative is to define your own plain function:
const theMethod = (object) => object.theProperty; // Or whatever.
const theProperty = theMethod(theObject);
You could also consider a Proxy.
This way you can dynamically query the property and respond to it.
Let’s say you have an object with properties a through z and you want to implement methods getA through getZ:
const theProxiedObject = new Proxy(theObject, {
get(target, property, receiver){
const letter = property.match(/^get(?<letter>[A-Z])$/)?.groups?.letter.toLowerCase();
if(letter){
return () => target[letter];
}
return Reflect.get(target, property, receiver);
}
});
console.assert(theProxiedObject.getB() === theProxiedObject.b);
You could also extend your object’s prototype using another class, and use this instead:
class GetterOfB extends Object{
b;
constructor(init){
super();
Object.assign(this, init);
}
getB(){
return this.b;
}
}
const theObject = new GetterOfB({
b: "c"
});
const theB = theObject.getB();
All you have to keep in mind is to not modify things you didn’t define yourself.
Often, we are presented with an array (IEnumerable) property that specific values need to be extracted. in c# we can do something similar to:
public AssetModel PromoImage {
get
{
return Assets.FirstOrDefault(x => x.AssetTypeCd == "promoimage");
}
private set { }
}
Is there a way to easily to this within Angular 2?
Lodash provides similar functionality to LINQ for JavaScript programs (and then some), though not deferred execution -- LINQ queries are deferred until they are enumerated, while lodash (usually) performs the query immediately and returns an array/object of results. (Though in this case LINQ wouldn't even defer it since FirstOrDefault returns a scalar and not a queryable/enumerable.)
In your case, you would do something like this:
let obj = {
get promoImage() {
return _.find(assets, a => a.assetTypeCd === 'promoimage');
},
// ...
};
Then accessing obj.promoImage will execute the function to obtain the attribute's value.
(Here I assume this is where we are creating the new object, and assets is the assets list in the lexical scope of the constructor function. You can change it to reference this if you are storing data on the object itself and not in constructor upvalues.)
Notes:
Lodash does not depend on Angular at all.
ES6 provides a find() method on the Array prototype, so this feature will be built-in to browsers once ES6 is adopted. Sadly, IE is (as usual) the outlier without any support for it. However, Lodash is still a very useful library to have in your toolkit, and note that Lodash's find() works on objects too, not just arrays.
I'm creating a JS library object that has a primary function, but I also want to assign sub-methods to that primary function to extend it.
So right now what I have looks like this:
Parser.prototype = {
init: function(){
},
primaryFunction:function(){
}
}
I really like this notation for assigning methods to Parser.prototype, but how can I use similar notation for assigning methods to primaryFunction? Also, the submethods are characterized by a string. Because eventually, my goal is to be able call like this: primaryFunction["*"]();
It seems I can't do this:
primaryFunction:function(char){
"*":function(){
}
}
Sort of makes sense why, but am I forced to do this?
primaryFunction:function(char){
this["*"] = function(){};
}
There's a big difference between adding properties to Parser.prototype and adding properties to primaryFunction. When you add to Parser.prototype, you're adding properties to a non-function object that is used as the underlying prototype of any object (instance) created via the new Parser expression. But if you add properties to primaryFunction, you're adding those properties to the function object of primaryFunction, directly.
The result is that properties added to Parser.prototype become, in effect, methods of instances created via new Parser. Within calls to those "methods," provided they're made normally (instance.init() and similar), this will refer to the instance.
But properties added to primaryFunction are methods of the function primaryFunction, not instances created with it. If you call them in the normal way (instance.primaryFunction["*"]()), within the call this is primaryFunction (the function object), not instance.
If that's really want you want, then you simply assign them after the fact (you can't do it within the object initializer):
Parser.prototype.primaryFunction["*"] = function() { /* ... */ };
But there are relatively few use cases for doing that, not least because if you're using Parser as a constructor function (e.g., with new Parser), you probably want to do things with the instance created, and you won't have access to it in primaryFunction["*"] unless you do some funny stuff.
The funny stuff, FWIW, looks like this — but I'm not recommending it (nor recommending against it), just being complete:
/* ...inside the `Parser` function... */
this.primaryFunction["*"] = this.primaryFunction["*"].bind(this);
That creates a new function that, when called, will call primaryFunction but setting this to the instance created by new Parser, and saves the resulting function as an "own" property on the instance being created.
From your comment below:
So my use case is just a parsing object that performs different
actions based on what characters it reads. The Parser loops through a
string, and my goal is to call a different function based on what the
current character at the index is. There will only be a set of unique
functions for each special character that may be in the string. For
instance, those characters could be: *, /, \, <, >,
essentially non alphanumerics. For anything alphanumeric, I want a
default function to handle those. So essentially, in a loop I'm
getting the currentChar, and trying to call a sub-method based on
which char it is. So the loop is essentially doing:
this.handleChar[currentChar]().
The bind solution above would work for that. But I think I'd probably go another way: Have a private, shared map of handler functions that you call (either passing in the parser instance as an argument, or as this) with the characters:
var Parser = function() {
var handlers = {
"*": function(char) {
// handle *...
},
"/": function(char) {
// handle /...
},
// ...and so on...
"default": function(char) {
// default handling...
}
};
function Parser() {
// ...
}
// (I always advocate *augmenting*, not replacing, `FuncName.prototype` properties)
Parser.prototype.handleChar = function() {
var currentChar;
while (!!(currentChar = /*...get the character...*/)) {
(handlers[currentChar] || handlers.default).call(this, currentChar);
// On some older JavaScript engines, you'd have to write the above like this:
//
// (handlers[currentChar] || handlers["default"]).call(this, currentChar);
//
// ...because ES3 didn't let you use a keyword as a property name literal
// (ES5 does, and some engines always did).
}
};
return Parser;
}();
That example passes the parser instance as this by using Function#call (the first argument is what ends up being this in the function call). It also uses JavaScript's curiously-powerful || operator to pick the function on handlers to call.
(If you don't care that handlers be private, then you don't need the wrapper.)
Is it acceptable to add an attribute or value to a JavaScript function?
Example:
var f = 1;
function foo (param) {
f++;
}
var fooFunc = foo;
fooFunc.dummy = f;
console.log('fooFunc: ' + fooFunc);
console.log('fooFunc.dummy: ' + fooFunc.dummy);
The above example creates a function (foo), then assigns it to a new variable (fooFunc) and then adds a dummy attribute to fooFunc.
When run, this example prints the text of the function first, and then it prints the expected value (1 in this case). When printing the function, it doesn't show any indication of the dummy value:
fooFunc: function foo(param) {
f++;
}
fooFunc.dummy: 1
JsFiddle here - open the browser's JavaScript console to see the log messages: http://jsfiddle.net/nwinkler/BwvLf/
Why does this work? And where is the dummy attribute stored, and why isn't it printed when I log the function?
Lastly, even if this works, is it a good idea (or an acceptable practice) to use this? I don't want to start an open ended discussion on this, but rather see if there's documented uses of this, or people discouraging this in JavaScript coding guidelines.
Everything except primitives ( null, undefined, number, string, boolean ) in JavaScript are objects. So functions are basically objects.
Objects in JavaScript can have properties and methods, hence functions too.
all functions inherit from Function.prototype and has certain properties ( name, length ) and methods ( .call, .apply ) coming through this chain.
It is sometimes very useful to keep properties attached to the function itself, like cache information, number of invocations etc. There is nothing wrong out in using it this way.
More details : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
Let's have a look at ECMAScript documentation (Which is the standard JavaScript is based on). Here's the 3rd. version of it:
http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%203rd%20edition,%20December%201999.pdf
Go to chapter 15, Native ECMAScript Objects.
15.3 > Function objects.
There's a lot of interesting information there concerning your question, but the first thing worth noticing is that function is an object.
As an object, it has attributes (predefined and that you can assign yourself).
For example, try:
console.log('fooFunc.name: ' + fooFunc.name);
It should display "foo" in your case.
Since it's documented quite well, you can use it as a standard way, though it is not so well-spread and may seem a bit unusual.
Hope this helps.
It is normal object behavior, whether "acceptable" or not.
By using the function keyword you are actually calling the native predefined Function() constructor. Like any object constructor it returns an object after building it. Like any object, the returned object can have properties, including other functions as method properties.
var adder = function(a, b){return a+b};
adder.subtracter = function(a, b){return a-b};
console.log(adder(1,2)); // 3
console.log(adder.subtracter(1,2)); // -1
TIP: if you want to see the adder object and its subtracter method, switch to DOM view from Console view after running the above code in console and then search for "adder". You'll see the object there, and then you can collapse to see what it's made from, including a subtracter object.
Of course, a function object is a special native object, which makes it possible to make calls like this: adder() and actually run some code. The fact that a function object is harder to inspect for custom attached properties, combined with its native special object treats (read built-in restrictive behavior), should give you a hint that, while it's possible, attaching custom properties is not the intended nor a good use of a function object.