What Techniques Can Be Used to Define a Class in JavaScript, and What Are Their Trade-Offs

What techniques can be used to define a class in JavaScript, and what are their trade-offs?

Here's the way to do it without using any external libraries:

// Define a class like this
function Person(name, gender){

// Add object properties like this
this.name = name;
this.gender = gender;
}

// Add methods like this. All Person objects will be able to invoke this
Person.prototype.speak = function(){
alert("Howdy, my name is" + this.name);
};

// Instantiate new objects with 'new'
var person = new Person("Bob", "M");

// Invoke methods like this
person.speak(); // alerts "Howdy, my name is Bob"

Now the real answer is a whole lot more complex than that. For instance, there is no such thing as classes in JavaScript. JavaScript uses a prototype-based inheritance scheme.

In addition, there are numerous popular JavaScript libraries that have their own style of approximating class-like functionality in JavaScript. You'll want to check out at least Prototype and jQuery.

Deciding which of these is the "best" is a great way to start a holy war on Stack Overflow. If you're embarking on a larger JavaScript-heavy project, it's definitely worth learning a popular library and doing it their way. I'm a Prototype guy, but Stack Overflow seems to lean towards jQuery.

As far as there being only "one way to do it", without any dependencies on external libraries, the way I wrote is pretty much it.

What is the best way to write Java Script Class

This often depends on your own taste and on how you want to use the class... But some months back I asked myself a similar question and found a useful description where different ways of defining classes in Javascript are explained . This could also be helpful to clarify things to you...

In javascript, what are the trade-offs for defining a function inline versus passing it as a reference?

Here's your answer:

http://jsperf.com/function-assignment

Option 2 is way faster and uses less memory. The reason is that Option 1 creates a new function object for every iteration of the loop.

Difference between creating a class in javascript to create an object and creating an class and object in Java

In javascript there are no classes. That being said you can simulate classes using few different ways.

Using a function and defining methods to its prototype

var Person = function(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
};

// you can define Person.fullname = function() {} straight to the function
// drawback to this is fullname function will be created everytimg you create a new Person
// instead you can create fullname in the prototype of the Person so it only gets created once

Person.prototype.fullname = function() {
return this.firstName + ' ' + this.lastName;
};

var person = new Person('John', 'Doe');
person.fullname(); // John Doe

Using object literals

var Person = {
firstName: 'John',
lastName: 'Doe',
fullname: function() {
return this.firstName + ' ' + this.lastName;
}
};

Person.fullname(); // John Doe

Person.firstName = 'Jane';
Person.fullname(); // Jane Doe

Using Singleton

var person = new function() {
this.firstName = 'John';
this.lastName = 'Doe';
this.fullname = function() {
return this.firstName + ' ' + this.lastName;
};
};

person.fullname(); // John Doe

person.firstName = 'Jane';
person.fullname(); // Jane Doe

If you are planning to get more closer to how java classes work, apply inheritance, etc then I suggest you to go with the first method. Below is example of a simple inheritance using prototype.

var Person = function(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
};

Person.prototype.fullname = function() {
return this.firstName + ' ' + this.lastName;
};

Person.prototype.getFirstName = function() {
return this.firstName;
};

Person.prototype.getLastName = function() {
return this.lastName;
};

Person.prototype.setLastName = function(lastName) {
return this.lastName = lastName;
};

Person.prototype.setFirstName = function(firstName) {
return this.firstName = firstName;
};

var Student = function(firstName, lastName, grade) {
this.grade = grade;
Person.call(this, firstName, lastName);
};

Student.prototype = Object.create(Person.prototype);

Student.prototype.getGrade = function() {
return this.grade;
};

var student = new Student('John', 'Doe', '5th');
student.fullname(); // John Doe
student.getFirstName(); // John
student.setFirstName('Jane');
student.getFirstName(); // Jane
student.getGrade(); // 5th

That should explain how you can use classes in javascript similar to you are used to in java.

What is difference between using dot notation and brackets to access object properties?

Long answer short, both behave exactly the same. But there would be instances where dot notation will not always work. For example:

