Javascript - OOP

한윤서·2021년 11월 12일
0

Web

목록 보기
2/6

1. Object Basics

Class : Template of collection of related data and functionality (Added recently)
Object : Instance of class. Before the inclusion of class, js normally implemented object directly by defining it in several ways
Collection of related data and functionality

Syntax patter of an object :

const objectName = {
  member1Name: member1Value,
  member2Name: member2Value,
  member3Name: member3Value
};

The variable type of the member of object can be anything from integer variables to strings, arrays and functions.

Object is composed of series of keys and values -> key : value

Variables in Objects are called properties
Functions in Objects are called methods

Ex)

const person = {
  name: ['Bob', 'Smith'],
  age: 32,
  gender: 'male',
  interests: ['music', 'skiing'],
  bio: function() {
    alert(this.name[0] + ' ' + this.name[1] + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
  },
  greeting: function() {
    alert('Hi! I\'m ' + this.name[0] + '.');
  }
};

The following example is reffered to an object literal as we have written out the object contents (the values of variable and functions) as we have created it -> Contrast to objects instantiated from classes

Dot notation

Dot notation is used to access the encapsulated object's properties and methods.

Setting an object as another object's member is also possible. Access by using 2 dot notations :

//name was originally an array in the person class
//name = ['Bob', 'Smith'],
name : {
    first : 'Bob',
    last : 'Smith'
},
person.name.first
person.name.last

Bracket notation

Another way to acess object properties :

//Instead of using
person.age
person.name.first
//Use bracket notations
person['age']
person['name']['first']

Note : always add '' within the property name

Normally use the dot notation to get property value that is defined beforehand and bracket notation for property values that are defined during runtime
Ex)

function printValue(obj, key) {
    console.log(obj[key]);
}
printValue(ellie, 'name');

The following function passes the 'name' argument to the printValue function and the property value is defined during the code runtime. Therefore only obj[key] would be able to modify the value.

Setting object members

Set the value of object members by declaring the member you want to set using dot or bracket notation

person.age = 45;
person['name']['last'] = 'Cratchit';

It is also possible to create new members of the object after defining the object:

//Declare new properties and methods
person['eyes'] = 'hazel';
person.farewell = function() { alert("Bye everybody!"); }

Users are also able to delete properties later :

delete person.age;

this

this keyword refers to the current object the code is being written inside.
Ex)

greeting: function() {
  alert('Hi! I\'m ' + this.name.first + '.');
}

The following function's this refers to the person object itself.

this is good as it always ensures that the correct values are used when a member's contest changes : There might be more than 1 object instances which may cause confusion if you use the object name rather than this

2. Object-oriented programming

Basic idea of OOP : Use objects to model real world things that we want to represent inside our programs.

General concept of OOP

Defining object template - class

Ex) Display information about students and teachers at school

First, we would define the gerenic data and functionality of a class ofperson. Info related to a person would be name, age, gender etc. and also include a function to introduce themselves.
This is refered to an abstraction : Create simple model of a more complex thing. There is no direct info about each variables however the structure is only defined.

Creating actual objects

From our class, we can create object instances - objects that contain the data and functionality definied in the class.
When an object instance is created from a class, the class's constructor function is run to create it.
Instantiation : Object instance is instantiated from class

Inheritance for specialized classes

However in the follwing example, we want more specific types of people : students and teachers
In OOP it is possible to create new classes based on other classes - These new child classes(=subclasses) can be made to inherit the data and code features of parent classes.

Therefore you can reuse functionality common to all the object tupes (and no need to duplicate it). Were functionality defers, you can define specialized features directly

After defining these new classes, it is possible to make the object instances of the following child classes

Creating new objects

There are several ways to create a new object :

  • Object initializers (literals)
  • Constructors
  • Object.create method
  • The Object() constructor

Object initializers

Object initializers are creating objects with leteral notatoion. The syntax is :

var obj = { property_1:   value_1,   // property_# may be an identifier...
            2:            value_2,   // or a number...
            // ...,
            'property n': value_n }; // or a string

