javascript framework design and so on

  • 2020-06-19 09:38:40
  • OfStack

The appearance of class and inheritance in javascript indicates that javascript has reached the threshold of large-scale development. Before ECMAScript4, it tried to introduce classes, modules and other things, but due to too many features introduced, javascript became smoky and was rejected. Just delay the class to ES6. So far, javascript doesn't have a real class. However, we can simulate classes. For a while, class factories were the standard configuration of frameworks, and this chapter introduces various class implementations to make it easier for you to choose your favorite style of class in your framework or to choose.

1.javascript's support for classes

In other languages, instances of classes are generated by the constructor new. As a deliberate imitation of java language. javascript has the new operator, and all functions can be used as constructors. Constructors are no different from normal methods. In order for the browser to build its colorful ecosystem, such as Node,Element,HTMLElement, HTMLParagraphElement, javascript obviously USES inheritance to facilitate the sharing of some methods or attributes, so javascript borrows the prototype mechanism from other languages. Prototype exists as a special object attribute on each function. When a function names its "child" -- "instance" -- by the new operator new, the object named instance owns all the 1-cut members of the function's Prototype object, thus realizing that all instance objects share a set of methods or attributes. javascript's "class" is defined by modifying the Prototype object to distinguish the native object from its other defined "classes." In the browser, the class node is modified based on Object, Element is modified based on Node, and HTMLElement is modified based on Element... In contrast to our working business, we can create our own classes for reuse and sharing.


  function A(){

  }
  A.prototype = {
    aa:"aa",
    method:function(){
    }
  };
  var a = new A;
  var b = new A;
  console.log(a.aa === b.aa);
  console.log(a.method === b.method)

As such, we call the method defined on the prototype method, which is Shared by all instances, both good and bad. For differentiation, javascript allows us to specify its method directly within the constructor, which is called the privileged method. If it is an attribute, it is called a privileged attribute. They have one copy for each instance, and they do not affect each other. Therefore, we typically place methods Shared for manipulating data in prototypes and private properties in privileged properties. But if you put it on this, and you want people to access it, let's put it in the scope of the function. It becomes a real private property.


  function A() {
    var count = 0;
    this.aa = "aa";
    this.method = function() {
      return count;
    }
    this.obj = {}
  }
  A.prototype = {
    aa:"aa",
    method:function(){

    }
  };
  var a = new A;
  var b = new A;
  console.log(a.aa === b.aa);//true  Due to the aa The value of is the basic type, compare the value 
  console.log(a.obj === b.obj) //false  Reference types, which are recreated each time you enter the body of the function, and therefore not 1 The sample. 
  console.log(a.method === b.method); //false

A privileged method or property is just a method or property that covers up the stereotype, so by deleting the privileged method, you can go to the stereotype method or property of the same name.


  delete a.method;
  delete b.method;
  console.log(a.method === A.prototype.method);//true
  console.log(a.method === b.method); //true

In java, both stereotype and privileged methods are attribute instance methods, and in java there is another thing called class methods and class properties. They are also very simple to emulate with javascript, just defined on the function.


  A.method2 = function(){} // Class method 
  var c = new A;
  console.log(c.method2); //undefined

Next, let's look at the implementation of inheritance. As mentioned above, whatever is on Prototype, its instance will have something, whether the attribute is added later or the entire Prototype is transposed. If we replace this prototype object with the prototype of another class, it can easily get all the prototype members of that class.


  function A() {};
  A.prototype = {
    aaa : 1
  }
  function B() {};
  B.prototype = A.prototype;
  var b = new B;
  console.log(b.aaa); //=> 1;
  A.prototype.bb = 2;
  console.log(b.bb) //=> 2;

Since we are referring to the same object, this means that we modified the prototype of the A class, which is the same as fixing the prototype of the B class. Therefore, we cannot assign one object to two classes. There are two ways to do this,

Method 1: for in assigns the prototype members of the parent class to the prototype of the child class one by one
Method 2 is: the prototype of the subclass is not directly obtained by the parent class, first assign the prototype of the parent class to a function, and then use the instance of the function as the prototype of the subclass.