var person = {
"first name": "john",
"last name": "doe",
"fullname": "john doe"
};

person.fullname; // john doe

// to access first name property of person
person.first name; // will not work
person["first name"]; // john

When extending a class and using properties of original class what do you put inside of super()

You need to pass the arguments that the parent constructor expects, in your case you will need to pass a height and a width. How to get these values is up to you. For a square, it makes sense to pass the same value for both parameters. You do no need to assign .w and .h yourself in the Square constructor, the Rectangle constructor already creates these properties. So it just be just

class Square extends Rectangle {
constructor(s) {
super(s, s)
}
}

Is it possible to create a method in a class that references another class in javascript?

The issue doesn't have anything to do with modules. It's Second.parameterA - that will reference a property directly on the Second class, but no such property exists.

To see it when you reference Second.parameterA, you'd have to do

class Second {
static parameterA = 'something'

or, after the class definition

Second.parameterA = 'something';

But if you want the argument passed into Second to be shown, you'll have to get First to have a reference to that Second instance somehow - perhaps with a parameter to method1, like:

  method1(secondInstance) {
this.parameter1 = this.parameter1 + " " + secondInstance.parameterA
return this.parameter1;
}
console.log(first.method1(second));

class First {
constructor(
parameter1,
parameter2
) {
this.parameter1 = parameter1;
this.parameter2 = parameter2;
}
method1(secondInstance) {
this.parameter1 = this.parameter1 + " " + secondInstance.parameterA
return this.parameter1;
}
}
class Second {
constructor(
parameterA,
parameterB
) {
this.parameterA = parameterA;
this.parameterB = parameterB;
}
}

const first = new First(
"someStringForParameter1",
"someStringForParameter2"
);

const second = new Second(
"someStringForParameterA",
"someStringForParameterB"
);

console.log(first.method1(second));

How can a attribute (which is a class) access the class to which it is an attribute?

Personally if I had to do this I'd state that A can have an attribute parent that fulfils a Role HasBar like so.

role HasBar {
has $.bar = 2;
}

class A {
has $.foo = 1;
has HasBar $.parent;
}

class B does HasBar {
has A $.a;
submethod TWEAK {
$!a = A.new( parent => self );
}
}

Attributes generally don't (and shouldn't) know if they are contained in another object. So if you want to be able to do that you need to tell them the connection exists. I prefer using Roles to do this as then your A class could be an attribute in lots of different things as long as the container has a bar accessible.

How do I create a new class which both of my classes will inherit methods and properties from?

You can use the extends keyword for inheritance in ES6 classes:

class GameObject {
constructor(sprite, positionX, positionY, speed) {
this.sprite = sprite;
this.positionX = positionX;
this.positionY = positionY;
this.speed = speed;
}
getCenterPoint() {
return new Point(this.positionX + 16, this.positionY + 16);
}
}

class Enemy extends GameObject {
constructor(...props) {
super(...props);
this.direction = Math.floor(Math.random() * 7) + 1;
this.direction *= Math.floor(Math.random() * 2) == 1 ? 1 : -1;
this.active = false;
}
}

class Player extends GameObject {
constructor(...props) {
super(...props);
this.animationFrame = true;
}
}

alternative way to define a function inside a class method

Disclaimer: the following remarks represent my opinion about the topic of pythonic code.

Avoid Inner Functions

You should avoid inner functions at all cost. Sometimes they're necessary, but most of the time they're an indication that you might want to refactor your code.

Avoid re-reading multiple times

I would also avoid calling pd.read_csv every time I want to perform some operation in the data. Unless there's a good reason to read the file over and over again, It's more performant to read it once and store it in a class attribute, or property.

PEP-8 Naming Conventions

Another important thing to consider, if you're trying to make your code more pythonic, is to try to follow the PEP8 naming conventions, unless you're working on legacy code that does not follow PEP-8.

Class Overkill

Finally, I think that creating a class for what you're doing seems a little overkill. Most of your methods are simply transformations that could be easily converted to functions. Aside from making your code less complex, It would improve its reusability.

