Factories such as javascript Framework Design and javascript framework Factory

Source: Internet
Author: User

Factories such as javascript Framework Design and javascript framework Factory

The emergence of classes and inheritance in javascript indicates that javascript has reached the threshold for large-scale development. In the past, ECMAScript4 attempted to introduce classes, modules, and other things. However, due to excessive introduction of many features, javascript was so arrogant that it was rejected. However, it only delays the class to ES6. so far, javascript has no classes in the sense of authenticity. However, we can simulate the class. In the past, the class factory was the standard framework. This chapter will introduce the implementation of various types, it is convenient for you to select your favorite style in your own framework.

1. javascript support for Classes

In other languages, class instances must be created through the constructor new. As a language that deliberately imitates java. Javascript has the new operator, and all functions can be used as constructors. Constructors are no different from common methods. In order to build a thriving ecosystem, such as Node, Element, HTMLElement, and HTMLParagraphElement, the browser apparently uses inheritance relationships to facilitate the sharing of methods or attributes. Therefore, javascript draws on the prototype mechanism from other languages. Prototype exists in every function as a special object property. When a function uses the new operator to generate its "child"-"instance", the object named instance will have all the members of the Prototype object of the function, in this way, all instance objects share a set of methods or attributes. The so-called "class" in javascript is to modify this Prototype object to distinguish the native object and other defined "classes ". In the browser, the node class is modified based on the Object, while the Element is based on the Node, and the HTMLElement is based on the Element .... compared with our 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)

Generally, the method defined on the prototype is called the prototype method, which is shared by all instances. This is both good and bad. To achieve differentiation, javascript allows us to specify its method directly in the constructor, which is called a privileged method. If it is an attribute, it is called a privileged attribute. Each of these instances has a copy, which does not affect each other. Therefore, we usually place the Sharing Method for Data Operations in the prototype, and private attributes in the privileged attributes. But on this, it is still accessible to anyone. Put it in the scope of the function body. Then it becomes a real private property.

Function A () {var count = 0; this. aa = "aa"; this. method = function () {return count;} this. obj = {}}. prototype = {aa: "aa", method: function () {}}; var a = new A; var B = new A; console. log (. aa = B. aa); // true because the value of aa is of the basic type, the comparison value is console. log (. obj = B. obj) // false reference type. Each time you enter the function body, you must create a new one. Console. log (a. method === B. method); // false

Privileged methods or attributes only cover the methods or attributes of the prototype. Therefore, you only need to delete the privileged methods, and then the methods can be used to prototype methods or attributes with 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 prototype and privileged Methods attribute the instance method. In java, there is also a kind of thing called class method and class attribute. It is also very simple to use javascript to simulate them, just define them on the function.

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

Next, let's take a look at the inheritance implementation. As mentioned above, there is something on Prototype, and there is something on its instance, no matter whether this property was subsequently added, or the entire Prototype is replaced. If we replace this prototype object with the prototype of another class, it will easily obtain 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;

Because the same object is referenced, this means that we modify the prototype of Class A, which is equivalent to modifying the prototype of Class B. Therefore, we cannot assign an object to two classes. There are two ways to do this,

Method 1: grant the prototype members of the parent class to the prototype of the subclass one by one through for in.
Method 2: the prototype of the subclass is not directly obtained by the parent class. First, the prototype of the parent class is assigned to a function, and then the instance of the function is used as the prototype of the subclass.

Method 1: We usually need to implement the mixin method. Some books call it copy inheritance. The advantage is that it is simple and direct, and the disadvantage is that it cannot pass instanceof verification. The extend method of Prototype. js is used to do this.

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

Method 2 is a prototype, which is called prototype inheritance. Below is a template