Method 1, we usually implement a method like mixin, which some books call copy inheritance, which is simple and straightforward at its best, but not verified by instanceof at its worst. The extend method is used to do this.


  function extend (des, source) { //des = destination
    for (var property in source)
      des[property] = source[property];
    return des;
  }

Method 2, work on the prototype, so it's called prototype inheritance. Here's a sample


  function A() {};
  A.prototype = {
    aa:function(){
      alert(1)
    }
  }
  function bridge() {

  };
  bridge.prototype = A.prototype;

  function B() {}
  B.prototype = new bridge();

  var a = new A;
  var b = new B;

  console.log(a == b) //false  Proof of successful separation of prototypes 
  console.log(A.prototype == B.prototype) //true  Subclasses share the prototype methods of the superclass 
  console.log(a.aa === b.aa); // Add a new method dynamically for the parent class 
  A.prototype.bb = function () {
    alert(2)
  }
  //true , the method that inherits the parent class 
  B.prototype.cc = function (){
    alert(3)
  }
  //false  The superclass does not necessarily have subclasses new The instance 
  console.log(a.cc === b.cc)
  // And it goes through normally javascript Built-in validation mechanism instanceof
  console.log(b instanceof A) ;//true
  console.log(b instanceof B) ; //true

Method 2 can be verified by instanceof, which is built into es5 to implement prototype inheritance. It is Object.create, which is approximately equal to the following code without considering the second parameter.


  Object.create = function (o) {
    function F() {}
    F.prototype = o;
    return new F();
  }

The above method requires that a prototype of the parent class be passed in as an argument, and then returns the prototype of the child class

However, we still miss one thing -- a subclass should not only inherit from its parent, it should also have something of its own, and prototype inheritance does not allow a subclass to inherit from its parent or privileged members. These we have to add manually, such as class members, which we can do via the extend method above, and privileged members, which we can implement in the subclass constructor, via apply.


  function inherit(init, Parent, proto){
    function Son(){
      Parent.apply(this, argument); // The privileged member of the parent class is inherited first 
      init.apply(this, argument); // Execute your own constructor 
    }
  }
  // Due to the Object.create We faked it, so avoid using the control 2 A parameter 
  Son.prototype = Object.create(Parent.prototype,{});
  Son.prototype.toString = Parent.prototype.toString; // To deal with IEbug
  Son.prototype.valueOf = Parent.prototype.valueOf; // To deal with IEbug
  Son.prototype.constructor = Son; // Make sure the constructor is pointing normally, not Object
  extend(Son, proto) ;// Adds a unique prototype member of a subclass 
  return Son;

Next, do a group of experiments to test the backtracking mechanism of the instance. When we access a property of an object, it looks for its privileged member first, returns if it has the same name, finds a prototype if it doesn't, finds a prototype of its superclass if it doesn't... Let's try to change its prototype temporarily and see if its properties change to that.


  function A(){

  }
  A.prototype = {
    aa:1
  }
  var a = new A;
  console.log(a.aa) ; //=>1

  // Replace all of its prototypes 
  A.prototype = {
    aa:2
  }
  console.log(a.aa); //=>1

  // So we think of every instance 1 a constructor Method, pointing to its constructor 
  // And the constructor happens to have our prototype on it, javascript Does the engine trace the attribute through the route 
  function B(){

  }
  B.prototype = {
    aa:3
  }
  a.constructor = B;
  console.log(a.aa) //1  Not affected 

So instances of a class must be traceable through another channel. A look at the ecma specification shows that each object has an internal property [[prototype]], which holds the Prototype object referenced by our new constructor. In the standard browser with IE11, it exposes a property called ___ called ES98en__ to access it. So, as long as you leave the code on ___, ES100en.aa always returns 1.

Let's look at what happens at new.

