The encapsulation and inheritance of JS in layman's

Source: Internet
Author: User
Tags object object unique id ustring es6 class

Original address: http://www.cnblogs.com/yincheng/p/4943789.html

Although JS is an object-oriented language, it is not a typical object-oriented language. Java/c++ Object-oriented is object - class the relationship, while JS is object - object the relationship between the middle through the prototype prototype connection, the parent class and the subclass form a prototype chain. This paper analyzes the encapsulation of JS object, then discusses the way to implement inheritance correctly, then discusses several problems, and finally class makes a simple description of the new class keywords introduced by ES6.

JS class is actually a functional function, because it is not a typical OOP class, it is also called Pseudo-class. Understanding JS class, you need to have a better understanding of the function in JS. First, function itself is an object, which can be used as a function parameter or as a return value, just like an ordinary object. The function can then be used as a class, for example to implement a String class

1 var MyString = function (str) {2     this.content = str;3};4 5 var name = new MyString ("Hanmeimei"); 6 var addr = new MySt Ring ("China"), 7 Console.log (Name.content + "live in" + addr.content);

The first line declares a mystring function, obtains a mystring class, and this function is also the constructor of the mystring. The 5th line, the new object, executes the constructor, this points to the newly generated object, the 2nd line adds a content property to the object, and assigns the address of the new object to name. The 6th line again to create a new object, note that this is pointing to new objects, so the newly generated content and the front is not the same.

The above code is a bit problematic in the browser, because this code is running under the global scope, and the defined name variable is also global, so actually executing var name = new MyString ("") is equivalent to Window.name = new MyString (""), Since name is a variable that the window already exists as the second parameter of window.open, it can be used to pass data across domains. However, because Window.name does not support an instance that is set to a custom function, the setting is not valid or remains the default value: A string with the value "[Object Object]". The solution is to change the running environment of the code to local, that is, to wrap it up with a function:

(function () {    var name = new MyString ("Hanmeimei");    Console.log (name.content); Correct, Output Hanmeimei}) ();

So from this point of view, the code with a function package up, do not pollute the global scope, or is quite necessary. Next, back to the chase.

Each function in JS has a prototype attribute, which points to an ordinary object, which holds the address of the object. Each instance of this function new will be taken with a pointer (usually __proto__) to the object pointed to by prototype. The process is similar to the following:

var name = new MyString ();             Produces an object that executes the constructor name.__proto__ = Mystring.prototype;   Add a __proto__ property that points to the prototype of the class (this line of code is illustrative only)

As shown, the __proto__ of name and addr point to the prototype object of mystring:

As can be seen in JS, the method of the class is placed in the function of the prototype, each of its instances will get the method of the class.

Now add a ToString method for mystring:

MyString.prototype.toString = function () {    return this.content;};

A new property will be added to the MyString prototype object.

This time the instance name and addr have this method, calling this method:

Console.log (Name.tostring ()); Output HanMeimeiconsole.log (name + "lives in" + addr); "+" When connecting characters, automatically call ToString, output Hanmeimei lives in

This implements the basic encapsulation-the properties of the class are defined in the constructor, such as the content of mystring, and the method of the class is added in the prototype of the function, such as the ToString method of mystring.

At this point, consider a fundamental question, why is the method added to the prototype to be referenced by the object of the class? Because JS will first find the method on the object, if not found it will go to its prototype to find. For example, to execute name.tostring (), the first step name, the object itself does not have toString (only a content property), so it looks for the prototype object of name, which is the object that __proto__ points to, Found with the ToString attribute, so it was found.

What if the ToString method is not added for mystring? Since mystring is actually a function object, the above definition of MyString syntax action is equivalent to:

Just for example, you should avoid using this syntax form, because it will result in two compilations, affecting efficiency
var MyString = new Function ("str", "this.content = str");

By comparing the __proto__ of mystring and function, it can be seen from the side that mystring is actually an example of function:

Console.log (mystring.__proto__); Output [Function:empty]console.log (function.__proto__); Output [Function:empty]

