JS object oriented

Posted by jasonmills58 on Tue, 08 Mar 2022 11:34:51 +0100

Object can encapsulate multiple associated data to better describe a thing. Using objects to describe things is more conducive for us to separate real things into a data structure in the code: therefore, some programming languages are pure object-oriented programming languages, which are better than Java; When creating any class, you need to abstract it first, and then implement it according to the reality;

JavaScript actually supports a variety of programming paradigms, including functional programming and object-oriented programming: objects in JavaScript are designed as an unordered set of attributes, such as a hash table, which is composed of key and value; Key is an identifier name, and value can be any type or other object or function type; If the value is a function, we can call it an object method;

1. How to create an object?

In the early days, the most common way to create an Object was to use the Object class and use the new keyword to create an Object:
This is because many early JavaScript developers came from Java, and they are more used to creating an object through new in Java;
Later, for the sake of convenience, many developers directly created objects in the form of literal quantity: p this form looks more concise, and the cohesion between objects and attributes is stronger, so this method became popular later;

// Create an object to abstract (describe) a person
// 1. Creation method 1: create through new Object()
var obj = new Object()
obj.name = "why"
obj.age = 18
obj.height = 1.88
obj.running = function() {
  console.log(this.name + "Running~")
}

// 2. Creation method 2: literal form
var info = {
  name: "kobe",
  age: 40,
  height: 1.98,
  eating: function() {
    console.log(this.name + "I'm eating~")
  }
}

2. Control of attribute operations

Previously, our attributes are defined directly inside the object or added directly inside the object: but when doing so, we can't limit this attribute: for example, can this attribute be deleted through delete? Is this property traversed during for in traversal? If we want to control an attribute accurately, we can use the attribute descriptor. Through the attribute descriptor, you can accurately add or modify the attributes of the object; The property descriptor requires object Define property to add or modify properties;

Object. The defineproperty () method will directly define a new property on an object, or modify an existing property of an object and return the object. Three parameters can be received: obj the object whose attribute is to be defined; prop name or Symbol of the attribute to be defined or modified; Descriptor the attribute descriptor to be defined or modified; Return value: the object passed to the function.

2.1 data descriptor

The data descriptor has the following four characteristics:

[[Configurable]]: indicates whether the attribute can be deleted through delete, whether its characteristics can be modified, or whether it can be modified to access the attribute descriptor;

When we directly define a property on an object, the [[Configurable]] of this property is true;
When we define an attribute through the attribute descriptor, the [[Configurable]] of this attribute defaults to false;
[[Enumerable]]: indicates whether the attribute can be passed through for in or object Keys() returns the attribute;

When we directly define an attribute on an object, the [[Enumerable]] of this attribute is true;
When we define an attribute through the attribute descriptor, the [[Enumerable]] of this attribute defaults to false;
[[Writable]]: indicates whether the value of the attribute can be modified;

When we define a property directly on an object, the [[Writable]] of this property is true;
When we define an attribute through the attribute descriptor, the [[Writable]] of this attribute defaults to false;
[[value]]: the value value of the attribute. It will be returned when reading the attribute. When modifying the attribute, it will be modified;.

// Although name and age are not defined by attribute descriptors, they also have corresponding characteristics
// Value: assigned value
// configurable: true
// enumerable: true
// writable: true
var obj = {
  name: "why",
  age: 18
}

// Data attribute descriptor
// If attribute descriptors are used, there will be default features
Object.defineProperty(obj, "address", {
  // Many configurations
  // Value: "Beijing", / / the default value is undefined
  // This special cannot be deleted / redefined
  // configurable: false, / / the default value is false
  // //This special configuration determines whether the corresponding attribute (address) can be enumerated
  // enumerable: true, / / the default value is false
  // //This attribute is whether the attribute can be assigned (write value) 
  // writable: false / / the default value is false
})

// Test the function of configurable
// delete obj.name
// console.log(obj.name)
// delete obj.address
// console.log(obj.address)

// Object.defineProperty(obj, "address", {
//   value: "Guangzhou City",
//   configurable: true
// })

// Test the function of enumerable
console.log(obj)
for (var key in obj) {
  console.log(key)
}
console.log(Object.keys(obj))

// Test the function of Writable
// obj.address = "Shanghai"
// console.log(obj.address)

2.2 access attribute descriptor

The stored data descriptor has the following four characteristics:

[[Configurable]]: indicates whether the attribute can be deleted through delete, whether its characteristics can be modified, or whether it can be modified to access the attribute descriptor;

It is consistent with the data attribute descriptor;
When we directly define a property on an object, the [[Configurable]] of this property is true;
When we define an attribute through the attribute descriptor, the [[Configurable]] of this attribute defaults to false;
[[Enumerable]]: indicates whether the attribute can be passed through for in or object Keys() returns the attribute;

It is consistent with the data attribute descriptor;
When we directly define an attribute on an object, the [[Enumerable]] of this attribute is true;
When we define an attribute through the attribute descriptor, the [[Enumerable]] of this attribute defaults to false;
[[get]]: the function that will be executed when getting properties. The default is undefined

[[set]]: the function that will be executed when setting the property. The default is undefined

var obj = {
  name: "why",
  age: 18,
  _address: "Beijing"
}

// Access attribute descriptor
// 1. Hiding a private attribute is expected to be directly used and assigned by the outside world
// 2. If we want to intercept the process of accessing and setting the value of a certain attribute, we will also use the stored attribute descriptor
Object.defineProperty(obj, "address", {
  enumerable: true,
  configurable: true,
  get: function() {
    foo()
    return this._address
  },
  set: function(value) {
    bar()
    this._address = value
  }
})