1. An empty object instance was created
2.instance.__proto__ = intanceClass.prototype
3. Put this = instance in the constructor
Execute the code in the constructor
5. Determine if there is any return value. If there is no return value, the default value is undefined
So here's the result.


  function A() {
    var count = 0;
    this.aa = "aa";
    this.method = function() {
      return count;
    }
    this.obj = {}
  }
  A.prototype = {
    aa:"aa",
    method:function(){

    }
  };
  var a = new A;
  var b = new A;
  console.log(a.aa === b.aa);//true  Due to the aa The value of is the basic type, compare the value 
  console.log(a.obj === b.obj) //false  Reference types, which are recreated each time you enter the body of the function, and therefore not 1 The sample. 
  console.log(a.method === b.method); //false

0

With ___, we can inherit a simpler prototype with ___, let's experiment with the above example


  function A() {
    var count = 0;
    this.aa = "aa";
    this.method = function() {
      return count;
    }
    this.obj = {}
  }
  A.prototype = {
    aa:"aa",
    method:function(){

    }
  };
  var a = new A;
  var b = new A;
  console.log(a.aa === b.aa);//true  Due to the aa The value of is the basic type, compare the value 
  console.log(a.obj === b.obj) //false  Reference types, which are recreated each time you enter the body of the function, and therefore not 1 The sample. 
  console.log(a.method === b.method); //false

1

Because b. Succproto__, constructor is B and the prototype of B is derived from bridge, and brideprototype = A.prototype, in turn, we can easily inherit both classes by defining each succB.prototype.

The attribute of ___ has been added to es6, and can therefore be prevented by using it boldly

2. Implementation of various factories.

In the previous section, we demonstrated the implementation of various inheritance methods, but they were messy. We want to provide a specialized method that creates a class as long as the user passes in the corresponding parameters, or in a simple format of 1. Subclasses in particular.

Because the class factories of the mainstream frameworks rely too much on their vast toolbox functions, an elaborate class factory is only a hundred lines or so

P.js

https://github.com/jiayi2/pjs

Use version: https: / / github com/jiayi2 / factoryjs

This is a pretty clever library, especially when you call the parent method of the same name, it throws the prototype of the parent directly in front of you, even saving _super.


  function A() {
    var count = 0;
    this.aa = "aa";
    this.method = function() {
      return count;
    }
    this.obj = {}
  }
  A.prototype = {
    aa:"aa",
    method:function(){

    }
  };
  var a = new A;
  var b = new A;
  console.log(a.aa === b.aa);//true  Due to the aa The value of is the basic type, compare the value 
  console.log(a.obj === b.obj) //false  Reference types, which are recreated each time you enter the body of the function, and therefore not 1 The sample. 
  console.log(a.method === b.method); //false

2

Let's try to create a class:


  var Dog = P (function(proto, superProto){
    proto.init = function(name) { // The constructor 
      this.name = name;
    }
    proto.move = function(meters){ // Prototype method 
      console.log(this.name + " moved " + meters + " m.")
    }
  });
  var a = new Dog("aaa")
  var b = new Dog("bbb"); // No instance change 
  a.move(1);
  b.move(2);

In this case, we can try to create a more concise way of defining it


  function A() {
    var count = 0;
    this.aa = "aa";
    this.method = function() {
      return count;
    }
    this.obj = {}
  }
  A.prototype = {
    aa:"aa",
    method:function(){

    }
  };
  var a = new A;
  var b = new A;
  console.log(a.aa === b.aa);//true  Due to the aa The value of is the basic type, compare the value 
  console.log(a.obj === b.obj) //false  Reference types, which are recreated each time you enter the body of the function, and therefore not 1 The sample. 
  console.log(a.method === b.method); //false

4

The private property demo is safe and reliable because it is centrally defined in the function body.


  function A() {
    var count = 0;
    this.aa = "aa";
    this.method = function() {
      return count;
    }
    this.obj = {}
  }
  A.prototype = {
    aa:"aa",
    method:function(){

    }
  };
  var a = new A;
  var b = new A;
  console.log(a.aa === b.aa);//true  Due to the aa The value of is the basic type, compare the value 
  console.log(a.obj === b.obj) //false  Reference types, which are recreated each time you enter the body of the function, and therefore not 1 The sample. 
  console.log(a.method === b.method); //false

5

This is the end of this article, I hope you enjoy it.


Related articles: