IMHO this whole juxtaposition of OOP and functional ideologies doesn't really make much sense to me.
My impression was that article expressed similar idea - that you can use OO and functional techniques when it makes sense.
When I say that I prefer class-based composition over higher-order-function based composition I do not stand that I support OOP paradigm and reject functional paradigm.
I prefer classes because they work as anchor points for documentation, by providing a referable name that can be used with TypeScript or without TypeScript (with jsdoc) and thus making code easier to understand and navigate. Higher-order-functions are lacking this. If someone can figure this part out for higher-order-functions then I would happily use it.
"OO makes code understandable by encapsulating moving parts. FP makes code understandable by minimizing moving parts." Michael Feathers
The only takeaway I can do here is that both things are equally important
encapsulating moving parts
minimizing moving parts
Focusing only on 1 will lead to problems.
Some concepts that you bring - I don't see any reason to classify as "functional" or "OOP". For example railway programming - it's a technique for error handling. There is nothing about it that makes it "functional". It plays nicely if pattern matching is supported as 1st class citizen. But one can also use idea to implement this concept with OO. If it's practical to do so is another question. I'd lean towards saying that "everything has known error" is incorrect assumption - only on application boundaries there are known errors (data inputs, networking), within application there usually are no errors - if there are - then maybe it's a time to review architecture.
Polymorphic code in OO has exactly the same problem - and if you're unlucky, even if you locate the implementing class, you may still have to drill through several layers of implementation inheritance to find the code that is actually running - i.e. it's the type of code where even with a debugger you wonder - how did I even get here?
I don't think highly polymoprhic code is that normal. It's often a case of inheritance abuse. And then the more layers you have the stronger my point gets because there is no reason why higher-order-function based solution would have less layers than that of class-based solution, and with every single layer every time it's easier to navigate the code.
The point is that there are things that work in JavaScript that don't sit well with TypeScript as its design is much more OO centric than JavaScript's. When you use TypeScript you are effectively choosing OO - with a functional style your are constantly fighting an "impedance mismatch".
How could anyone consider JavaScript "functional" is something I don't quite understand. It came out same decade as Python and Ruby and all 3 have a lot in common. All 3 implement closures very similarly - similarly like it was done in Smalltalk - a precursor to all OO dynamic languages. All have OO model at it's core. Even a Function in JavaScript is a callable object - same as Python by the way ((function(){}) instanceof Object => true).
I can't think of good example of what works well with JS but not TypeScript thus I'm not entirely sure what you're pointing to.
I believe reason why some piece of code won't work with TypeScript is not because it's not OO but because it's "too dynamic".
But I think this sort of code tends to not work not only with TypeScript but also with all other JS ecosystem (js runtime engines, jsdocs, linters).
Meanwhile once there is enough tooling even mediocre solutions can gain momentum. TypeScript was languishing until VS Code came along.
It's never enough to bring in a concept but not solve the tooling problem. Thinking about tooling first also ensures that concept will play well with tooling. Some concepts make tooling practically impossible to build.
TypeScript from it's beginning was built thinking about tooling - from first days it come with language server that integrates seamlessly into any IDE. TypeScript became best language server available for javascript even if one is not using TypeScript. VSCode also became biggest open-source project in javascript ecosystem thus it showed cased how to solve scalability problems of javascript - with typescript. I totally agree that tooling will effect how solutions are created. In this case not exactly because of vscode, but because of typescript language server. Capability to produce tools always had and always will dictate what approach is taken and not purely theoretical background. We might like it might not but it's a simply a facts. This is true in every engineering field. When you write code for your own personal project you can explore anything you want. When you write code for it to serve a business together with many other practicing developers your goal is no longer to explore solution scope but to produce a solution that is well accepted in industry - and that means it a solution that plays well with existing tools - not hypothetical tools.
you can use OO and functional techniques when it makes sense.
The article uses "vs" and supplies two distinct implementations (though the first example is largely procedural). To highlight differences it's typical to take the examples to the extreme.
Of course there is a range of possible solutions between both extremes.
You seem to prefer leaning toward the OO end of the spectrum.
My contention is that JavaScript naturally tends more towards the function-based end (not necessarily to the extreme of my "function-style" example).
by providing a referable name that can be used with TypeScript or without TypeScript (with jsdoc) and thus making code easier to understand and navigate.
// a function that takes any two arguments of the same type // and returns a result of that typetypeOp2Args<T>=(a0:T,a1:T)=>T;constadd:Op2Args<number>=(augend,addend)=>augend+addend;constsub=(minuend:number,subtrahend:number)=>minuend-subtrahend;functionmult(multiplicand:number,multiplier:number){returnmultiplicand*multiplier;};functionrun(f:Op2Args<number>,a:number,b:number){returnf(a,b);}construnOp:(op:Op2Args<number>)=>void=op=>run(op,2,3);console.log(runOp(add));// 5console.log(runOp(sub));// -1console.log(runOp(mult));// 6
In the above example Op2Args is a referable name - but from your past comments you don't like this because at the definition site of f it isn't obvious that either add, sub or mult might be used.
both things are equally important
As stated "minimizing moving parts" has priority (as long as all the tradeoffs are acceptable) over "encapsulating moving parts". Without minimizing the moving parts first, "accidental complexity" tends to be entombed into the encapsulation. But that is true of any approach.
For example railway programming
is considered functional because "mapping a function over a type" is extremely common in functional programming - in an OOP without first class functions you would have to "map an object over another object" where the former has to implement a method (i.e. a single method interface) the latter is requiring - with is way more convoluted.
I don't think highly polymorphic code is that normal.
i.e. appendChild will append anything that implements the Node interface - that could be a lot of different kinds of objects.
In the example above Op2Args makes f polymorphic.
React uses React.Component so that it can treat all your components in an identical manner - that is polymorphism in action.
Python
Guido van Rossum is anti-functional - lambda almost didn't make the cut.
All 3 implement closures very similarly - similarly like it was done in Smalltalk - a precursor to all OO dynamic languages.
Closures were devised by Peter Landin in 1964 as described in "The Mechanical Evaluation of Expressions" (lambda calculus). That is why closures are considered a "functional" feature - regardless of where they may have been adopted later.
All have OO model at its core.
If you were talking about TypeScript I would agree - it's opinionated towards class-based OO. But in the case of JavaScript I disagree and I've elaborated on that here.
While being essentially imperative the core building block of JavaScript is the function. Multiple functions can share state through a shared closure so that these functions may act as methods. That is essentially how OOP with functions works.
The object - in an "object-oriented" (but not class-based) sense - is an emergent concept via the function context - this giving a function the capability to access other values on the "object" the function was being referenced through.
The prototype chain then makes it possible to share a function across object instances (to save memory). So conceptually object instances sharing the same function across the prototype chain belong to the same "class" - but there is no class construct, only object instances.
I can't think of good example of what works well with JS but not TypeScript.
I'm referring to the lopsided "typing tax" that TypeScript imposes on "function-style" vs "class-style" (or "procedural-style") code that you yourself already commented on. If one persists on using function and closure based approaches in TypeScript one quickly finds oneself knee deep in noisy type definitions expressed in TypeScript's typing meta language. Functional languages already devised concise ways of expressing function types.
With "class-style" code you can coast on rudimentary and terse typing features for a long time before you ever have to dive into the advanced types. But making it easy to work with function and closure types wasn't a priority - despite the fact that these are core JavaScript features.
Going by the mantra "make doing the right things easy and the wrong things hard" by focusing on making "class-style" code easier to type, TypeScript reinforces the idea that "class-based object-orientation" is the "right thing" while "function-style" is the wrong thing.
TypeScript began its life as an attempt to bring traditional object-oriented types to JavaScript so that the programmers at Microsoft could bring traditional object-oriented programs to the web. ... The resulting system is powerful, interesting and messy.
In 2008 Douglas Crockford wrote:
JavaScript is most despised because it isn’t some other language. If you are good in some other language and you have to program in an environment that only supports JavaScript, then you are forced to use JavaScript, and that is annoying. Most people in that situation don’t even bother to learn JavaScript first, and then they are surprised when JavaScript turns out to have significant differences from the some other language they would rather be using, and that those differences matter (JtGP, p.2).
"TypeScript the Good Parts" focuses on "class-based object-orientation" much the same way that "JavaScript the Good Parts" doesn't. In a strange twist TypeScript has become that other language that most people would rather be using so that they don't have to bother learning JavaScript first.
It's never enough to bring in a concept but not solve the tooling problem.
Oliver Steele made an interesting distinction between "Language Mavens" and "Tool Mavens" in IDE divide - he comes to the conclusion that tool-orientation comes at the cost of language features.
Ironically Java has been continually ridiculed for its need of tooling to "keep developers productive" in the face of the all the language's and ecosystem's warts. Meanwhile it's perfectly acceptable for the members of the JavaScript community to employ tool heavy build pipelines (editors requiring language servers) and constantly clamour for "better tools".
I have read JavaScript the Good-Parts. Haven't yet had chance to read the similarly named one on TypeScript.
On function returning objects through closures as means of OO there are benefits and weaknesses compared to class (old prototype based) approach.
function MyObj {
var name = 'something'
return {
get name() { return name }
}
}
I think it's not a good approach. I think Crockfords originally didn't take all important aspects into consideration.
It's useable in isolated case but I believe it was never practical to accept this as a standard way to move forward with OO in JS.
I also believe even before introduction of 'class' it was never mainstream approach. The approach that was always considered "correct" was prototypal OO (check examplesf influencial js libraries such as dojo, prototype, jQuery v1 - most common are custom class builders due to verbosity of direct usage of prototype, or use of prototype directly)
function MyObj{}
MyObj.prototype.name = function() { }
And due to multitude of different approaches towards doing OO in JS, there was a need to have a standard. Class was a straight forward thing that is widely understood and played perfectly well with JS prototype thing.
The approach of doing OO through means of closures comes with some benefits over prototypal/class based approach
You don't need 'this' when writing the code
Truly private scope
methods are indefinitely are bound to closure, thus there is no risk of screwing this up
This can be worked without much difficulty in 'class' and it was already proven.
Regarding the private attributes
ES7 supports private members throughh #privateMember
there is static code checking available for jsdoc tag /** @private */
Regarding the this bounding problem
static type checking can report this error
onClick=e => myObject.onClick(e) solves this
I consider passing method from object as a function to always be a weak practice. If one wants to pass function - this is excellent use case for arrow function. Always create a new arrow function, if wanting to pass a function. Arrow functions are the link between functional and OO approaches. Using method without object (as in passing it as function) doesn't check out with core OO idea of message passing to object.
Weak points of this approach to do OO I think were much harder to iron out. It simply didn't played nicely with underlying language concepts that existed from 1st days of JavaScript (prototypes). Problems of this approach:
same thing as with higher-order-function this pattern does not declare a type thus there is no means of referring to it - you'd need to invent something here to be able to refer it (something like x' in ReScript)
there was no means of introspection (which was understood as important goal of dynamic language at certain point, however now with transpilers commonly used this might not be a needed feature at all)
certain performance implications - objects created through this way are more memory heavy and slower to instantiate, might again not matter that much today but was a factor to chose prototype over this method
most importantly: there is no means of type checking const x = MyObject(); x instanceof MyObject, keyword instanceof just no longer makes any sense, it's hard to move forward with a technique that doesn't play well with certain 1st class language features already in existence
I understand the point of not using classes, in favor of doing pure functional development in JS. I completely miss the point of trying to use functions for OO where classes are available to implement OO.
Many arguments are Closure Compiler related. Now of course if to be purists arguments about specific tool maybe would not matter. But consider that job of developers is to produce working software. If you had to write a bigger piece of code in js then Closure Compiler was the tool to go with, it would be either impossible, or you'd need to invent your own tools.
Haven't yet had chance to read the similarly named one on TypeScript.
There isn't one.
But when Microsoft states "so that the programmers at Microsoft could bring traditional object-oriented programs to the web" they are talking about C# style class-based object-orientation.
TypeScript was designed with a good class-based OO development experience in mind - sacrificing the ease of other approaches that may be equally valid under JavaScript (which claims to be multi-paradigm).
I also believe even before introduction of 'class' it was never mainstream approach.
You are correct. Because mainstream OO is "class-based". "OOP with closures" is only about objects, not classes.
The approach that was always considered "correct" was prototypal OO.
I would argue that the mainstream didn't even accept "prototypal OO" as correct given that there is no explicit class mechanism and the confusion that it caused up to and including ES5. But it certainly is possible to emulate class-based OO with prototypal OO. Strictly speaking membership to a class should last for the lifetime of the object. JavaScript objects can be augmented after construction - so "class membership" isn't fixed.
Class was a straight forward thing that is widely understood and played perfectly well with JS prototype thing.
The important aspect was that it aligned with the mainstream mindset of class-based object-orientation. However the code before ES2015 wasn't straightforward.
some benefits
You missed:
Elimination of inheritance. Commonalities have to be managed entirely through composition.
Don't need new to instantiate an object (useful if instances are cached);
ES7 supports private members through #privateMember
They landed in Chrome 74 but as such the proposal has been stuck at stage 3 since 2017 - it didn't get into ES2020; maybe it will be part of ES2021 (ES2016 is ECMA-262 7ᵗʰ Edition).
Regarding the this bounding problem
The problem with this has more to do with developers from other languages not understanding how it works - that it is a deliberate decision not to bind the function to the object.
(something like x' in ReScript)
'x is a simply type variable just like T is a type variable in Op2Args<T>. And the object returned by the factory still has a structural type.
keyword instanceof just no longer makes any sense,
instanceof can be a great escape hatch but the whole point of polymorphism is that the object should know what to do without the type needing to be known first (Replace Conditional with Polymorphism).
I completely miss the point of trying to use functions for OO where classes are available to implement OO.
There isn't just one kind of object-orientation. You are correct that the mainstream assumes "class-based object-orientation" when OO is mentioned; really COP - class-oriented programming would have been a better name (the code is declaring classes, not assembling objects).
"OOP with closures" doesn't seek to emulate "class-based object-orientation". Without inheritance, composition is the only option which leads to a simpler style of object-orientation. Also the notion isn't that "closures are like classes" but that "closures are like objects".
Once "closures are like objects" sinks in, it should become apparent that there are situations where closures can be more succinct than objects (created by a class).
Also consider that in 2008 ES5 wasn't even finalized yet - class wasn't official until 2015.
Which one is easier to understand
// closures as objectsfunctionphone(phoneNumber){return{getPhoneNumber:getPhoneNumber,getDescription:getDescription};functiongetPhoneNumber(){returnphoneNumber;}functiongetDescription(){return'This is a phone that can make calls.';}}functionsmartPhone(phoneNumber,signature){varcore=phone(phoneNumber);signature=signature||'sent from '+core.getPhoneNumber();return{getPhoneNumber:core.getPhoneNumber,getDescription:getDescription,sendEmail:sendEmail};functiongetDescription(){returncore.getDescription()+' It can also send email messages';}functionsendEmail(emailAddress,message){console.log('To: '+emailAddress+'\n'+message+'\n'+signature);}}
Or this one?
// Combination inheritance// prototype chaining + constructor stealing//functionPhone(phoneNumber){this.phoneNumber=phoneNumber;}functiongetPhoneNumber(){returnthis.phoneNumber;}functiongetDescription(){return'This is a phone that can make calls.';}Phone.prototype.getPhoneNumber=getPhoneNumber;Phone.prototype.getDescription=getDescription;functionSmartPhone(phoneNumber,signature){Phone.call(this,phoneNumber);// inherit propertiesthis.signature=signature||'sent from '+this.getPhoneNumber();}SmartPhone.prototype=newPhone();// inherit methodsfunctionsendEmail(emailAddress,message){console.log('To: '+emailAddress+'\n'+message+'\n'+this.signature);}functiongetDescriptionSmart(){vardescription=Phone.prototype.getDescription.call(this);returndescription+' It can also send email messages';}SmartPhone.prototype.sendEmail=sendEmail;SmartPhone.prototype.getDescription=getDescriptionSmart;
If you had to write a bigger piece of code in js then Closure Compiler was the tool to go with, it would be either impossible, or you'd need to invent your own tools.
Not everybody has Google size problems - and the tradeoffs of the closure-based approach are known.
Clearly in a project using the Closure compiler one would stick to the recommended coding practices. But when in the past I had a look at the Closure library it struck me that it was organized to appeal to Java programmers - so it's not that surprising that the compiler would favour the pseudo-classical approach (apart from being more optimizable).
In any case I'm not recommending ignoring class - just to be familiar with the closure based approach; it does exist in the wild and in some situations it could come in handy.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
IMHO this whole juxtaposition of OOP and functional ideologies doesn't really make much sense to me.
My impression was that article expressed similar idea - that you can use OO and functional techniques when it makes sense.
When I say that I prefer class-based composition over higher-order-function based composition I do not stand that I support OOP paradigm and reject functional paradigm.
I prefer classes because they work as anchor points for documentation, by providing a referable name that can be used with TypeScript or without TypeScript (with jsdoc) and thus making code easier to understand and navigate. Higher-order-functions are lacking this. If someone can figure this part out for higher-order-functions then I would happily use it.
The only takeaway I can do here is that both things are equally important
Focusing only on 1 will lead to problems.
Some concepts that you bring - I don't see any reason to classify as "functional" or "OOP". For example railway programming - it's a technique for error handling. There is nothing about it that makes it "functional". It plays nicely if pattern matching is supported as 1st class citizen. But one can also use idea to implement this concept with OO. If it's practical to do so is another question. I'd lean towards saying that "everything has known error" is incorrect assumption - only on application boundaries there are known errors (data inputs, networking), within application there usually are no errors - if there are - then maybe it's a time to review architecture.
I don't think highly polymoprhic code is that normal. It's often a case of inheritance abuse. And then the more layers you have the stronger my point gets because there is no reason why higher-order-function based solution would have less layers than that of class-based solution, and with every single layer every time it's easier to navigate the code.
How could anyone consider JavaScript "functional" is something I don't quite understand. It came out same decade as Python and Ruby and all 3 have a lot in common. All 3 implement closures very similarly - similarly like it was done in Smalltalk - a precursor to all OO dynamic languages. All have OO model at it's core. Even a Function in JavaScript is a callable object - same as Python by the way (
(function(){}) instanceof Object => true
).I can't think of good example of what works well with JS but not TypeScript thus I'm not entirely sure what you're pointing to.
I believe reason why some piece of code won't work with TypeScript is not because it's not OO but because it's "too dynamic".
But I think this sort of code tends to not work not only with TypeScript but also with all other JS ecosystem (js runtime engines, jsdocs, linters).
It's never enough to bring in a concept but not solve the tooling problem. Thinking about tooling first also ensures that concept will play well with tooling. Some concepts make tooling practically impossible to build.
TypeScript from it's beginning was built thinking about tooling - from first days it come with language server that integrates seamlessly into any IDE. TypeScript became best language server available for javascript even if one is not using TypeScript. VSCode also became biggest open-source project in javascript ecosystem thus it showed cased how to solve scalability problems of javascript - with typescript. I totally agree that tooling will effect how solutions are created. In this case not exactly because of vscode, but because of typescript language server. Capability to produce tools always had and always will dictate what approach is taken and not purely theoretical background. We might like it might not but it's a simply a facts. This is true in every engineering field. When you write code for your own personal project you can explore anything you want. When you write code for it to serve a business together with many other practicing developers your goal is no longer to explore solution scope but to produce a solution that is well accepted in industry - and that means it a solution that plays well with existing tools - not hypothetical tools.
The article uses "vs" and supplies two distinct implementations (though the first example is largely procedural). To highlight differences it's typical to take the examples to the extreme.
Of course there is a range of possible solutions between both extremes.
You seem to prefer leaning toward the OO end of the spectrum.
My contention is that JavaScript naturally tends more towards the function-based end (not necessarily to the extreme of my "function-style" example).
In the above example
Op2Args
is a referable name - but from your past comments you don't like this because at the definition site off
it isn't obvious that eitheradd
,sub
ormult
might be used.As stated "minimizing moving parts" has priority (as long as all the tradeoffs are acceptable) over "encapsulating moving parts". Without minimizing the moving parts first, "accidental complexity" tends to be entombed into the encapsulation. But that is true of any approach.
is considered functional because "mapping a function over a type" is extremely common in functional programming - in an OOP without first class functions you would have to "map an object over another object" where the former has to implement a method (i.e. a single method interface) the latter is requiring - with is way more convoluted.
Polymorphism is everywhere.
i.e. appendChild will append anything that implements the Node interface - that could be a lot of different kinds of objects.
In the example above
Op2Args
makesf
polymorphic.React uses React.Component so that it can treat all your components in an identical manner - that is polymorphism in action.
Guido van Rossum is anti-functional - lambda almost didn't make the cut.
Closures were devised by Peter Landin in 1964 as described in "The Mechanical Evaluation of Expressions" (lambda calculus). That is why closures are considered a "functional" feature - regardless of where they may have been adopted later.
If you were talking about TypeScript I would agree - it's opinionated towards class-based OO. But in the case of JavaScript I disagree and I've elaborated on that here.
While being essentially imperative the core building block of JavaScript is the function. Multiple functions can share state through a shared closure so that these functions may act as methods. That is essentially how OOP with functions works.
The object - in an "object-oriented" (but not class-based) sense - is an emergent concept via the function context -
this
giving a function the capability to access other values on the "object" the function was being referenced through.The prototype chain then makes it possible to share a function across object instances (to save memory). So conceptually object instances sharing the same function across the prototype chain belong to the same "class" - but there is no class construct, only object instances.
Given the function first nature of JavaScript, OO workarounds like the Command Pattern (as in Replace Conditional Dispatcher with Command) aren't necessary - just pass a function with its attached closure.
I'm referring to the lopsided "typing tax" that TypeScript imposes on "function-style" vs "class-style" (or "procedural-style") code that you yourself already commented on. If one persists on using function and closure based approaches in TypeScript one quickly finds oneself knee deep in noisy type definitions expressed in TypeScript's typing meta language. Functional languages already devised concise ways of expressing function types.
With "class-style" code you can coast on rudimentary and terse typing features for a long time before you ever have to dive into the advanced types. But making it easy to work with function and closure types wasn't a priority - despite the fact that these are core JavaScript features.
Going by the mantra "make doing the right things easy and the wrong things hard" by focusing on making "class-style" code easier to type, TypeScript reinforces the idea that "class-based object-orientation" is the "right thing" while "function-style" is the wrong thing.
TypeScript admits as much:
In 2008 Douglas Crockford wrote:
"TypeScript the Good Parts" focuses on "class-based object-orientation" much the same way that "JavaScript the Good Parts" doesn't. In a strange twist TypeScript has become that other language that most people would rather be using so that they don't have to bother learning JavaScript first.
Oliver Steele made an interesting distinction between "Language Mavens" and "Tool Mavens" in IDE divide - he comes to the conclusion that tool-orientation comes at the cost of language features.
Ironically Java has been continually ridiculed for its need of tooling to "keep developers productive" in the face of the all the language's and ecosystem's warts. Meanwhile it's perfectly acceptable for the members of the JavaScript community to employ tool heavy build pipelines (editors requiring language servers) and constantly clamour for "better tools".
I have read JavaScript the Good-Parts. Haven't yet had chance to read the similarly named one on TypeScript.
On function returning objects through closures as means of OO there are benefits and weaknesses compared to class (old prototype based) approach.
I think it's not a good approach. I think Crockfords originally didn't take all important aspects into consideration.
It's useable in isolated case but I believe it was never practical to accept this as a standard way to move forward with OO in JS.
I also believe even before introduction of 'class' it was never mainstream approach. The approach that was always considered "correct" was prototypal OO (check examplesf influencial js libraries such as dojo, prototype, jQuery v1 - most common are custom class builders due to verbosity of direct usage of prototype, or use of prototype directly)
And due to multitude of different approaches towards doing OO in JS, there was a need to have a standard. Class was a straight forward thing that is widely understood and played perfectly well with JS prototype thing.
The approach of doing OO through means of closures comes with some benefits over prototypal/class based approach
This can be worked without much difficulty in 'class' and it was already proven.
Regarding the private attributes
#privateMember
Regarding the
this
bounding problemonClick=e => myObject.onClick(e)
solves thisI consider passing method from object as a function to always be a weak practice. If one wants to pass function - this is excellent use case for arrow function. Always create a new arrow function, if wanting to pass a function. Arrow functions are the link between functional and OO approaches. Using method without object (as in passing it as function) doesn't check out with core OO idea of message passing to object.
Weak points of this approach to do OO I think were much harder to iron out. It simply didn't played nicely with underlying language concepts that existed from 1st days of JavaScript (prototypes). Problems of this approach:
x'
in ReScript)const x = MyObject(); x instanceof MyObject
, keywordinstanceof
just no longer makes any sense, it's hard to move forward with a technique that doesn't play well with certain 1st class language features already in existenceI understand the point of not using classes, in favor of doing pure functional development in JS. I completely miss the point of trying to use functions for OO where classes are available to implement OO.
Here is a case from 2009 that argued against
functional pattern proposed by Douglas Crockford bolinfest.com/javascript/inheritan...
Many arguments are Closure Compiler related. Now of course if to be purists arguments about specific tool maybe would not matter. But consider that job of developers is to produce working software. If you had to write a bigger piece of code in js then Closure Compiler was the tool to go with, it would be either impossible, or you'd need to invent your own tools.
There isn't one.
But when Microsoft states "so that the programmers at Microsoft could bring traditional object-oriented programs to the web" they are talking about C# style class-based object-orientation.
TypeScript was designed with a good class-based OO development experience in mind - sacrificing the ease of other approaches that may be equally valid under JavaScript (which claims to be multi-paradigm).
You are correct. Because mainstream OO is "class-based". "OOP with closures" is only about objects, not classes.
I would argue that the mainstream didn't even accept "prototypal OO" as correct given that there is no explicit class mechanism and the confusion that it caused up to and including ES5. But it certainly is possible to emulate class-based OO with prototypal OO. Strictly speaking membership to a class should last for the lifetime of the object. JavaScript objects can be augmented after construction - so "class membership" isn't fixed.
The important aspect was that it aligned with the mainstream mindset of class-based object-orientation. However the code before ES2015 wasn't straightforward.
You missed:
new
to instantiate an object (useful if instances are cached);They landed in Chrome 74 but as such the proposal has been stuck at stage 3 since 2017 - it didn't get into ES2020; maybe it will be part of ES2021 (ES2016 is ECMA-262 7ᵗʰ Edition).
The problem with
this
has more to do with developers from other languages not understanding how it works - that it is a deliberate decision not to bind the function to the object.'x
is a simply type variable just likeT
is a type variable inOp2Args<T>
. And the object returned by the factory still has a structural type.instanceof
can be a great escape hatch but the whole point of polymorphism is that the object should know what to do without the type needing to be known first (Replace Conditional with Polymorphism).There isn't just one kind of object-orientation. You are correct that the mainstream assumes "class-based object-orientation" when OO is mentioned; really COP - class-oriented programming would have been a better name (the code is declaring classes, not assembling objects).
"OOP with closures" doesn't seek to emulate "class-based object-orientation". Without inheritance, composition is the only option which leads to a simpler style of object-orientation. Also the notion isn't that "closures are like classes" but that "closures are like objects".
Once "closures are like objects" sinks in, it should become apparent that there are situations where closures can be more succinct than objects (created by a class).
Also consider that in 2008 ES5 wasn't even finalized yet -
class
wasn't official until 2015.Which one is easier to understand
Or this one?
Not everybody has Google size problems - and the tradeoffs of the closure-based approach are known.
Clearly in a project using the Closure compiler one would stick to the recommended coding practices. But when in the past I had a look at the Closure library it struck me that it was organized to appeal to Java programmers - so it's not that surprising that the compiler would favour the pseudo-classical approach (apart from being more optimizable).
In any case I'm not recommending ignoring
class
- just to be familiar with the closure based approach; it does exist in the wild and in some situations it could come in handy.