How I would write the Analysis class

from __future__ import absolute_import, annotations

from pathlib import Path
from typing import Any, Collection, Iterable, Type, Union

import numpy as np
import pandas as pd
from pandas.core.dtypes.dtypes import ExtensionDtype # type: ignore

# Custom types for type hinting
Axes = Collection[Any]
NpDtype = Union[
str, np.dtype, Type[Union[str, float, int, complex, bool, object]]
]
Dtype = Union["ExtensionDtype", NpDtype]

# Auxiliary functions
def is_iterable_not_string(iterable: Any) -> bool:
"""Return True, if `iterable` is an iterable object, and not a string.

Parameters
----------
iterable: Any
The object to check whether it's an iterable except for strings,
or not.

Returns
-------
bool
True, if object is iterable, but not a string.
Otherwise, if object isn't an iterable, or if it's a string, return
False.

Examples
--------
>>> import numpy as np
>>> import pandas as pd
>>> class FakeIterable(int):
... def __iter__(self): pass
>>> print(is_iterable_not_string('abcde'))
False
>>> print(is_iterable_not_string(bytes(12345)))
False
>>> print(is_iterable_not_string(12345))
False
>>> print(is_iterable_not_string(123.45))
False
>>> print(is_iterable_not_string(type))
False
>>> print(is_iterable_not_string(list)) # Type list isn't iterable
False
>>> print(is_iterable_not_string(object))
False
>>> print(is_iterable_not_string(None))
False
>>> print(is_iterable_not_string(list())) # Empty list is still iterable
True
>>> # `FakeIterable` has a method `__iter__`, therefore it's considered
>>> # iterable, even though it isn't.
>>> print(is_iterable_not_string(FakeIterable(10)))
True
>>> print(is_iterable_not_string(list('abcde')))
True
>>> print(is_iterable_not_string(tuple('abcde')))
True
>>> print(is_iterable_not_string(set('abcde')))
True
>>> print(is_iterable_not_string(np.array(list('abcdef'))))
True
>>> print(is_iterable_not_string({col: [1, 2, 3, 4] for col in 'abcde'}))
True
>>> print(is_iterable_not_string(
... pd.DataFrame({col: [1, 2, 3, 4] for col in 'abcde'}))
... )
True
>>> print(is_iterable_not_string(pd.DataFrame()))
True

Notes
-----
In python, any object that contains a method called `__iter__` considered
an “iterable”. This means that you can, in theory, fake an “iterable”
object, by creating a method called `__iter__` that doesn't contain any
real implementation. For a concrete case, see the examples section.

Python common iterable objects:

- strings
- bytes
- lists
- tuples
- sets
- dictionaries

Python common non-iterable objects:

- integers
- floats
- None
- types
- objects

"""
return (not isinstance(iterable, (bytes, str))
and isinstance(iterable, Iterable))

def prepare_dict(data: dict) -> dict:
"""Transform non-iterable dictionary values into lists.

Parameters
----------
data : dict
The dictionary to convert non-iterable values into lists.

Returns
-------
dict
Dictionary with non-iterable values converted to lists.

Examples
--------
>>> import pandas as pd
>>> d = {'a': '1', 'b': 2}
>>> prepare_dict(d)
{'a': ['1'], 'b': [2]}
>>> pd.DataFrame(d) # doctest: +ELLIPSIS
Traceback (most recent call last):
...
ValueError: If using all scalar values, you must pass an index
>>> pd.DataFrame(prepare_dict(d))
a b
0 1 2

Notes
-----
Use this function to prepare dictionaries, before calling
`pandas.DataFrame`, to make sure all values have the correct format.
"""
return {
key: value if is_iterable_not_string(value) else [value]
for key, value in data.items()
}

def check_dict_value_lens(data: dict) -> bool:
"""Check whether all values from dictionary have the same lenght.

Parameters
----------
data : dict
The dictionary to check the values lenghts.

Returns
-------
bool
True, if all `data` values have the same lenght. False otherwise.
"""
min_len = min(map(lambda value: len(value), data.values()))
return all(len(value) == min_len for value in data.values())