Object initializers are expressions, and each object initializer results in a new object being created whenever the statement in which it appears is executed. Identical object initializers create distinct objects that will not compare to each other as equal -> are instances of Object.

Constructors and object instances

Constructor functions are used to define and initialize objects and their features. Constructors provide the means to create many objects effectively.

Ex) Without a constructor, to define the properties of a person :

function createNewPerson(name) {
  const obj = {};
  obj.name = name;
  obj.greeting = function() {
    alert('Hi! I\'m ' + obj.name + '.');
  };
  return obj;
}
const salva = createNewPerson('Salva');
salva.name;
salva.greeting();

Would have to define the function to create a person and also have to call the properties and methods additionally to make changes.

Using a constructor would make things much simpler by :

function Person(name) {
  this.name = name;
  this.greeting = function() {
    alert('Hi! I\'m ' + this.name + '.');
  };
}

A constructor does not retrun anything or explicitly creat an object - defines properties and methods. The this is used to point to the object instances created.

To call a constructor to create objects :

let person1 = new Person('Bob');
let person2 = new Person('Sarah');

The new is used to tell the browser we want to create a new object instance : Instantiate an Object

The Object() constructor

The Object() represents a constructor for generic objects. It normally generates an empty project
The following

let person1 = new Object();

would store an empty object in the person1 variable. Adding the properties and methods to the object can be done later by using dot notation :

person1.name = 'Chris';
person1['age'] = 38;

Another way is to pass the object literals as a parameter to the Object() constructor :

let person1 = new Object({
  name: 'Chris',
  age: 38,
  greeting: function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
});

The create() method

For simplicity, it would be sometimes prefered to create objects without first creating constructors (ex: for situations were you are only creating few instances of object).

You can create a new object, using an existing object as the prototype of the newly created object

let person2 = Object.create(person1);
//Note that person1 is an object (not an object instance) in the following example

The following person1's constructor is copied to the new person2 object. person2 would then be able to access the properties and methods

Note : The create() method can be used both to copy an object and also an object instance

Useful operators for objects

in operator

The in operator checks the existence of a property in an object and returns a boolean value

console.log('name' in Yoonseo);	//return true
console.log('success' in Yoonseo);	//return true
console.log('failure' in Yoonseo);	//return false

for...in operator

for...in syntax is used to iterate through an object till it finds the appropriate property

for(key in Yoonseo) {
    console.log(key);
}

The following function iterates and gets all of the key in object to be shown in console

Copying object

It is available to copy an object to another empty object by using Object.assign()

Object.assign(cloneObject, oritinalObject);

It is also possible to overwrite more than 1 functions

const fruit1 = {color : 'red'};
const fruit2 = {color : 'blue', size: 'big'};
const mixed = Object.assign({}, fruit1, fruit2);

The empty object is overwrited by 2 functions fruit2 and fruit1. fruit1 and fruit2 both has the color property. The one with higher priority is fruit 2 as the objects on the right side overwrites the one in the left.

Object values

Using Object.values(), it returns an array of the values stored in the object inserted as a parameter

key values

Using Object.keys(), it returns an array of the keys stored in the object inserted as a parameter

3. Object prototypes

JavaScript is often described as a prototype-based language — to provide inheritance, objects can have a prototype object, which acts as a template object that it inherits methods and properties from.

An object's prototype object may also have a prototype. The following relationship can repeat which makes a prototype chain

For an object instance made, a link is formed between the instance and its prototypes. Properties and methods are found by walkng up the chain of prototype.

Understanding prototype objects

Ex) We have a person1 object instance, instantiated from the class Person :

function Person(first, last, age, gender, interests) {

  // property and method definitions
  this.name = {
    'first': first,
    'last' : last
  };
  this.age = age;
  this.gender = gender;
}
//Object instance
let person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);

In the person1 object, not only the user is able to access properties and methods defined in the constructor Person but is also able to access the Object prototype's method and properties.

