current position:Home>Design pattern -- strategy pattern
Design pattern -- strategy pattern
2021-08-27 00:14:43 【Xiao Guo is in Shenzhen】
1. Definition
The strategy pattern : Define a set of algorithms , Encapsulate them one by one , And make them interchangeable .
2. Use the strategy model to calculate bonus
The strategy pattern has a wide range of applications . Taking the calculation of year-end bonus as an example . for example , Performance is S The year-end bonus for the people of 4 Double salary , Performance is A The year-end bonus for the people of 3 Double salary , And performance is B Our year-end bonus is 2 Double salary . Provide a piece of code , To calculate the employee's year-end bonus .
2.1 The original code implementation
Write a project called calculateBonus To calculate everyone's bonus amount . Function takes two parameters : The employee's salary and his performance appraisal grade .
var calculateBonus = function(performanceLevel, salary){
if (performanceLevel === 'S') {
return salary * 4;
}
if (performanceLevel === 'A') {
return salary * 3;
}
if (performanceLevel === 'B') {
return salary * 2;
}
}
calculateBonus('B', 20000) // Output : 40000
calculateBonus('S', 6000) // Output : 24000
Copy code
This code is very simple , But there are obvious disadvantages .
-
calculateBonus The function is huge , It contains a lot of if-else sentence , These statements need to cover all logical branches .
-
calculateBonus The function lacks elasticity , If a new performance level is added C, Or want to put performance S The bonus factor is changed to 5, Then we must go deep calculateBonus Internal implementation of function , It's against openness - Closed principle .
-
The reusability of the algorithm is poor , What if you need to reuse these bonus algorithms elsewhere in the program ? Our choice is to copy and paste .
2.2 Use composite functions to refactor code
Encapsulate this algorithm into small functions , These small functions are well named , You can know at a glance which algorithm it corresponds to , They can also be reused elsewhere in the program .
var performanceS = function(salary){
return salary * 4;
}
var performanceA = function(salary){
return salary * 3;
}
var performanceB = function(salary){
return salary * 2;
}
var calculateBonus = function(performanceLevel, salary) {
if (performanceLevel === 'S') {
return performanceS(salary)
}
if (performanceLevel === 'A') {
return performanceA(salary)
}
if (performanceLevel === 'B') {
return performanceB(salary)
}
}
calculateBonus('A', 10000) // Output : 30000
Copy code
The program has been improved , But very limited , Still not solved :calculateBonus Functions can get bigger and bigger , And it lacks flexibility when the system changes .
2.3 Refactoring code using policy patterns
The strategy pattern is to define a series of algorithms , Encapsulate them one by one . Separating the unchanging from the changing is the theme of every design pattern , The strategic model is no exception , The purpose of strategy pattern is to separate the use of algorithm from the implementation of algorithm .
In this case , The way the algorithm is used is the same , The calculated bonus amount is obtained according to a certain algorithm . The implementation of the algorithm is different and changing , Each performance corresponds to different calculation rules .
A program based on policy patterns consists of at least two parts . The first part is a set of policy classes , The strategy class encapsulates the specific algorithm , And responsible for the specific calculation process . The second part is the environment class Content, Content Accept the customer's request , Then delegate the request to a policy class . To do that , explain Content To maintain a reference to a policy object in .
Now refactor the above code with the policy pattern . The first version imitates the implementation in traditional object-oriented languages . First, the calculation rules of each performance are encapsulated in the corresponding policy class :
var performanceS = function(){};
performanceS.prototype.calculate = function(salary) {
return salary * 4;
};
var performanceA = function(){};
performanceA.prototype.calculate = function(salary) {
return salary * 3;
};
var performanceB = function(){};
performanceB.prototype.calculate = function(salary) {
return salary * 2;
};
// Next, define the bonus class Bonus:
var Bonus = function(){
this.salary = null; // The original wage
this.strategy = null; // The strategic target corresponding to the performance level
}
Bonus.prototype.setSalary = function(salary) {
this.salary = salary; // Set the employee's original salary
}
Bonus.prototype.setStrategy = function(strategy) {
this.strategy = strategy; // Set the strategy object corresponding to the employee performance level
}
Bonus.prototype.getBonus = function(){ // Get the bonus amount
if(!this.strategy) {
throw new Error(' Not set strategy attribute ')
}
return this.strategy.calculate(this.salary) // Delegate the operation of bonus calculation to the corresponding strategic object
}
Copy code
Before completing the final code , Review the idea of the strategic model : Define a set of algorithms , Encapsulate them one by one , And make them interchangeable .
If this sentence is more detailed , Namely : Define algorithms for some columns , Encapsulate them into policy classes , Algorithms are encapsulated in methods within the policy class . At the customer's right Context When making a request ,Context Always delegate the request to one of these policy objects for calculation .
Now let's finish the rest of the code in this example . So let's create one bonus object , And give bonus Object to set some original data , For example, the original salary of employees . Next, a policy object for calculating bonus is also passed into bonus The object is stored inside . When calling bonus.getBonus() When calculating the bonus ,bonus The object itself does not have the ability to calculate , Instead, the request is delegated to the previously saved policy object :
var bonus = new Bonus();
bonus.setSalary(10000);
bonus.setStrategy(new performanceS()); // Set policy object
console.log(bonus.getBonus()); Output 40000
bonus.setStrategy(new performanceA()); // Set policy object
console.log(bonus.getBonus()); Output 30000
Copy code
After refactoring through the policy pattern , The code becomes clearer , The responsibilities of each category are more distinct . But this code is based on the imitation of traditional object-oriented language .
3.JavaScript Version of the policy model
In the previous example ,strategy Objects are created from various policy classes , This is to simulate the implementation of some traditional object-oriented languages . In fact, JavaScript In language , Functions are also objects , So a simpler and more direct way is to put strategy Directly defined as a function :
var strategies = {
"S": function(salary){
return salary * 4;
},
"A": function(salary){
return salary * 3;
},
"B": function(salary){
return salary * 2;
}
}
Copy code
Again , Context There is no need to use Bonus Class , We still use calculateBonus Functions act as Context To accept the user's request .
var calculateBonus = function(level, salary) {
return strategies[level](salary);
}
console.log(calculateBonus('S', 20000)); // Output : 80000
console.log(calculateBonus('A', 10000)); // Output : 30000
Copy code
4. The embodiment of polymorphism in policy pattern
Refactoring code by using policy patterns , We eliminate a large number of conditional branch statements in the original program . All the logic related to the calculation of bonus is no longer put in Context in , Instead, it is distributed among various policy objects .Context No ability to calculate bonus , Instead, the responsibility is delegated to a policy object . The algorithm responsible for each policy object has been encapsulated in the object . When we issue... To these policy objects “ Calculate the bonus ” The request of , They will return different calculation results , This is the embodiment of object polymorphism , It's also “ They can replace each other ” Purpose . Replace Context Currently saved policy objects in , We can execute different algorithms to get the results we want .
5. Use strategy mode to realize jog animation
5.1 The principle of realizing animation effect
Play some original pictures with small gap in faster frames , To achieve visual animation . stay JavaScript in , You can change one of the elements continuously CSS attribute , such as :left、top、background-position To achieve animation .
5.2 Ideas and some preparatory work
The goal is : Write an animation class and some slow motion algorithms , Let the ball move in the page with various slow effects .
Ideas : Before the exercise starts , Information needed :
- At the beginning of the animation , The original position of the ball ;
- The target position where the ball moves
- The exact point in time at the beginning of the animation
- The duration of the ball movement
adopt setInterval Create a timer , Timer intervals 19ms Cycle time . In every frame of the timer , Take the time that animation has consumed 、 The original position of the ball 、 The information such as the target position of the ball and the total duration of the animation are transmitted to the slow motion algorithm . The algorithm will pass these parameters , Calculate the current position of the ball . Finally, update the div Corresponding CSS attribute , The ball can move smoothly .
5.3 Let the ball move
Slow motion algorithm : Accept 4 Parameters , The meanings are respectively : Time consumed by animation 、 The original position of the ball 、 The target position of the ball 、 The total duration of the animation , The returned value is the current position where the animation element should be .
var tween = {
linear: function(t,b,c,d) {
return c*t/d + b;
},
easeIn: function(t,b,c,d) {
return c * (t/= d) * t + b;
},
strongEaseIn: function(t,b,c,d) {
return c * (t/= d) * t * t * t * t+ b;
},
strongEaseOut: function(t,b,c,d) {
return c * ((t = t/d - 1) * t * t * t * t + 1)+ b;
},
sineaseIn: function(t,b,c,d) {
return c * (t/= d) * t * t + b;
},
sineaseOut: function(t,b,c,d) {
return c * ((t= t/ d - 1) * t * t + 1) + b;
},
}
Copy code
Complete code
First place a... In the page div:
<body>
<div style="position:absolute;background:blue" id="div"> I am a div </div>
</body>
Copy code
So let's define Animate class ,Animate The constructor of takes a parameter : About to move dom node .
var Animate = function(dom) {
this.dom = dom; // Moving dom node
this.startTime = 0; // Start time of exercise
this.startPos = 0; // At the beginning of the animation ,dom Location of nodes , namely dom Initial position
this.endPos = 0; // At the end of the animation ,dom Location of nodes , namely dom Target location
this.propertyName = null; //dom Nodes need to be changed css Property name
this.easing = null; // Slow motion algorithm
this.duration = null; // Animation duration
}
Copy code
Next Animate.prototype.start Method is responsible for starting the animation , The moment the animation is started , To record some information , It is used by the slow motion algorithm to calculate the current position of the ball in the future . After recording this information , This method is also responsible for starting the timer .
Animate.prototype.start = function(propertyName, endPos, duration, easing) {
this.startTime = +new Date; // Animation start time
this.startPos = this.dom.getBoundingClientRect()[propertyName]; // dom Initial position of node
this.propertyName = propertyName; // dom Nodes need to be changed css Property name
this.endPos = endPos; // dom Node target location
this.duration = duration; // Animation duration
this.easing = tween[easing]; // Slow motion algorithm
var self = this;
var timeId = setInterval(function(){ // Start timer , Start animating
if(self.step()===false){ // If the animation is over , Then clear the timer
clearInterval(timeId);
}
}, 19);
}
Copy code
Animate.prototype.start The method accepts the following 4 Parameters :
- propertyName: To change CSS Property name , such as :'left'、'top', Move left and right and move up and down respectively .
- endPos: The target position of the ball
- duration: Animation duration
- easing: Slow motion algorithm
Next up is Animate.prototype.step Method , This method represents what to do in each frame of the ball motion . Here, , This method is responsible for calculating the current position of the ball and calling the update CSS Method of attribute value Animate.prototype.update.
Animate.prototype.step = function(){
var t = +new Date; // Get the current time
if (t >= this.startTime + this.duration) {
this.update(this.endPos); // Update the ball CSS Property value
return false;
}
var pos = this.easing(t - this.startTime, this.startPos, this.endPos - this.startPos, this.duration);
// pos Is the current position of the ball
this.update(pos); // Update the ball CSS Property name
}
Copy code
The current time is greater than the sum of the animation start time plus the animation duration , It means that the animation is over , At this point, correct the position of the ball . Because after the start of this frame , The position of the ball is close to the target position , But it may not be exactly equal to the target position . At this point, we should actively correct the current position of the ball as the final target position . In addition, let Animate.prototype.step Method returns false, You can inform Animate.prototype.start Method clear timer
Finally, it is responsible for updating the ball CSS Property value Animate.prototype.update Method :
Animate.prototype.update = function(pos) {
this.dom.style[this.propertyName] = pos + 'px';
}
// test
var div = document.getElementById('div');
var animate = new Animate(div);
animate.start('left', 500, 1000, 'strongEaseOut');
Copy code
6. More broadly “ Algorithm ”
The strategy pattern is to define a series of algorithms , And encapsulate them . The previous examples of calculating bonus and slow motion animation encapsulate some algorithms .
By definition , The strategy pattern is used to encapsulate the algorithm . But if the strategy pattern is only used to encapsulate the algorithm , It's a bit overqualified . In actual development , It usually spreads the meaning of the algorithm , So that the policy pattern can also be used to encapsulate some columns “ Business rules ”. As long as these business rules point to the same goal , And can be replaced by , You can encapsulate them with a policy pattern .
7. Form verification
Use policy mode to complete form verification
Check logic
- The username cannot be empty
- Password length cannot be less than 6 position
- The phone number must be in the format
7.1 The first version of the form validation
No policy pattern was introduced
<html>
<body> <form action="http://xxx.com/register" id="registerForm" method="post"> Please enter a user name :<input type="text" name="userName" /> Please input a password :<input type="text" name="password" /> Please enter your mobile number :<input type="text" name="phoneNumber" /> <button> Submit </button> </form> <script> var registerForm = document.getElementById('registerForm'); registerForm.onsubmit = function(){ if (registerForm.username.value === '') { alert(' The username cannot be empty '); return false; } if (registerForm.password.value.length < 6) { alert(' Password length cannot be less than 6 position '); return false; } if (!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)) { alert(' Incorrect format of mobile phone number '); return false; } } </script> </body>
</html>
Copy code
This is a very common coding format , Its disadvantages are the same as the original version of bonus calculation .
- registerForm.onsubmit The function is huge , It contains a lot of if-else sentence , These statements need to override all validation rules
- registerForm.onsubmit The function lacks elasticity , If a new verification rule is added , Or you want to check the length of the password from 6 Change to 8, Must go deep registerForm.onsubmit Internal implementation of function , It's illegal to open - Closed principle
- The reusability of the algorithm is poor , If you add another form to the program , This form also needs some similar checks , Then we may copy these verification logic everywhere .
7.2 Refactoring form validation with policy mode
First step : Encapsulate the verification logic into policy objects :
var strategies = {
isNonEmpty: function(val, errorMsg) { // Not empty
if (value === '') {
return errorMsg;
}
},
minLength: function(value, length, errorMsg) { // Limit the minimum length
if(value.length < length) {
return errorMsg;
}
},
isMobile: function(value, errorMsg) {
if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) { // Cell phone number format
return errorMsg
}
}
}
Copy code
Next, we'll implement Validator class .Validator Class here as Context, Responsible for receiving user requests and delegating them to strategy object . In the given Validator Class code , Let's first understand how users respond to Validator Class to send the request :
var validataFunc = function(){
var validator = new Validator(); // Create a validator object
// Add some validation rules
validator.add(registerForm.userName, 'isNonEmpty', ' The username cannot be empty ');
validator.add(registerForm.password, 'minLength:6', ' Password length cannot be less than 6 position ');
validator.add(registerForm.phoneNumber, 'isMobile', ' Incorrect format of mobile phone number ');
var errorMsg = validator.start(); // Get the calibration results
return errorMsg; // Return verification result
}
var registerForm = document.getElementById('registerForm');
registerForm.onsubmit = function(){
var errorMsg = validataFunc(); // If errorMsg There is an exact return value , Description failed the verification
if (errorMsg) {
alert(errorMsg)
return false; // Block form submission
}
}
Copy code
You can see that in this code , First create a validator object , And then through validator.add Method , Go to validator Add some validation rules to the object .validator.add Method acceptance 3 Parameters , Use the following code to illustrate :
validator.add(registerForm.password, 'minLength:6', ' Password length cannot be less than 6 position ');
Copy code
- registerForm.password For those involved in verification input Input box .
- 'minLength:6' Is a string separated by colons . In front of the colon minLength Selected on behalf of customers strategy object , The number after the colon 6 Indicates some parameters necessary in the verification process .'minLength:6' It means checking registerForm.password This text input box is value The minimum length is 6. If the string does not contain a colon , It indicates that no additional parameter information is required in the verification process , such as ‘isNonEmpty’.
- The first 3 The first parameter is the error message returned when the verification fails .
When we went to validator After adding a series of verification rules to the object , Would call validator.start() Method to start verification . If validator.start() Returns an exact errorMsg String as return value , This indicates that the verification failed , At this point, let registerForm.onsubmit Method returns false To prevent form submission .
And finally Validator The realization of the class :
var Validator = function(){
this.cache = []; // Save validation rules
}
Validator.prototype.add = function(dom, rule, errorMsg) {
var ary = rule.split(':'); // hold strategy Separate from parameters
this.cache.push(function(){ // Wrap the verification steps with empty functions , And put in cache
var strategy = ary.shift(); // User selected strategy
ary.unshift(dom.value); // hold input Of value Add to parameter list
ary.push(errorMsg); // hold errorMsg Add to parameter list
return strategies[strategy].apply(dom, ary);
});
};
Validator.prototype.start = function(){
for (var i=0, validatorFunc; validatorFunc = this.cache[i++];) {
var msg = validatorFunc(); // Start checking , And get the returned information after verification
if (msg) { // If there is an exact return value , Indicates that the verification failed
return msg;
}
}
}
Copy code
After refactoring the code using the policy pattern , Just passed “ To configure ” You can complete the verification of a form , These validation rules can also be reused anywhere in the program , It can also be in the form of plug-ins , It can be easily transplanted to other projects .
When modifying verification rules , Just write or rewrite a small amount of code . For example, if you want to change the verification rule of the user name input box to that the user name cannot be less than 10 Characters , The code is as follows
validator.add(registerForm.userName, 'isNonEmpty', ' The username cannot be empty ');
// Change to
validator.add(registerForm.userName, 'minLength:10', ' User name length cannot be less than 10 position ');
Copy code
7.3 Add multiple verification rules to a file input box
<html>
<body> <form action="http://xxx.com/register" id="registerForm" method="post"> Please enter a user name :<input type="text" name="userName" /> Please input a password :<input type="text" name="password" /> Please enter your mobile number :<input type="text" name="phoneNumber" /> <button> Submit </button> </form> <script> // Policy object var strategies = { isNonEmpty: function(val, errorMsg) { // Not empty if (value === '') { return errorMsg; } }, minLength: function(value, length, errorMsg) { // Limit the minimum length if(value.length < length) { return errorMsg; } }, isMobile: function(value, errorMsg) { if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) { // Cell phone number format return errorMsg } } } // Validator class var Validator = function(){ this.cache = []; // Save validation rules } Validator.prototype.add = function(dom, rules) { var self = this; for (var i=0, rule; rule = rules[i++];) { (function(rule){ var strategyAry = rule.strategy.split(':'); var errorMsg = rule.errorMsg; self.cache.push(function(){ var strategy = strategyAry.shift(); strategyAry.unshift(dom.value); strategyAry.push(errorMsg); return strategies[strategy].apply(dom, strategyAry); }) })(rule) } }; Validator.prototype.start = function(){ for (var i=0, validatorFunc; validatorFunc = this.cache[i++];) { var errorMsg = validatorFunc(); if (errorMsg) { return errorMsg; } } } // Customer call code var registerForm = document.getElementById('registerForm'); var validataFunc = function(){ var validator = new Validator(); validator.add(registerForm.userName, [{ strategy: 'isNonEmpty', errorMsg: ' The username cannot be empty ' },{ strategy: 'minLength:10', errorMsg: ' User name length cannot be less than 10 position ' }]) validator.add(registerForm.password, [{ strategy: 'minLength:6', errorMsg: ' Password length cannot be less than 6 position ' }]) validator.add(registerForm.phoneNumber, [{ strategy: 'isMobile', errorMsg: ' Incorrect format of mobile phone number ' }]) var errorMsg = validator.start(); return errorMsg; } registerForm.onsubmit = function(){ var errorMsg = validataFunc(); if (errorMsg) { alert(errorMsg) return false; } } </script> </body>
</html>
Copy code
5.8 Advantages and disadvantages of the policy model
advantage :
- Strategic patterns use combinations 、 Technologies and ideas such as delegation and polymorphism , It can effectively avoid multiple conditional selection statements
- The strategic model provides openness to - The perfect support of the closed principle , Encapsulate the algorithm in a separate strategy in , Make them easy to switch , Easy to understand , extensible
- The algorithm in the policy pattern can also be reused in other parts of the system , So as to avoid a lot of duplicate copy and paste work
- Use composition and delegation in a policy pattern to make Context Have the ability to execute algorithms , It's also a lighter alternative to inheritance
shortcoming :
-
Using the policy pattern will add many policy classes or policy objects to the program , But it's actually better than stacking the logic they're responsible for Context Better in China
-
Using the policy pattern , You have to know all about strategy, You have to know all of them strategy The difference between , So that we can choose the right one strategy . such as , We have to choose a suitable travel route , You must first understand the choice of aircraft 、 train 、 Details of plans like bicycles . here strategy To expose all its implementations to customers , At this time, those who violate the principle of least knowledge .
5.9 First class function objects and policy patterns
The previous policy pattern examples , Existing versions that simulate traditional object-oriented languages , There are also targets for JavaScript A unique implementation of the language . In the traditional class centered object-oriented language , Different algorithms or behaviors are encapsulated in various policy classes ,Context Delegate requests to these policy objects , These policy objects will return different execution results according to the request , This will show the polymorphism of the object
In languages where functions are first-class objects , The strategy model is invisible .strategy It's a variable whose value is a function . stay JavaScript in , In addition to using classes to encapsulate algorithms and behaviors , Using functions is of course an option . these ” Algorithm “ Can be encapsulated in functions and passed around , That's what I often say ” Higher order function “. In fact, JavaScript In a language that treats functions as first-class objects , The strategy model has been integrated into the language itself , We often use higher-order functions to encapsulate different behaviors , And pass it to another function . When we call these functions “ call ” Information time , Different functions will return different execution results . stay JavaScript in ,“ Polymorphism of function objects ” It's easier .
stay JavaScript in , Policy classes are often replaced by functions , At this time, the strategy mode becomes a kind of “ Invisible ” The pattern of .
copyright notice
author[Xiao Guo is in Shenzhen],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2021/08/20210827001435096a.html
The sidebar is recommended
- Crazy blessing! Tencent boss's "million JVM learning notes", real topic of Huawei Java interview 2020-2021
- JS JavaScript how to get the subscript of a value in the array
- How to implement injection in vuex source code?
- JQuery operation select (value, setting, selected)
- One line of code teaches you how to advertise on Tanabata Valentine's Day - Animation 3D photo album (music + text) HTML + CSS + JavaScript
- An article disassembles the pyramid architecture behind the gamefi outbreak
- BEM - a front-end CSS naming methodology
- [vue3] encapsulate custom global plug-ins
- Error using swiper plug-in in Vue
- Another ruthless character fell by 40000, which was "more beautiful" than Passat and maiteng, and didn't lose BMW
guess what you like
-
Huang Lei basks in Zhang Yixing's album, and the relationship between teachers and apprentices is no less than that in the past. Netizens envy Huang Lei
-
He was cheated by Wang Xiaofei and Li Chengxuan successively. Is an Yixuan a blessed daughter and not a blessed home?
-
Zhou Shen sang the theme song of the film "summer friends and sunny days" in mainland China. Netizen: endless aftertaste
-
Pink is Wangyuan online! Back to the peak! The new hairstyle is creamy and sassy
-
Front end interview daily 3 + 1 - day 858
-
Spring Webflux tutorial: how to build reactive web applications
-
[golang] walk into go language lesson 24 TCP high-level operation
-
August 23, 2021 Daily: less than three years after its establishment, Google dissolved the health department
-
The female doctor of Southeast University is no less beautiful than the female star. She has been married four times, and her personal experience has been controversial
-
There are many potential safety hazards in Chinese restaurant. The top of the program recording shed collapses, and the artist will fall down if he is careless
Random recommended
- Anti Mafia storm: He Yun's helpless son, Sun Xing, is destined to be caught by his dry son
- Introduction to flex flexible layout in CSS -- learning notes
- CSS learning notes - Flex layout (Ruan Yifeng tutorial summary)
- Today, let's talk about the arrow function of ES6
- Some thoughts on small program development
- Talk about mobile terminal adaptation
- Unwilling to cooperate with Wang Yibo again, Zhao Liying's fans went on a collective strike and made a public apology in less than a day
- JS function scope, closure, let, const
- Zheng Shuang's 30th birthday is deserted. Chen Jia has been sending blessings for ten years. Is it really just forgetting to make friends?
- Unveil the mystery of ascension
- Asynchronous solution async await
- Analysis and expansion of Vue infinite scroll source code
- Compression webpack plugin first screen loading optimization
- Specific usage of vue3 video play plug-in
- "The story of huiyeji" -- people are always greedy, and fairies should be spotless!
- Installing Vue devtool for chrome and Firefox
- Basic usage of JS object
- 1. JavaScript variable promotion mechanism
- Two easy-to-use animation JS that make the page move
- Front end Engineering - scaffold
- Java SQL Server intelligent fixed asset management, back end + front end + mobile end
- Mediator pattern of JavaScript Design Pattern
- Array de duplication problem solution - Nan recognition problem
- New choice for app development: building mobile applications using Vue native
- New gs8 Chengdu auto show announces interior Toyota technology blessing
- Vieira officially terminated his contract and left the team. The national security club sent blessings to him
- Less than 200000 to buy a Ford RV? 2.0T gasoline / diesel power, horizontal bed / longitudinal bed layout can be selected
- How does "heart 4" come to an end? Pinhole was boycotted by the brand, Ma Dong deleted the bad comments, and no one blessed him
- We are fearless in epidemic prevention and control -- pay tribute to the front-line workers of epidemic prevention!
- Front end, netty framework tutorial
- Xiaomi 11 | miui12.5 | android11 solves the problem that the httpcanary certificate cannot be installed
- The wireless charging of SAIC Roewe rx5 plus is so easy to use!
- Upload and preview pictures with JavaScript, and summarize the most complete mybatis core configuration file
- [25] typescript
- CSS transform Complete Guide (Second Edition) flight.archives 007
- Ajax foundation - HTTP foundation of interview essential knowledge
- Cloud lesson | explain in detail how Huawei cloud exclusive load balancing charges
- Decorator pattern of JavaScript Design Pattern
- [JS] 10. Closure application (loop processing)
- Left hand IRR, right hand NPV, master the password of getting rich