def read_file(path: Path | str, **kwargs: Any) -> pd.DataFrame:
"""
Read a DataFrame from a file.

Supported file types are:
- `.csv`
- `.xlsx`, `.xls`, `.xlsm`, `.xlsb` (Excel files)
- `.json`
- `.parquet`
- `.feather`
- `.html`

Parameters
----------
path : Path | str
The path to the file.
kwargs : Any
Keyword arguments to pass to pandas io functions.

Returns
-------
pd.DataFrame
The DataFrame read from the file.

Raises
------
ValueError
If the file type not supported.
FileNotFoundError
If the file doesn't exist.
"""
_path = Path(path)
path = str(path)

if not _path.is_file():
raise FileNotFoundError(f"File {path} does not exist.")
if _path.suffix in [".csv", ".txt"]:
return pd.read_csv(path, **kwargs)
if ".xls" in _path.suffix:
return pd.read_excel(path, **kwargs)
if _path.suffix == ".json":
return pd.read_json(path, **kwargs)
if _path.suffix == ".pickle":
return pd.read_pickle(path, **kwargs)
if _path.suffix == ".html":
return pd.read_html(path, **kwargs)
if _path.suffix == ".feather":
return pd.read_feather(path, **kwargs)
if _path.suffix in [".parquet", ".pq"]:
return pd.read_parquet(path, **kwargs)
raise ValueError(f"File {path} has an unknown extension.")

def highlight(df: pd.DataFrame) -> pd.DataFrame:
"""Highlight a DataFrame.

Parameters
----------
df : pd.DataFrame
The DataFrame to highlight. Required indexes:
- ["USL", "LSL", "1", "2", "3", "4", "5"]

Returns
-------
pd.DataFrame
The DataFrame with highlighted rows.
"""
# The dataframe cells background colors.
c1: str = "background-color:red"
c2: str = "background-color:yellow"
c3: str = "background-color:green"

# Rows over which the highlighting function should apply
rows: list[str] = ["1", "2", "3", "4", "5"]

# First boolean mask for selecting the df elements
m1 = (df.loc[rows] > df.loc["USL"]) | (df.loc[rows] < df.loc["LSL"])

# Second boolean mask for selecting the df elements
m2 = (df.loc[rows] == df.loc["USL"]) | (df.loc[rows] == df.loc["LSL"])

# DataFrame with same index, and column names as the original,
# but with filled empty strings.
df_highlight = pd.DataFrame("", index=df.index, columns=df.columns)

# Change values of df1 columns by boolean mask
df_highlight.loc[rows, :] = np.select(
[m1, m2], [c1, c2], default=c3
)

return df_highlight

class Analysis:
"""
Read a dataframe, and help performing some analysis in the data.

Parameters
----------
path_or_data : str | Path | pd.DataFrame
The path to a file, or a dataframe to analyze.

Attributes
----------
_data : pd.DataFrame
The data read from the file.
_path : str | Path
The path to the file.

Examples
--------
>>> data = {
... 'A-A': [
... 0.37, 0.39, 0.35, 0.35, 0.36, 0.34, 0.35, 0.33, 0.36, 0.33,
... ],
... 'A-B': [
... 10.24, 10.26, 10.22, 10.23, 10.19, 10.25, 10.2, 10.17, 10.25,
... 10.17,
... ],
... 'A-C': [
... 5.02, 5.04, 5.0, 5.05, 5.07, 5.03, 5.08, 5.05, 5.08, 5.03,
... ],
... 'A-D': [
... 0.63, 0.65, 0.63, 0.65, 0.67, 0.66, 0.69, 0.62, 0.69, 0.62,
... ],
... 'A-E': [
... 20.3, 20.32, 20.28, 20.45, 20.25, 20.33, 20.22, 20.4,
... 20.45, 20.22,
... ],
... }
>>> index = ['Tg', 'USL', 'LSL', '1', '2', '3', '4', '5', 'Max', 'Min']
>>> analysis = Analysis.from_dict(data, index=index)
>>> analysis.get_std()
A-A 0.011402
A-B 0.031937
A-C 0.019494
A-D 0.025884
A-E 0.097211
dtype: float64
"""