MyString's __proto__ pointer, pointing to the function of the prototype, can be seen through the browser's debug function, which is the prototype of object, as shown in:

Because object is the root class in JS, all other classes inherit from it, and this root class provides 6 methods, such as ToString, valueof, and so on.

Therefore, the ToString method of the object prototype is found, and the lookup is completed and executed:

Console.log (Name.tostring ()); Output {content: ' Hanmeimei '}

As you can see here, the inheritance in JS is the prototype of the __proto__ of a function (such as mystring) that points to another function (such as Object). Based on this, write a custom class unicodestring inherited from MyString

var ustring = function () {};

To implement inheritance:

Ustring.prototype = Mystring.prototype; Error implementation

Note that the above inheritance method is wrong, this is simply to point to the prototype of the Ustring mystring, that is, ustring and mystring use the same prototype, sub-class ustring additions and deletions to change the method of the prototype, mystring will change accordingly, Another class that inherits mystring such as asciistring will change accordingly. According to the above analysis, it should be to let the __proto__ attribute in Ustring's prototype point to the MyString prototype, rather than let Ustring's prototype point to MyString. In other words, let Ustring have its own prototype, adding a pointer to the prototype of the parent class on its prototype:

ustring.prototype.__proto__ = Mystring.prototype;  Not the right implementation

Because __proto__ is not a standard syntax, in some browsers is not visible, if you run the above code on Firefox, Firefox will give a warning:

mutating the [[Prototype]] of an object would cause your code to run very slowly; Instead create the object with the correct initial [[Prototype]] value using Object.create

The logical thing to do is to make prototype equal to an object, the object's __proto__ to the parent class's prototype, so this object needs to be an instance of a function, and the prototype of that function points to the prototype of the parent class. , so the following implementations are obtained:

object.create = function (o) {2     var F = function () {};3     f.prototype = o;4     return new F (); 5};6 7 ustring . prototype = Object.create (Mystring.prototype);

Line 2nd of the code, define a temporary function, line 3rd Let the prototype of this function point to the prototype of the parent class, line 4th returns an instance, the __proto__ of this instance points to the prototype of the parent class. Line 7th assigns this instance to the prototype of the subclass. The implementation of the inheritance is basically done here.

But there is one small problem. The normal prototype will have a constructor pointing to the constructor function itself, such as the mystring above:

The function of this constructor is to invoke the constructor in the prototype, such as adding a copy copy function to the MyString class:

1 MyString.prototype.copy = function () {2//  return MyString (this.content);              There is a problem with this implementation, and the following is an analysis of 3     return new This.constructor (this. Content);  The correct implementation of 4};5 6 var anothername = Name.copy (); 7 Console.log (Anothername.tostring ());            Output HanMeimei8 console.log (anothername instanceof MyString);   Output true

The problem is: the 7th line in the Object.create code, completely covering up the ustring prototype, instead of a new object, the object's __proto__ point to the parent class is mystring prototype, So UString.prototype.constructor in the search, Ustring.prototype did not constructor this attribute, so it points to the __proto__ to find, Found the constructor of MyString, so Ustring's constructor is actually mystring constuctor, as shown below, USTR2 is actually an instance of mystring, not the desired ustring. Instead of constructor, calling directly with a name (line 2nd of the code above) also has this problem.

var ustr = new ustring (), var ustr2 = Ustr.copy (); Console.log (ustr  instanceof ustring);//Output Trueconsole.log (ustr2 Instanceof ustring); Output Falseconsole.log (ustr2 instanceof Mystring); Output true

Therefore, the implementation of inheritance needs to add one more step, the subclass Ustring prototype in the constructor point back to its own:

UString.prototype.constructor = ustring;

When executing the this.constructor () in the copy function, it is actually ustring (). It is normal to make instanseof judgment at this time:

Console.log (ustr2 instanceof ustring); Output true

The related operation can be encapsulated into a function to facilitate reuse.

The basic place to inherit the core is over here, and then there are a few more questions to consider.

The first one is how the constructor of the parent class is called in the subclass constructor, and the constructor of the parent class is used as a normal function, and the this pointer of a subclass is passed:

1 var ustring = function (str) {2//MyString (str);   Incorrect implementation of 3     Mystring.call (this, str); 4};5 6 var ustr = new Ustring ("Hanmeimei"); 7 Console.log (USTR + "");  Output Hanmeimei

Note that the 3rd line passes a this pointer, and when the mystring is called, this point points to the newly generated Ustring object, and if you use line 2nd directly, the execution context is window,this will point to Window,this.content = STR is equivalent to window.content = str.

The second problem is the implementation of a private property, the variable defined in the first constructor, whose instance is public and can be accessed directly, as follows:

var MyString = function (str) {    this.content = str;}; var str = new MyString ("Hello");        //Direct access, output hello

However, in typical object-oriented programming, attributes should be private, and operation properties should be accessed through the methods/interfaces provided by the class in order to achieve encapsulation. In JS to achieve private, with the help of function scope:

var MyString = function (str) {    this.sayhi = function () {        return "HI" + str;    }}; var str = new MyString ("Hanmeimei"); Console.log (Str.sayhi ());            Output Hi, Hanmeimei

However, the problem is that the definition of the function must be placed in the constructor instead of the prototype discussed earlier, causing each instance to be created with an identical function, resulting in a waste of memory space. So this implementation is at the expense of memory. If there are many instances, the memory space will be greatly increased, this problem is not negligible, so it is not realistic to implement the property in JS, even if the ES6 class syntax is not implemented. However, you can add a static private member variable to the class, which is shared by all instances of the class, as in the following case:

var Worker; (function () {    var id = +;    Worker = function () {        id++;    };    Worker.prototype.getId = function () {        return ID;    };}) var worker1 = new Worker (); Console.log (Worker1.getid ())   ; Output 1001var worker2 = new Worker (); Console.log (Worker2.getid ());   Output 1002

The above example uses the static variables of the class to produce a unique ID for each worker. At the same time, this ID is not allowed to be modified directly by worker instances.

The third problem is the virtual function, which is not much meaningful to discuss virtual function in JS. virtual function of a large role is to achieve runtime dynamic, this runtime is based on the type of subclass, but JS is a weak type of language, the subclass of the type is Var, as long as the sub-class has a corresponding method, you can pass the parameter "polymorphic" run. Much simpler than strong-type languages such as C++/java.

Finally, the new class keyword introduced by ES6

 1//need to run in strict mode 2 ' use strict '; 3  class  mystring{4  Constructor  (str) {5 this.content = str; 6} 7 toString () {8 return this.content; 9}10//static static function keyword added all  static  co NCAT (str1, str2) {return str1 + str2;13}14}15//extends inheritance keyword-class ustring  extends  M  Ystring{18 Constructor (str) {19//Use Super to call the parent class method  Super  (str),}22}23 var str1 =                       New MyString ("Hello"), str2 = new MyString ("World"), and Console.log (STR1);               Output mystring {content: "Hello"}27 console.log (str1.content);            Output hello28 Console.log (str1.tostring ());       Output hello29 Console.log (Mystring.concat (str1, str2));//output Hello World 
3031 var ustr = new Ustring ("ustring"); Console.log (USTR); Output mystring {content: "ustring"}33 Console.log (Ustr.tostring ()); Output ustring

From the output of the results, the new class still does not implement the property private function, see line 27th. and from the 26th line, the so-called class is actually the compiler to help us achieve the above complex process, the essence is the same, but to make the code more simple and clear. A different point is that the static keyword is used to invoke the class's function directly with the class name. ES6 's support is still low, and the latest Chrome and safari support for Class,firefox is not very good.

Finally, although the general Web page JS a lot of small projects, seemingly do not need to encapsulate, inherit these technologies, but if you can use object-oriented ideas to write code, regardless of the project size, as long as the application of the appropriate, and even some design patterns of thinking, will make the code maintainability and Extensibility. So you can always try to write like this.

The encapsulation and inheritance of JS in layman's

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.