Home Web Front-end JS Tutorial Modularization of JavaScript: Encapsulation (closure), inheritance (prototype) Introduction_javascript skills

Modularization of JavaScript: Encapsulation (closure), inheritance (prototype) Introduction_javascript skills

May 16, 2016 pm 05:28 PM
javascript encapsulation Modular inherit Closure

Although JavaScript is inherently casual, as browsers are able to do more and more things, the language is becoming more and more serious. Under complex logic, JavaScript needs to be modularized, and the modules need to be encapsulated, leaving only interfaces for external calls. Closure is the key to module encapsulation in JavaScript, and it is also a point that many beginners find difficult to understand. At first, I was confused. Now, I am confident that I have a deeper understanding of this concept. In order to facilitate understanding, the article attempts to encapsulate a relatively simple object.

We are trying to maintain a counter object ticker on the page, which maintains a value n. As the user operates, we can increment the count (add 1 to the value n), but cannot decrement n or change n directly. Moreover, we need to query this value from time to time.

Open-door JSON style modularity

A way to open the door is:

Copy the code The code is as follows:

var ticker = {
n:0,
tick:function(){
this.n ;
},
};

This way of writing is natural and effective. When we need to increase the count, we call the ticker.tick() method. When we need to query the count, we access the ticker.n variable. But its shortcomings are also obvious: users of the module are allowed to change n freely, such as calling ticker.n-- or ticker.n=-1. We have not encapsulated ticker. n and tick() appear to be "members" of ticker, but their accessibility is the same as ticker, and they are global (if ticker is a global variable). In terms of encapsulation, this modular approach is only a little bit more ridiculous than the following one (although for some simple applications, this little bit is enough).

Copy code The code is as follows:

var ticker = {};
var tickerN = 0;
var tickerTick = function(){
tickerN ;
}

tickerTick();

It is worth noting that in tick(), I access this.n - this is not because n is a member of ticker, but because it is ticker that calls tick(). In fact, it would be better to write ticker.n here, because if tick() is called, it is not ticker, but something else, such as:

Copy code The code is as follows:

var func = ticker.tick;
func( );

At this time, it is actually window that calls tick(), and when the function is executed, it will try to access window.n and an error will occur.

In fact, this "open door" modular approach is often used to organize JSON-style data rather than programs. For example, we can pass the following JSON object to a function of ticker to determine that ticker starts counting from 100 and advances by 2 each time.

Copy code The code is as follows:

var config = {
nStart:100,
step:2
}

Scope chain and closure
Look at the following code. Note that we have implemented the customization of ticker by passing in config.

Copy code The code is as follows:

function ticker(config){
var n = config.nStart;
function tick(){
n = config.step;
}
}
console.log(ticker.n); // ->undefined

You may be wondering, why did ticker change from an object to a function? This is because only functions have scope in JavaScript, and variables inside the function cannot be accessed from outside the function body. Accessing ticker.n outside ticker() will result in undefined , but accessing n within tick() will have no problem. From tick() to ticker() to the global, this is the "scope chain" in JavaScript.

But there is still a problem, that is - how to call tick()? The scope of ticker() also covers tick(). There are two solutions:

•1) Will need to call the method as the return value, just as we use the method of incrementing n as the return value of ticker();
•2) Set the variables of the outer scope, just as we did in ticker() Set getN in .

Copy code The code is as follows:

var getN;
function ticker(config){
var n = config.nStart;
getN = function(){
return n;
};
return function(){
n = config.step;
};
}

var tick = ticker({nStart:100,step:2});
tick();
console.log(getN()); // ->102

Please see, at this time, the variable n is in a "closure" and cannot be directly accessed outside ticker(), but it can be observed or manipulated through two methods.

In the first piece of code in this section, after the ticker() method is executed, n and tick() are destroyed until the next time the function is called; but in the second piece of code, ticker() After execution, n will not be destroyed because tick() and getN() may access it or change it, and the browser will be responsible for maintaining n. My understanding of "closure" is: a mechanism used to ensure that n, a variable that is within the function scope, needs to be maintained after the function is executed, and may be accessed through other methods, is not destroyed.

But, I still feel something is wrong? What if I need to maintain two objects ticker1 and ticker2 with the same functionality? There is only one ticker(), so we can’t write it again, right?

new operator and constructor
If you call a function through the new operator, a new object will be created and the function will be called using that object. In my understanding, the construction process of t1 and t2 in the following code is the same.

Copy code The code is as follows:

function myClass(){}
var t1 = new myClass();
var t2 = {};
t2.func = myClass;
t2.func();
t2.func = undefined;

T1 and t2 are both newly constructed objects, and myClass() is the constructor. Similarly, ticker() can be rewritten.

Copy code The code is as follows:

function TICKER(config){
var n = config.nStart;
this.getN = function(){
return n;
};
this.tick = function(){
n = config.step;
}
}

var ticker1 = new TICKER({nStart:100,step:2});
ticker1.tick();
console.log(ticker1.getN()); // ->102
var ticker2 = new TICKER({nStart:20,step:3});
ticker2.tick();
ticker2.tick();
console.log(ticker2.getN()) ; // ->26

By convention, constructors are capitalized. Note that TICKER() is still a function, not a pure object (the reason why we say "pure" is that functions are actually objects, and TICKER() is a function object). The closure is still valid and we cannot access ticker1.n .

Prototype prototype and inheritance
The above TICKER() still has flaws, that is, ticker1.tick() and ticker2.tick() are independent of each other! Please see, every time you use the new operator to call TICKER(), a new object will be generated and a new function will be generated to bind to this new object. Every time a new object is constructed, the browser will open up a space. Storing tick() itself and variables within tick() is not what we expect. We expect ticker1.tick and ticker2.tick to point to the same function object.

This requires the introduction of prototypes.

In JavaScript, except for Object objects, other objects have a prototype property, which points to another object. This "another object" still has its prototype object and forms a prototype chain, which ultimately points to the Object object. When calling a method on an object, if it is found that the object does not have a specified method, then search for this method on the prototype chain until the Object object.

Functions are also objects, so functions also have prototype objects. When a function is declared (that is, when the function object is defined), a new object is generated, the prototype property of this object points to the Object object, and the constructor property of this object points to the function object.

The prototype of a new object constructed through a constructor points to the prototype object of the constructor. So we can add functions to the prototype object of the constructor, and these functions will not depend on ticker1 or ticker2, but on TICKER.

You might do this:

Copy code The code is as follows:

function TICKER(config){
var n = config.nStart;
}
TICKER.prototype.getN = function{
// attention : invalid implementation
return n;
};
TICKER.prototype.tick = function{
// attention: invalid implementation
n = config.step;
};

Please note that this is an invalid implementation. Because the methods of the prototype object cannot access the contents of the closure, that is, the variable n. After the TICK() method is run, n can no longer be accessed, and the browser will destroy n. In order to access the contents of the closure, the object must have some concise instance-dependent methods to access the contents of the closure, and then define complex public methods on its prototype to implement the logic. In fact, the tick() method in the example is concise enough, let's put it back into TICKER. Next implement a more complex method tickTimes() that will allow the caller to specify the number of times tick() is called.

Copy code The code is as follows:

function TICKER(config){
var n = config.nStart;
this.getN = function(){
return n;
};
this.tick = function(){
n = config.step;
} ;
}
TICKER.prototype.tickTimes = function(n){
while(n>0){
this.tick();
n--;
}
};
var ticker1 = new TICKER({nStart:100,step:2});
ticker1.tick();
console.log(ticker1.getN()); // - >102
var ticker2 = new TICKER({nStart:20,step:3});
ticker2.tickTimes(2);
console.log(ticker2.getN()); // - >26

This TICKER is great. It encapsulates n and cannot directly change it from outside the object, and the complex function tickTimes() is defined on the prototype. This function operates on the data in the object by calling small functions of the instance.