_path: Path | str | None = None
_data: pd.DataFrame | None = None

@property
def path(self) -> str | Path:
"""Get the path to the file.

Returns
-------
str | Path
The path to the file.

Raises
------
ValueError
If `_path` is `None`.
"""
if self._path is None:
raise ValueError("Path not set.")
return str(self._path)

@path.setter
def path(self, path: str | Path):
"""Set the path of the file to analyze.

Parameters
----------
path : str | Path
The path of the file to analyze.
Path should point to a `.csv` file.

Raises
------
FileNotFoundError
If the path not found.
"""
_path = Path(path)
if _path.is_file():
self._path = str(path)
else:
raise FileNotFoundError(f"Path {path} does not exist.")

@property
def data(self) -> pd.DataFrame:
"""Dataframe read from `path`.

Returns
-------
pd.DataFrame
The dataframe read from `path`.
"""
if self._data is None:
self._data = self.get_data()
return self._data

@data.setter
def data(self, data: pd.DataFrame):
"""Set the data to analyze.

Parameters
----------
data : pd.DataFrame
The data to analyze.
"""
self._data = data

def __init__(self, path_or_data: str | Path | pd.DataFrame):
"""Initialize the Analyzer.

Parameters
----------
path_or_data : str | Path | pd.DataFrame
The path to a file, or a dataframe to analyze.

Raises
------
ValueError
If `path_or_data` not a `str`, `Path`, or `pd.DataFrame`.
"""
if isinstance(path_or_data, (str, Path)):
self.path = path_or_data
elif isinstance(path_or_data, pd.DataFrame):
self.data = path_or_data
else:
raise ValueError(f"Invalid type {type(path_or_data)}.")

def get_data(self) -> pd.DataFrame:
"""Read the data from the file.

Returns
-------
pd.DataFrame
The dataframe read from the `path` property.
"""
return read_file(self.path)

def get_std(self) -> pd.Series:
"""Calcuate the standard deviation of every column.

Returns
-------
pd.Series
The standard deviation of every column.
"""
return self.data.loc["1":"5"].apply(lambda x: x.std()) # type: ignore

def highlight_frame(
self, round_values: int | None = None
) -> pd.io.formats.style.Styler: # type: ignore
"""Highlight dataframe, based on some condition.

Parameters
----------
round_values: int | None
If defined, sets the precision of the Styler object with the
highlighted dataframe.

Returns
-------
pd.io.formats.style.Styler
The Styler object with the highlighted dataframe.
"""
highlight_df = self.data.style.apply(highlight, axis=None)
if isinstance(round_values, int) and round_values >= 0:
return highlight_df.format(precision=round_values)
return highlight_df

@classmethod
def from_dict(
cls,
data: dict,
index: Axes | None = None,
columns: Axes | None = None,
dtype: Dtype | None = None,
) -> Analysis:
"""Create an Analysis object from a dictionary.

Parameters
----------
data : dict
The dictionary to create the Analysis object from.
index : Index or array-like
Index to use for resulting frame. Defaults to RangeIndex, if
no indexing information part of input data and no index provided.
columns : Index or array-like
Column labels to use for resulting frame when data doesn't have
them, defaulting to RangeIndex(0, 1, 2, ..., n).
If data contains column labels, will perform column selection
instead.
dtype : dtype, default None
Data type to force. Only a single dtype allowed. If None, infer.

Returns
-------
Analysis
An instance of the `Analysis` class.

Raises
------
ValueError
If dictionary values have different lenghts.
"""
data = prepare_dict(data)
if check_dict_value_lens(data):
return cls(
pd.DataFrame(data, index=index, columns=columns, dtype=dtype)
)
raise ValueError(
f"Dictionary values don't have the same lenghts.\nData: {data}"
)

if __name__ == "__main__":
import doctest
doctest.testmod()



Related Topics



Leave a reply



Submit