Read Secrets of the JavaScript Ninja object

Posted by bensonang on Tue, 09 Jul 2019 19:11:13 +0200

Object Oriented and Prototype

Understanding prototypes

In JavaScript, inheritance can be achieved through prototypes.The concept of prototype is simple.Each object contains a reference to a prototype, and when looking for an attribute, if the object itself does not have that attribute, it looks for the attribute on the prototype.

Each object can have a prototype, and each object can have a prototype, and so on, to form a chain of prototypes.Finding specific properties will be delegated across the prototype chain and will stop only if no more prototypes can be found.

Object Constructors and Prototypes

When Ninja is called as a function, nothing is done.When using the new operator, an object is returned and its prototype is Ninja, so ninja2 can call the swingSword method.But the swingSword method is a prototype property of Ninja, not a property of a ninja instance

function Ninja(){}
Ninja.prototype.swingSword = function(){
  return true;
};  
const ninja1 = Ninja();
assert(ninja1 === undefined,
"No instance of Ninja created.");  
const ninja2 = new Ninja();
assert(ninja2 &&
ninja2.swingSword &&
ninja2.swingSword(),
"Instance exists and method is callable." );

Instance Properties

  1. Instance attributes take precedence over prototype attributes when they have duplicate names in prototypes and instances
  2. Multiple instances are created, each of which is a separate copy, but the prototype references the same method
function Ninja(){
  this.swung = false;
  this.swingSword = function(){
    return !this.swung;  
  };
}
Ninja.prototype.swingSword = function(){
  return this.swung;
};  
const ninja = new Ninja();
assert(ninja.swingSword(),
"Called the instance method, not the prototype method.");

Implementing object types through constructors

constructor

Check types through the constructor property

Assert (ninja.constructor == Ninja, constructor refers to a type that detects ninja, and the result is a reference to its constructor

Create a new object with a constructor reference

const ninja2 = new ninja.constructor();- Create the second instantiated object by the constructor method of the first instantiated object

Implement Inheritance

To achieve inheritance, Person instances are used as prototypes for Ninja, so when the Niaja drug calls person's method, it looks along the prototype chain.

function Person(){}
Person.prototype.dance = function(){};
function Ninja(){}
Ninja.prototype = new Person(); ⇽--- By adding Ninja Prototype assignment Person Instance, implementation Ninja inherit Person
const ninja = new Ninja();
assert(ninja instanceof Ninja,
"ninja receives functionality from the Ninja prototype");
assert(ninja instanceof Person, "... and the Person prototype");
assert(ninja instanceof Object, "... and the Object prototype");
assert(typeof ninja.dance === "function", "... and can dance!")

Problems rewriting constructor properties

By setting the Person instance object as
When we prototyped the Ninja constructor, we lost the association between Ninja and the original Ninja prototype.

//Configuring the constructor object with defineProperty
function Person(){}
  Person.prototype.dance = function(){};
  function Ninja(){}
  Ninja.prototype = new Person();
  Object.defineProperty(Ninja.prototype,"constructor",{
    enumerable: false,
    value: Ninja,
    writable: true
  }
);

class using JavaScript in ES6

ES6 uses the keyword class to implement classes, but its underlying implementation is still based on prototype inheritance!

Use class keyword

class Ninja{
  constructor(name){
    this.name = name;
  }s
  wingSword(){
    return true;
  }
  //Static method
  static compare(ninja1, ninja2){
    return ninja1.level - ninja2.level;
  }
}

Implement Inheritance

class Person {
  constructor(name){
    this.name = name;
  }
  dance(){
    return true;
  }
}
class Ninja extends Person
  constructor(name, weapon){
    super(name); ⇽--- Use keywords super Call base class constructor
    this.weapon = weapon;
  }
  wieldWeapon(){
    return true;
  }
}

Control access to objects

Use getter and setter to control property access

//Use literal get set
const ninjaCollection = {
  ninjas: ["Yoshi", "Kuma", "Hattori"],
  get firstNinja(){
    report("Getting firstNinja");
    return this.ninjas[0];
  }, ⇽--- Definition firstNinja Of getter Method, return ninjas The first value in the list, and record one
  //news
  set firstNinja(value){
    report("Setting firstNinja");
    this.ninjas[0] = value;
  } ⇽--- Definition firstNinja Of setter Method, setting ninjas The first value in the list, and record one
  //news
};

//ES6 class
class NinjaCollection {
  constructor(){
    this.ninjas = ["Yoshi", "Kuma", "Hattori"];
  }
  get firstNinja(){
    report("Getting firstNinja");
    return this.ninjas[0];
  }
  set firstNinja(value){
    report("Setting firstNinja");
    this.ninjas[0] = value;
  } ⇽--- stay ES6 Of class Use getter and setter
}
const ninjaCollection = new NinjaCollection();

Checking attribute values using getter s and setter s

function Ninja() {
  let _skillLevel = 0;
  Object.defineProperty(this, 'skillLevel', {
    get: () => _skillLevel,
    set: value => {
    if(!Number.isInteger(value)){
      throw new TypeError("Skill level should be a number");
      } ⇽--- Verify that the value passed in is integer.If not, throw an exception
    _skillLevel = value;
    }
  });
}
const ninja = new Ninja();

Use proxy to control access

A proxy can be understood as a generalized setter and getter, except that each setter and getter can only control a single object property, whereas a proxy can be used for general processing of object interaction, including methods that call objects

Create proxy through Proxy constructor

const emperor = { name: "Komei" };  
const representative = new Proxy(emperor, {  
  get: (target, key) => {
  report("Reading " + key + " through a proxy");
    return key in target ? target[key]
      : "Don't bother the emperor!"
  },
  set: (target, key, value) => {
    report("Writing " + key + " through a proxy");
    target[key] = value;
  }
});

Logging with proxy

function makeLoggable(target){
  return new Proxy(target, {
    get: (target, property) => {
      report("Reading " + property);
      return target[property];},
    set: (target, property, value) => {
      report("Writing value " + value + " to " + =property);
      target[property] = value;  
  }
});
}
let ninja = { name: "Yoshi"};
ninja = makeLoggable(ninja);
assert(ninja.name === "Yoshi", "Our ninja Yoshi");
ninja.weapon = "sword"; ⇽--- Logs are logged through the proxy method when proxy objects are read and written

The following can be accomplished elegantly using a proxy.

  • Logging.
  • Performance measurement.
  • Data validation.
  • Automatically populate object properties (to avoid unwanted null exceptions).
  • Array negative index.

Topics: PHP Attribute Javascript