If you call a method or property defined in the Object prototype
ex)

person1.valueOf()

valueOf() returns the value of the object it is called on. The process is :

  • The browser initially checks to see if the person1 object has a valueOf() method available on it
  • If doesn't, browser checks to see if the person1's prototype object has a valueOf() method available on it. It goes up its prototype till it finds the following property or method.

Prototype property

Not all of the properties and methods defined in the Object class can be accessed by the person1 object. Some are inherited and some aren't

The inherited ones are the ones defined on the prototype property : Ones that begin wih Object.prototype.

The prototype property's value is an object, storing properties and methods that we want to be inherited by objects further down the prototype chain.

  • Object.prototype.toString() and Object.prototype.valueOf() are available to any object types that inherit from Object.prototype
  • This includes any new object instances created from the Person() constructor aswell

The prototype property of an object instance can be accessed as it is defined as __proto__ in one of the properties

Revisiting create()

Previously the create() function has been described to be used to create a new object instance by copying the class of an existing object instance :

let person2 = Object.create(person1);

What it actually is doing is creating a new object from a specified prototype object. In the following example, person2 is created by using person1 as a prototype object

person2.__proto__

Following code will return the person1 object

Constructor property

Every constructor function has a prototype property(property that can be accessed to the inherited object instances) which is an object containing a constructor property

The following constructor property points to the original constructor form.

In the previous example, properties defined on the Person.prototype property can be accessed by the instance objects created using the Person() constructor. Therefore the constructorproperty is available on both person1 and person2

Ex)

person1.constructor
person2.constructor

Both of the codes return the Person() constructor.

It is also possible to put parenthesis at end of the constructor property to create another object instance from that constructor (as the constructor is a function after all

Ex)

let person3 = new person1.constructor('Karen', 'Stephenson', 26, 'female', ['playing drums', 'mountain climbing']);

The following constructor property can be useful when you want to create a new instance and don't have a reference to the original constructor easily available for some reason.

Also if you have an object instance and you want to return the name of the constructor it is an instance of, you can use the following:

instanceName.constructor.name

Modifying prototypes

By modifying the prototype property of a constructor function -> methods added to the prototype are then available to be used by all object intances created by the constructor

Ex) Add a new function to Person() constructor's prototype :

Person.prototype.farewell = function() {
  alert(this.name.first + ' has left the building. Bye for now!');
};
//Object insatnce of Person() can access the newly added function
person1.farewell();

Note : Although the object instance is instantiated before the newly included method to the prototype, the instantiated object can still access to the method.

Use of modifying prototypes

The prototype property is rarely used to define or modify properties of prototypes due to the lack of flexibility. Ex)

Person.prototype.fullName = this.name.first + ' ' + this.name.last;

The following code is to define a person's name by using name.first and name.last. However this is done outside of the constructor function and therefore the this keyword would be referencing the global scope and therefore return undefined. Therefore it would be better to define properties inside the constructor.

Properties are noramlly defined within the constructor functions. prototype is normally used to defined the methods of a constructor for a better desing of code to seperate properties and methods.

// Constructor with property definitions
function Test(a, b, c, d) {
  // property definitions
}

// First method definition
Test.prototype.x = function() { ... };
// Second method definition
Test.prototype.y = function() { ... };
// etc.

3. Inheritance in Javascript

Prototypal inheritance

Inheritance by using protype chains involved built-int browser functions (ex : Object()). How would javascript create an object that inherits from another object.

The following process is described by using an example

Start with constructor

function Person(first, last, age, gender, interests) {
  this.name = {
    first,
    last
  };
  this.age = age;
  this.gender = gender;
  this.interests = interests;
};

Person.prototype.greeting = function() {
  alert('Hi! I\'m ' + this.name.first + '.');
};

The following constructor defines all the properties inside function and methods in global scope using prototype

We would want to create a Teacher class that inherits all members from Person but also include :
1. A new property, subject
2. An updated greeting method, with a more formal greeting sentence

Defining a Teacher() constructor function