console.log(obj.address)

obj.address = "Shanghai"
console.log(obj.address)

function foo() {
  console.log("Got it once address Value of")
}

function bar() {
  console.log("Set addres Value of")
}

2.3 define multiple attributes at the same time

Object. The defineproperties () method directly defines multiple new properties or modifies existing properties on an object and returns the object.

var obj = {
  // Private attribute (private attribute without strict meaning in js)
  _age: 18,
  _eating: function() {},
  set age(value) {
    this._age = value
  },
  get age() {
    return this._age
  }
}

Object.defineProperties(obj, {
  name: {
    configurable: true,
    enumerable: true,
    writable: true,
    value: "why"
  },
  age: {
    configurable: true,
    enumerable: true,
    get: function() {
      return this._age
    },
    set: function(value) {
      this._age = value
    }
  }
})

obj.age = 19
console.log(obj.age)

console.log(obj)

2.4 object method supplement

Get the property descriptor of the object:

  • getOwnPropertyDescriptor
  • getOwnPropertyDescriptors

Prohibit object from extending new attribute: preventExtensions

  • Adding a new attribute to an object will fail (an error will be reported in strict mode);

Sealed object, not allowed to configure and delete attribute: seal

  • It's actually a call to preventExtensions
  • And set the configurable:false of the existing attribute

Freeze object, modification of existing property is not allowed: freeze

  • It's actually a call to seal
  • And set the writable: false of the existing attribute
var obj = {
  // Private attribute (private attribute without strict meaning in js)
  _age: 18,
  _eating: function() {}
}

Object.defineProperties(obj, {
  name: {
    configurable: true,
    enumerable: true,
    writable: true,
    value: "why"
  },
  age: {
    configurable: true,
    enumerable: true,
    get: function() {
      return this._age
    },
    set: function(value) {
      this._age = value
    }
  }
})

// Gets the attribute descriptor of an attribute attribute
console.log(Object.getOwnPropertyDescriptor(obj, "name"))
console.log(Object.getOwnPropertyDescriptor(obj, "age"))

// Gets all property descriptors of the object
console.log(Object.getOwnPropertyDescriptors(obj))

2.5 creating multiple objects

If we want to create a series of objects now: for example, Person objects, including Zhang San, Li Si, Wang Wu, Li Lei and so on, their information is different; So what is the best way to create it? So far, we have learned two ways:

  • new Object mode;
  • How to create literal quantity;

This method has a big disadvantage: when creating the same object, you need to write duplicate code;

We can think of a way to create objects: factory mode

  • Factory mode is actually a common design mode;
  • Usually, we will have a factory method through which we can generate the desired object;
// Factory mode: factory function
function createPerson(name, age, height, address) {
  var p = {}
  p.name = name
  p.age = age
  p.height = height;
  p.address = address

  p.eating = function() {
    console.log(this.name + "I'm eating~")
  }

  p.running = function() {
    console.log(this.name + "Running~")
  }

  return p
}

var p1 = createPerson("Zhang San", 18, 1.88, "Guangzhou City")
var p2 = createPerson("Li Si", 20, 1.98, "Shanghai")
var p3 = createPerson("Wang Wu", 30, 1.78, "Beijing")

// Disadvantages of factory mode (unable to obtain the most real type of object)
console.log(p1, p2, p3)

There is a big problem in creating objects with factory methods: when printing objects, the types of objects are all Object types, but from some angles, these objects should have a common type;

Now let's look at another pattern: the way of constructor; Let's first understand what is a constructor?

  • Constructors, also known as constructor s, are usually functions that we call when creating objects;
  • In other oriented programming languages, constructor is a method existing in a class, which is called construction method;
  • But the constructors in JavaScript are a little different;

What about constructors in JavaScript?

  • Constructor is also an ordinary function. In terms of expression, it is no different from thousands of ordinary functions;
  • Then, if such an ordinary function is called with the new operator, the function is called a constructor;

So what's special about being called by new?

2.6 function of new operator call

If a function is called using the new operator, it will perform the following operations:

  1. Create a new object (empty object) in memory;
  2. The [[prototype]] attribute inside the object will be assigned as the prototype attribute of the constructor; (detailed later);
  3. This inside the constructor will point to the new object created; (this = {})
  4. The internal code of the execution function (function body code);
  5. If the constructor does not return a non empty object, it returns the created new object;
function foo() {
  console.log("foo~, Function body code")
}

// foo is an ordinary function
// foo()

// Call foo function in another way: call a function through the new keyword, and the function is a constructor
var f1 = new foo
console.log(f1)


// When we call a function through new, what is the difference between it and the call through?

2.7 scheme for creating objects - constructors

// Specification: constructors are generally capitalized
function Person(name, age, height, address) {
  this.name = name
  this.age = age
  this.height = height
  this.address = address

  this.eating = function() {
    console.log(this.name + "I'm eating~")
  }

  this.running = function() {
    console.log(this.name + "Running")
  }
}


var p1 = new Person("Zhang San", 18, 1.88, "Guangzhou City")
var p2 = new Person("Li Si", 20, 1.98, "Beijing")

console.log(p1)
console.log(p2)
p1.eating()
p2.eating()

This constructor can ensure that our object has the type of Person (actually the attribute of constructor, which we will discuss later); But do constructors have no disadvantages? Constructor also has disadvantages. It is that we need to create a function object instance for the function of each object;

Topics: Java Javascript Front-end