Does JavaScript have the interface type (such as Java's 'interface')?
There's no notion of "this class must have these functions" (that is, no interfaces per se), because:
- JavaScript inheritance is based on objects, not classes. That's not a big deal until you realize:
- JavaScript is an extremely dynamically typed language -- you can create an object with the proper methods, which would make it conform to the interface, and then undefine all the stuff that made it conform. It'd be so easy to subvert the type system -- even accidentally! -- that it wouldn't be worth it to try and make a type system in the first place.
Instead, JavaScript uses what's called duck typing. (If it walks like a duck, and quacks like a duck, as far as JS cares, it's a duck.) If your object has quack(), walk(), and fly() methods, code can use it wherever it expects an object that can walk, quack, and fly, without requiring the implementation of some "Duckable" interface. The interface is exactly the set of functions that the code uses (and the return values from those functions), and with duck typing, you get that for free.
Now, that's not to say your code won't fail halfway through, if you try to call some_dog.quack()
; you'll get a TypeError. Frankly, if you're telling dogs to quack, you have slightly bigger problems; duck typing works best when you keep all your ducks in a row, so to speak, and aren't letting dogs and ducks mingle together unless you're treating them as generic animals. In other words, even though the interface is fluid, it's still there; it's often an error to pass a dog to code that expects it to quack and fly in the first place.
But if you're sure you're doing the right thing, you can work around the quacking-dog problem by testing for the existence of a particular method before trying to use it. Something like
if (typeof(someObject.quack) == "function")
{
// This thing can quack
}
So you can check for all the methods you can use before you use them. The syntax is kind of ugly, though. There's a slightly prettier way:
Object.prototype.can = function(methodName)
{
return ((typeof this[methodName]) == "function");
};
if (someObject.can("quack"))
{
someObject.quack();
}
This is standard JavaScript, so it should work in any JS interpreter worth using. It has the added benefit of reading like English.
For modern browsers (that is, pretty much any browser other than IE 6-8), there's even a way to keep the property from showing up in for...in
:
Object.defineProperty(Object.prototype, 'can', {
enumerable: false,
value: function(method) {
return (typeof this[method] === 'function');
}
}
The problem is that IE7 objects don't have .defineProperty
at all, and in IE8, it allegedly only works on host objects (that is, DOM elements and such). If compatibility is an issue, you can't use .defineProperty
. (I won't even mention IE6, because it's rather irrelevant anymore outside of China.)
Another issue is that some coding styles like to assume that everyone writes bad code, and prohibit modifying Object.prototype
in case someone wants to blindly use for...in
. If you care about that, or are using (IMO broken) code that does, try a slightly different version:
function can(obj, methodName)
{
return ((typeof obj[methodName]) == "function");
}
if (can(someObject, "quack"))
{
someObject.quack();
}
What is an interface in JavaScript?
An interface describes the shape of an object. (what properties it has, what type of values those properties contain, etc.) It's not an object itself - it's a more abstract description of what a particular object that implements the interface looks like.
For example, in the HTML standard, the DragEvent interface is described as such:
[Exposed=Window]
interface DragEvent : MouseEvent {
constructor(DOMString type, optional DragEventInit eventInitDict = {});
readonly attribute DataTransfer? dataTransfer;
};
dictionary DragEventInit : MouseEventInit {
DataTransfer? dataTransfer = null;
};
So DragEvent
is a type of MouseEvent
(which is another interface). It has a constructor function, so you can call new
on window.DragEvent
. When calling the constructor, you call it with the following arguments:
type
, which is aDOMString
(which is basically just any plain string)- An optional argument of type
DragEventInit
(which the documentation defines), which defaults to the empty object
A DragEvent instance also has a dataTransfer property
Note that the "interface" definition you're linking to is not exactly a JavaScript thing, but more of a thing for web APIs. In other implementations of JavaScript not in browsers (for example, in Node), an interface may mean something different (or nothing at all).
TypeScript, a widely used static type checker for JavaScript, has a very similar notion of interfaces, which describe the shape of a particular object. For example:
// Define the shape of a Foo object
interface Foo {
prop: string;
}
// Create an object that implements Foo
const someFoo: Foo = {
prop: 'somevalue'
};
Node.appendChild: Argument 1 does not implement interface Node in pure JS
You should invoke the function by specifying the parenthesis after the function name:
addCarBtn.addEventListener('click', () => formCars.appendChild(createBrandCar()));
Also, since you have already one list item on page load you should increment the size by 1:
let size = ++formCars.getElementsByTagName('li').length;
Demo:
const form = document.getElementById('newBrand');
const formCars = document.getElementById('formCars');
const addCarBtn = document.getElementById('addCar');
addCarBtn.addEventListener('click', () => formCars.appendChild(createBrandCar()));
function createBrandCar() {
const result = document.createElement('li');
let size = ++formCars.getElementsByTagName('li').length;
result.innerHTML = `
<legend>Car ${size}</legend>
<label>Name
<input type="text" name="carName${size}" />
</label>`;
return result
}
<form id="newBrand">
<fieldset>
<ul id="formCars">
<li>
<legend>Car 1</legend>
<label>Name
<input type="text" name="carName1" />
</label>
</li>
</ul>
</fieldset>
<button type="button" id="addCar">+</button>
</form>
Interface type check with Typescript
You can achieve what you want without the instanceof
keyword as you can write custom type guards now:
interface A {
member: string;
}
function instanceOfA(object: any): object is A {
return 'member' in object;
}
var a: any = {member: "foobar"};
if (instanceOfA(a)) {
alert(a.member);
}
Lots of Members
If you need to check a lot of members to determine whether an object matches your type, you could instead add a discriminator. The below is the most basic example, and requires you to manage your own discriminators... you'd need to get deeper into the patterns to ensure you avoid duplicate discriminators.
interface A {
discriminator: 'I-AM-A';
member: string;
}
function instanceOfA(object: any): object is A {
return object.discriminator === 'I-AM-A';
}
var a: any = {discriminator: 'I-AM-A', member: "foobar"};
if (instanceOfA(a)) {
alert(a.member);
}
Related Topics
JavaScript Thousand Separator/String Format
Sending Credentials with Cross-Domain Posts
Optional Chaining in JavaScript
My Very Simple Greasemonkey Script Is Not Running
Sort Mixed Alpha/Numeric Array
When Is the Body of a Promise Executed
Why Is {} + {} No Longer Nan in Chrome Console
Generating Non-Repeating Random Numbers in Js
How to Do Outerhtml in Firefox
How to Use Onclick() or Onselect() on Option Tag in a Jsp Page
Anonymous Class Instance - Is It a Bad Idea
What Is the Correct Term for Variable Shadowing in JavaScript
Activexobject in Firefox or Chrome (Not Ie!)
Inspect Extension's Chrome.Storage in Devtools
Generate PDF from HTML Using PDFmake in Angularjs
String.Replace' Weird Behavior When Using Dollar Sign ($) as Replacement
How to Get the (X, Y) Pixel Coordinates of the Caret in Text Boxes