Define a Teacher() constructor :

function Teacher(first, last, age, gender, interests, subject) {
  Person.call(this, first, last, age, gender, interests);
  //New property that parent object does not have
  this.subject = subject;
}
  • .call() function allows you to call a constructor function defined somewhere else, but in the current context -> inherit
  • this in the 1st parameter specifies the value (in this case the object from Teacher class) to use when running the function
  • We want the Teacher() function to take same parameters as the Person() constructor therefore specify them all as parameters in call() function
  • The last .subject property is a property that Person() does not have

Setting Teacher()'s prototype and constructor reference

The newly defined constructor of Teacher() has a prototype property, which by default just contains an object with a reference to the constructor function itself -> constructor property. It does not contain the methods of Person constructor's prototype property.

Therefore we need to get Teacher() to inherit the methods defined on Person()'s prototype

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

The following code uses create() to create a new object and make it the value of Teacher.prototype. The created new object has Person.prototype as its prototype and inherits all methods available.

Note that the constructor prototype in the Teacher.prototype also points to Person() as it has been inherited from Person.prototype.

The following problem can be fixed by setting the source code using :

Object.defineProperty(Teacher.prototype, 'constructor', {
    value: Teacher,
    enumerable: false, // so that it does not appear in 'for in' loop
    writable: true });

for the Teacher.prototype.constructor to return Teacher() as expected

Giving Teacher() a new additional method

To add a new method, the easiest way would be to define it on Teacher()'s prototype :

Teacher.prototype.greeting = function() {
  let prefix;

  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
};

Object member summary

There are mainly 4 types of property/methods in JS

  1. Those defined in a constructor function that are give to object instances.
  2. Those defined on constructor's prototype, which are inherited by all instances and inheriting object classes
  3. Those defined on an object instance
  4. Those defined directly on the constructor themselves, that are only available on the constructor (=class and not on object instance --> Reduce the need to instantiate an object) : recognized by only being chained directly onto a constructor (ex : Object.keys in which Object is the class)
    -> These are called Static properties / methods and is mentioned by using the keyword : static
    Ex)
    class User {
      static staticMethod() {
        alert(this === User);
      }
    }

    User.staticMethod(); // Accessed by using class name

IMPORTANT : Acknowledge the difference between the 1st and 2nd type -> 1st is defined in constructor and 2nd is defined in prototype.

Getters and Setters

Getter : Method that gets the value of a specific property. Effective when you want to reflect the status of an internal variable witout requiring the use of explicit method calls.
Setter : Method that sets the value of a specific property or whenever a specified property is attempted to be changed.

Defining a getter and setter on new objects in object initializers are done by including the get and set keywords in code

class Teacher extends Person {
  constructor(first, last, age, gender, interests, subject, grade) {
    super(first, last, age, gender, interests);
    // subject and grade are specific to Teacher
    this.subject = subject;
    this.grade = grade;
  }

  get subject() {
    return this._subject;
  }

  set subject(newSubject) {
    this._subject = newSubject;
  }
}

The following code has a getter and setter for the subject property.

Note : After a getter and setter is defined for a specific property, when you try to get the value of the property or access it to change the value via this., automatically the getter and setter function is called.

When you try to get the subject property value by this.subject, the get subject() getter is called.

When you try to change the subject property value by modifying using this.subject, the set subject() setter is called.

This leads to an infinite loop if the getter and setter is formed by :

  get subject() {
    return this.subject;
  }

  set subject(newSubject) {
    this.subject = newSubject;
  }

where the getter and setter functions both have the this.subject code. This leads to an infinite loop as the code calls the getter or setter. Therefore the getter and setter should have a different syntax for the property : this._subjcet

Although the syntax is different, the getter and setter function for the subject property is set to subject therefore the syntax defined within the getter and setter does not matter.

ECMAScript 2015 Class

ECMAScript 2015 introduces class syntax to JavaScript as a way to write reusable classes using easier, cleaner syntax, which is more similar to classes in C++ or Java.