Function A () {};. prototype = {aa: function () {alert (1) }} function bridge () {}; bridge. prototype =. prototype; function B () {} B. prototype = new bridge (); var a = new A; var B = new B; console. log (a = B) // false indicates that the prototype console is successfully separated. log (. prototype = B. prototype) // The true subclass shares the prototype console of the parent class. log (. aa = B. aa); // dynamically Add A new method for the parent Class. prototype. bb = function () {alert (2)} // true, inherits the method B of the parent class. prototype. cc = function () {alert (3)} // false the parent class may not have a subclass of the new instance console. log (. cc = B. cc) // and it can pass the verification mechanism instanceof console provided by javascript normally. log (B instanceof A); // true console. log (B instanceof B); // true

Method 2 can pass the instanceof verification. es5 has built-in this method to implement prototype inheritance, which is Object. create. If the second parameter is not considered, it is equal to the following code.

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

The preceding method requires that a prototype of the parent class be input as a parameter, and then the prototype of the subclass is returned.

However, we have missed something like this-subclass should not only inherit the inheritance of the parent class, but also have its own things, prototype inheritance does not allow subclass to inherit the members and privileged members of the parent class. We have to add these manually, such as class members. We can use the extend method above, and the privileged members can be implemented through apply in the subclass constructor.