So, in order to maintain the encapsulation of the object, my suggestion is to decouple the operation of the data into the smallest possible unit function, defined in the constructor as instance-dependent (also called " "Private"), and implement complex logic on the prototype (ie "Public").

Finally, let’s talk about inheritance. In fact, when we define a function on the prototype, we are already using inheritance! Inheritance in JavaScript is more...well...simple, or crude, than in C. In C, we may define an animal class to represent an animal, and then define a bird class to inherit the animal class to represent a bird, but what I want to discuss is not such inheritance (although such inheritance can also be implemented in JavaScript); I want to The discussion of inheritance in C would be to define an animal class and then instantiate a myAnimal object. Yes, this is instantiation in C, but is treated as inheritance in JavaScript.

JavaScript does not support classes. The browser only cares about the objects currently available and does not bother to worry about what classes these objects are and what structure they should have. In our example, TICKER() is a function object. We can assign a value to it (TICKER=1) and delete it (TICKER=undefined). However, because there are currently two objects, ticker1 and ticker2, through the new operator When it is called, TICKER() acts as a constructor, and the TICKER.prototype object acts as a class.

The above is the method of JavaScript modularization that I know. If you are also a beginner, I hope it will be helpful to you. If there is something wrong, please point it out.

Author: Master Yiyezhai
Source: www.cnblogs.com/yiyezhai

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Roblox: Bubble Gum Simulator Infinity - How To Get And Use Royal Keys
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
Mandragora: Whispers Of The Witch Tree - How To Unlock The Grappling Hook
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
Nordhold: Fusion System, Explained
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Hot Topics

Java Tutorial
1667
14
PHP Tutorial
1273
29
C# Tutorial
1255
24
Detailed explanation of C++ function inheritance: How to use 'base class pointer' and 'derived class pointer' in inheritance? Detailed explanation of C++ function inheritance: How to use 'base class pointer' and 'derived class pointer' in inheritance? May 01, 2024 pm 10:27 PM

In function inheritance, use "base class pointer" and "derived class pointer" to understand the inheritance mechanism: when the base class pointer points to the derived class object, upward transformation is performed and only the base class members are accessed. When a derived class pointer points to a base class object, a downward cast is performed (unsafe) and must be used with caution.

What is the meaning of closure in C++ lambda expression? What is the meaning of closure in C++ lambda expression? Apr 17, 2024 pm 06:15 PM

In C++, a closure is a lambda expression that can access external variables. To create a closure, capture the outer variable in the lambda expression. Closures provide advantages such as reusability, information hiding, and delayed evaluation. They are useful in real-world situations such as event handlers, where the closure can still access the outer variables even if they are destroyed.

TrendForce: Nvidia's Blackwell platform products drive TSMC's CoWoS production capacity to increase by 150% this year TrendForce: Nvidia's Blackwell platform products drive TSMC's CoWoS production capacity to increase by 150% this year Apr 17, 2024 pm 08:00 PM

According to news from this site on April 17, TrendForce recently released a report, believing that demand for Nvidia's new Blackwell platform products is bullish, and is expected to drive TSMC's total CoWoS packaging production capacity to increase by more than 150% in 2024. NVIDIA Blackwell's new platform products include B-series GPUs and GB200 accelerator cards integrating NVIDIA's own GraceArm CPU. TrendForce confirms that the supply chain is currently very optimistic about GB200. It is estimated that shipments in 2025 are expected to exceed one million units, accounting for 40-50% of Nvidia's high-end GPUs. Nvidia plans to deliver products such as GB200 and B100 in the second half of the year, but upstream wafer packaging must further adopt more complex products.

What are the advantages and disadvantages of closures in C++ functions? What are the advantages and disadvantages of closures in C++ functions? Apr 25, 2024 pm 01:33 PM

A closure is a nested function that can access variables in the scope of the outer function. Its advantages include data encapsulation, state retention, and flexibility. Disadvantages include memory consumption, performance impact, and debugging complexity. Additionally, closures can create anonymous functions and pass them to other functions as callbacks or arguments.

How to implement closure in C++ Lambda expression? How to implement closure in C++ Lambda expression? Jun 01, 2024 pm 05:50 PM

C++ Lambda expressions support closures, which save function scope variables and make them accessible to functions. The syntax is [capture-list](parameters)->return-type{function-body}. capture-list defines the variables to capture. You can use [=] to capture all local variables by value, [&] to capture all local variables by reference, or [variable1, variable2,...] to capture specific variables. Lambda expressions can only access captured variables but cannot modify the original value.

Detailed explanation of C++ function inheritance: How to debug errors in inheritance? Detailed explanation of C++ function inheritance: How to debug errors in inheritance? May 02, 2024 am 09:54 AM

Inheritance error debugging tips: Ensure correct inheritance relationships. Use the debugger to step through the code and examine variable values. Make sure to use the virtual modifier correctly. Examine the inheritance diamond problem caused by hidden inheritance. Check for unimplemented pure virtual functions in abstract classes.

AMD 'Strix Halo” FP11 package size exposed: equivalent to Intel LGA1700, 60% larger than Phoenix AMD 'Strix Halo” FP11 package size exposed: equivalent to Intel LGA1700, 60% larger than Phoenix Jul 18, 2024 am 02:04 AM

This website reported on July 9 that the AMD Zen5 architecture "Strix" series processors will have two packaging solutions. The smaller StrixPoint will use the FP8 package, while the StrixHalo will use the FP11 package. Source: videocardz source @Olrak29_ The latest revelation is that StrixHalo’s FP11 package size is 37.5mm*45mm (1687 square millimeters), which is the same as the LGA-1700 package size of Intel’s AlderLake and RaptorLake CPUs. AMD’s latest Phoenix APU uses an FP8 packaging solution with a size of 25*40mm, which means that StrixHalo’s F

How do C++ functions improve the efficiency of GUI development by encapsulating code? How do C++ functions improve the efficiency of GUI development by encapsulating code? Apr 25, 2024 pm 12:27 PM

By encapsulating code, C++ functions can improve GUI development efficiency: Code encapsulation: Functions group code into independent units, making the code easier to understand and maintain. Reusability: Functions create common functionality that can be reused across applications, reducing duplication and errors. Concise code: Encapsulated code makes the main logic concise and easy to read and debug.

See all articles