Does the same functionality of the previous object, object instances and prototypes but in a much more easier code structure

The general structure of the new syntax :

class Person {
  constructor(first, last, age, gender, interests) {
    this.name = {
      first,
      last
    };
    this.age = age;
    this.gender = gender;
    this.interests = interests;
  }
  //Reduces the need to add by using prototypes as it will automatically add the function in prototype
  greeting() {
    console.log(`Hi! I'm ${this.name.first}`);
  };

  farewell() {
    console.log(`${this.name.first} has left the building. Bye for now!`);
  };
}

The class statement indicates that we are creating a new class :

  • The constructor() defines a constructor function
  • greeting() and farewell() are class methods : The benefit of the class syntax is that it reduces the need to add by using prototypes as it will automatically add the function in prototype
  • Another advantage of the class syntax is that it enables the use of static methods and properties that was introduced previously

Then instantiate object instances using the new operator :

let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
han.greeting();

Inheritance with class syntax

To extend the properties and methods from the Person class to Teacher class using modern class syntax :

class Teacher extends Person {
  //Newly added property and methods for Teacher class
  constructor(subject, grade) {
    this.subject = subject;
    this.grade = grade;
  }
}

We use the extends keyword to tell the JS the calss we want to base our new class on. However unlike the old style where the new operator initializes the this to the new allocated object, it isn't automatically initialized for a class defined by extends

For sub-classes, this initialization to a newly allocaated object is always dependant on by calling the parent class constructor. Therefore the super() operator is used to call the parent's constructor.

Also it is required for the sub-class to inherit properties from the parent class. Therefore include arguments in the super() operator, passing the necessary arguments to initialize the parent class properties in our sub-class thereby inheriting it.

class Teacher extends Person {
  constructor(first, last, age, gender, interests, subject, grade) {
    // Now 'this' is initialized by calling the parent constructor.
    super(first, last, age, gender, interests);

    // subject and grade are specific to Teacher
    this.subject = subject;
    this.grade = grade;
  }
}

instanceof

instanceof is a code that returns a boolean value based on whether a certain object is an instance of a class. Ex)

console.log(rectangle instanceof Rectangle);	//Return true
console.log(triangle instanceof Rectangle);	//Return false
console.log(triangle instanceof Object);	//Return true (as every object instance has Object in hierarchy)

4. JSON

HTTP

HTTP(Hypertext Transfer Protocal) is a protocal transmitting hypermedia documents. It was designed for communication between web browsers and web servers.

What is JSON

JSON(JavaScript Object Notation) is a standard text-based format for representing structured data, based on js object. It is normally used for transmitting data in web between client and server (ex : Send some data from server to client)

Although it resembles JS object literal syntax, it can be used independently from JS and many programming environments feature the ability to read(parse) and generate JSON.

JSON exists as a string - Useful when you want to transmit data across a network. The transmitted data needs to be converted to a native JS object when you want to access the data.
-> JavaScript provides a global JSON object that has methods available for converting between the two
-> The similar structure between JSON and normal JS objects make it easy for conversion between JSON data to JS object

JSON structure

JSON is composed of strings in which the general format very much resembles JavaScript object literal format. It is possible to include basic data types in JSON like a standard Javascript object.

Ex)

{
  "squadName": "Super hero squad",
  "homeTown": "Metro City",
  "formed": 2016,
  "active": true,
  "members": [
    {
      "name": "Molecule Man",
      "powers": [
        "Radiation resistance",
        "Turning tiny",
        "Radiation blast"
      ]
    },
    {
      "name": "Madame Uppercut",
      "powers": [
        "Million tonne punch",
        "Damage resistance",
        "Superhuman reflexes"
      ]
    }
  ]
}

If we load this string to a JS program, ex) parsed it into a variable superHeroes we could access the data inside it using dot and bracket notation:

superHeroes.homeTown
superHeroes['active']

To access further down the hierarchy, you have to chain the property name together with array incexes together. To access the 3rd power of the 2nd hero :

superHeroes['members'][1]['powers'][2]

JSON notes

  • JSON is purely a string with a specified data format -> It contains only properties, no methods (ignored when object is converted to JSON)
  • JSON requires double quotes to be used around strings and property names
  • Even a single misplaced comma or colon can cause a JSON file to go wrong, and not work
  • Unlike in JavaScript code in which object properties may be unquoted, in JSON only quoted strings may be used as properties
  • key-value pairs
  • Used for serialization and transmission of data between the network and the network connection
  • Independent programming language and platform

Working through a JSON example

The following example describes the process of how the web page uses JSON to send it to server and request for a JS object to be able to use them.

For the example we use the JSON defined previously, which has info about superheros.

Retrieve JSON data and convert to Object

We use an API called XMLHttpRequest (XHR) to obtain the JSON. The XMLHttpRequest object allows us to make network requests to retrieve resources from server visa JS.

1) First store the URL of the JSON into a variable

let requestURL = 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json';

2) To request, we need to create a new request object instance from the XMLHttpRequest constructor, using the new keyword.

let request = new XMLHttpRequest();

3) Now we need to open the request using the open() method.

request.open('GET', requestURL);
  • The 'GET' parameter defines the method to use when making network request : retrieve simple data
  • requestURL defines the URL to make request to

4) Set the responseType to JSON so that XHR knows that the server will be returning JSON, and that this should be converted to JS object.
Then send the request with send() method

request.responseType = 'json';
request.send();
  1. Get the response to return from server then using the object obtained
request.onload = function() {
  const superHeroes = request.response;
  populateHeader(superHeroes);
  showHeroes(superHeroes);
}

Transfering between JSON and object

The above examples used the XHR request to conver the JSON response directly into a JavaScript object in the server automatically.

However sometimes, automatic conversion is not available and therefore

  • When we receive a raw JSON string, we need to convert it to object ourselves
  • When we want to send a JavaScript object across a network, we need to convert it to JSON before string

The process of transfering an object to JSON file : serialize
-> stringify() : Accepts an object as parameter, and returns the equivalent JSON string
Process of transfering the JSON file back to an object : deserialize
-> parse() : Accepts a JSON string as parameter, and returns the corresponding JS object

//Javascript object defined
const rabbit = {
    name : 'tori',
    size : null,
    birthDate : new Date(),
    jump: () => {
      console.log(`${name} can jump!`);
    },
};

//1. Object to JSON : serialize

//Convert the javascript object defined into a JSON string
//The new Date() returns a string of the date and birthDate has a string value of the date stored
//The method is truncated and not included in the string
json = JSON.stringify(rabbit);
console.log(json);

//Using call back function to choose specific values within the string
//output : {"name" : "tori"}
json = JSON.stringify(rabbit, ['name']);
console.log(json);

//Using call back function to modify string values of key
//change name value to "ellie"
json  = JSON.stringify(rabbit, (key,value) => {
    console.log(`key: ${key}, value: ${value}`);
    //If key is name, return string of 'ellie', else return the value of each key
    return key === 'name'? 'ellie' : value;
});

//2. JSON to object
json = JSON.stringify(rabbit);
const obj = JSON.parse(json);
//The "json" JSON file obtained from the rabbit object is parsed
//obj stores the parsed "json" in the form of JS object 
console.log(obj);
//The following method causes an error 
//This is because obj is parsed from "json" which does not contain a method as it is a JSON file
obj.jump();

//The rabbit object can use the getter function defined in the Date() object
//obj object can't as the "json" stores birthDate key's value as a string type (not object type) -> has no getter()
console.log(rabbit.birthDate.getDate());
console.log(obj.birthDate.getDate());

//Add the following code while obj parses json
//When key value is birthDate, returns a new Date() object (not the original string value stored in json)
const obj = JSON.parse(json, (key,value) => {
    console.log(`key: ${key}, value: ${value}`);
    return key === 'birthDate' ? new Date(value) : value;
});
profile
future eo

0개의 댓글