Function inherit (init, Parent, proto) {function Son () {Parent. apply (this, argument); // inherits the init. apply (this, argument); // execute your own constructor} // because of the Object. create is forged, so avoid using the second parameter Son. prototype = Object. create (Parent. prototype, {}); Son. prototype. toString = Parent. prototype. toString; // handle IEbug Son. prototype. valueOf = Parent. prototype. valueOf; // handle IEbug Son. prototype. constructor = Son; // ensure that the constructor points normally, instead of Object extend (Son, proto); // Add the unique prototype member return Son of the subclass;

Next, we will conduct a group of experiments to test the backtracing mechanism of the instance. When we access an attribute of an object, we first look for its privileged members. If there is a same name, we will return. If there is no prototype, we will find the prototype of the parent class... we try to temporarily modify its prototype to see which attribute it will change.

Function A () {}. prototype = {aa: 1} var a = new A; console. log (. aa); // => 1 // replace all its prototypes with. prototype = {aa: 2} console. log (. aa); // => 1 // so we think that each instance has a constructor method pointing to its constructor // and the constructor has our prototype on it, does the javascript engine use this route to backtrack attributes? function B () {} B. prototype = {aa: 3}. constructor = B; console. log (. aa) // 1 indicates not to be affected

Therefore, instances of the class must be traced back through another channel. The ecma specification shows that each object has an internal attribute [[prototype]. it saves the Prototype object referenced by the constructor when new is used. In the standard browser and IE11, it exposes a property named _ proto _ to access it. Therefore, as long as the code above does not move _ proto _, a. aa always returns 1.

Let's take a look at what happened to the new operation.

1. An empty object instance is created.
2. instance. _ proto _ = intanceClass. prototype
3. this = instance in the constructor
4. Execute the code in the constructor
5. Determine whether there is any returned value. If there is no returned value, the default value is undefined. If the returned value is of the composite data type, the return value is directly returned. Otherwise, the return value is this
So we have the following results.

Function A () {console. log (this. _ proto __. aa); // 1 this. aa = 2}. prototype = {aa: 1} var a = new A; console. log (. aa). _ proto _ = {aa: 3} console. log (. aa) // => 2 delete. aa; // delete a privileged attribute to expose the console of the same name attribute on the prototype chain. log (. aa) // => 3

With _ proto __, we can make it easier to inherit from the prototype design. Let's take the above example and modify it for testing.

Function A () {}. prototype = {aa: 1} function bridge () {} bridge. prototype =. prototype; function B () {} B. prototype = new bridge (); B. prototype. constructor = B; var B = new B; B. prototype. cc = function () {alert (3)} // String. prototype = new String (). _ proto _ => true console. log (B. prototype. _ proto _ =. prototype) // true console. log (B. _ proto _ = B. prototype); // true console. log (B. _ proto __. _ proto _ =. prototype); // true to get the prototype object of the parent class

Because B. _ proto __. constructor is B, while B's prototype is from bridge, while bride. prototype =. prototype, in turn, when we define B. prototype. _ proto _ =. prototype, you can easily implement the inheritance of two classes.

The _ proto _ attribute has been added to es6, so you can avoid bold use

2. Implementation of various types of factories.

In the previous section, we demonstrated the implementation of various inheritance methods, but they are all messy. We hope to provide a special method, as long as the user passes in the corresponding parameters, or in a certain simple format, you can create a class. Especially subclass.

Because the class factories of mainstream frameworks rely too much on their complex tool functions, a sophisticated class factory is about a hundred lines.

Very sophisticated library, P. js

Https://github.com/jiayi2/pjs

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

This is a very sophisticated library, especially when calling methods with the same name as the parent class, it directly leaves the prototype of the parent class in front of you, saving _ super.

  var P = (function(prototype, ownProperty, undefined) { return function P(_superclass /* = Object */, definition) {  // handle the case where no superclass is given  if (definition === undefined) {   definition = _superclass;   _superclass = Object;  }  // C is the class to be returned.  //  // When called, creates and initializes an instance of C, unless  // `this` is already an instance of C, then just initializes `this`;  // either way, returns the instance of C that was initialized.  //  // TODO: the Chrome inspector shows all created objects as `C`  //    rather than `Object`. Setting the .name property seems to  //    have no effect. Is there a way to override this behavior?  function C() {   var self = this instanceof C ? this : new Bare;   self.init.apply(self, arguments);   return self;  }  // C.Bare is a class with a noop constructor. Its prototype will be  // the same as C, so that instances of C.Bare are instances of C.  // `new MyClass.Bare` then creates new instances of C without  // calling .init().  function Bare() {}  C.Bare = Bare;  // Extend the prototype chain: first use Bare to create an  // uninitialized instance of the superclass, then set up Bare  // to create instances of this class.  var _super = Bare[prototype] = _superclass[prototype];  var proto = Bare[prototype] = C[prototype] = C.p = new Bare;  // pre-declaring the iteration variable for the loop below to save  // a `var` keyword after minification  var key;  // set the constructor property on the prototype, for convenience  proto.constructor = C;  C.extend = function(def) { return P(C, def); }  return (C.open = function(def) {   if (typeof def === 'function') {    // call the defining function with all the arguments you need    // extensions captures the return value.    def = def.call(C, proto, _super, C, _superclass);   }   // ...and extend it   if (typeof def === 'object') {    for (key in def) {     if (ownProperty.call(def, key)) {      proto[key] = def[key];     }    }   }   // if no init, assume we're inheriting from a non-Pjs class, so   // default to using the superclass constructor.   if (!('init' in proto)) proto.init = _superclass;   return C;  })(definition); } // as a minifier optimization, we've closured in a few helper functions // and the string 'prototype' (C[p] is much shorter than C.prototype)})('prototype', ({}).hasOwnProperty);

We try to create a class:

Var Dog = P (function (proto, superProto) {proto. init = function (name) {// 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. move (1); B. move (2 );

In the current situation, we can try to create a more concise definition method.

Var Animal = P (function (proto, superProto) {proto. init = function (name) {// constructor this. name = name;} proto. move = function (meters) {// Prototype Method console. log (this. name + "moved" + meters + "m. ")}); var a = new Animal (" aaa ") var B = new Animal (" bbb "); // no instance change. move (1); B. move (2 );//............... var Snake = P (Animal, function (snake, animal) {snake. init = function (name, eyes) {animal. init. call (this, arguments); // call the parent class constructor this. eyes = 2;} snake. move = function () {console. log ('slithering... '); animal. move. call (this, 5); // call the method with the same name as the parent class}); var s = new Snake ("snake", 1); s. move (); console. log (s. name); console. log (s. eyes );

Private Attribute demonstration. It is safe and reliable because it is defined in the function body!

Var Cobra = P (Snake, function (cobra) {var age = 1; // Private attributes // You can also compile the private method cobra here. glow = function () {// return age ++ ;}}); var c = new Cobra ("cobra"); console. log (c. glow (); // 1 console. log (c. glow (); // 2 console. log (c. glow (); // 3

The above is all the content of this article. I hope you will like it.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.