Three principles for writing JavaScript: each responsible component encapsulation process abstraction
Each party shall take its own responsibility
HTML/CSS/JavaScript each has its own responsibilities
- HTML -> Structural ; CSS -> Presentational ; JavaScript -> Behavioral
- Unnecessary direct manipulation of styles by JS should be avoided
- You can use class to represent status
- Zero JS scheme should be sought for pure presentation class interaction
Component encapsulation
A component is a unit that contains modules (HTML), styles (CSS) and functions (JS) extracted from a Web page. Good components have encapsulation, correctness, expansibility and reusability. The steps of implementing components: structure design, presentation effect, behavior design, and three refactorings: plug-in refactoring, template refactoring, and abstract refactoring.
- Structural design: HTML
- Presentation effect: CSS
Behavior design: JS
- API (function), API design shall ensure atomic operation, single responsibility and flexibility.
- Event (control flow), which uses custom events to decouple.
Plug in refactoring, i.e. decoupling
- Extract control elements into plug-ins
- The relationship between plug-ins and components is established through dependency injection
Templating reconstruction
- Template HTML for easier extension
Abstract refactoring (component framework)
- Abstract the general component model
Process abstraction
Process abstraction refers to some methods used to deal with local detail control. It is the basic application of functional programming.
Higher order function
- Take function as parameter
- Take function as return value
- Often used in function decorators
//Zero order higher-order function, equivalent to direct call function function HOF_0 (fn){ return function(...args){ return fn.apply(this.args); } }
- Construct the once high-order function. In order to enable the "execute only once" requirement (such as some asynchronous operations and one-time HTTP requests) to cover different event processing, we can separate this requirement by using closures. This process is called process abstraction.
function once ( fn ) { return function (...args){ const res = fn.allpy (this.args); fn = null; return res; } }
The anti shake function does not execute the function immediately when the event is triggered for the first time, but gives a period value. If the rolling event is not triggered again within the period value, the function will be executed. If the rolling event is triggered again within the period value, the current timing will be cancelled and the timing will be restarted
const debounce = (fn, delay) => { let timer = null; //With closures return function () { clearTimeout(timer); timer = setTimeout(fn, delay); } }
Throttling function is a function that is opened regularly like a control valve, that is, after the function is executed once, it will temporarily fail in a certain period of time, and then reactivate after this period of time. Effect: if a large number of the same events are triggered in a short time, after the function is executed once, the function will not work within the specified time period until it takes effect again.
const myThrottle = (fn, delay) => { let flag = true; return function () { if (!flag) { return false; } else { flag = false; setTimeout(() => { fn(); flag = true; }, delay); } } }
Why use higher order functions?
- Reduce the number of non pure functions in the system and improve the testability and stability of the system.
Write JavaScript
Writing code should pay attention to
- efficiency
- style
- Usage scenario
- appointment
- Design
The specific code implementation depends on the scenario. Different scenarios focus on different points. For example, in some low-level scenarios, they may pay more attention to efficiency, and in multi person collaboration, they may pay more attention to conventions.
Determine whether it is a power of 4
General operation
const isPowerOfFour = (num) => { num = parseInt(num); while(num > 1){ if(num % 4) return false; num /= 4; } return true; }
Optimized version 1, the binary number using the power of 4, the highest bit is 1, and the lower bit is an even number of 0
const isPowerOfFour = (num) => { num = parseInt(num); return num > 0 && (num & (num-1)) === 0 && (num & 0XAAAAAAAA) === 0; }
- Optimized version 2, using regular expressions
const isPowerOfFour = (num) => { num = parseInt(num).toString(2); return /^1(?:00)*$/.test(num); }
Achieve traffic light switching effect
In version 1, a callback may occur using setTimeout
const traffic = document.getElementById('traffic'); (function reset(){ traffic.className = 's1'; setTimeout(function(){ traffic.className = 's2'; setTimeout(function(){ traffic.className = 's3'; setTimeout(function(){ traffic.className = 's4'; setTimeout(function (){ traffic.className = 's5'; setTimeout(reset,1000) },1000) },1000) },1000) },1000) })();
Optimized version, using async/await
const traffic = document.getElementById('traffic'); function wait(time){ return new Promise(resolve => setTimeout(resolve,time)) } function setState(state){ traffic.className = state; } async function start(){ while(1){ setState('wait'); await wait(1000); setState('stop'); await wait(3000); setState('pass'); await wait(3000); } } start();
shuffle algorithm
Error example
It seems that you can shuffle cards correctly, but in fact, the probability of smaller cards in front is greater, and the probability of larger cards in the back is greaterconst cards = [0,1,2,3,4,5,6,7,8,9]; const shuffle = (cards) => { return [...cards].sort(() => { Math.random() > 0.5 ? -1 : 1 }); }
- Correct example
const cards = [0,1,2,3,4,5,6,7,8,9]; function *draw(cards){ const c = [...cards]; for(let i = c.length ; i > 0 ; i--){ const pIdx = Math.floor(Math.random() * i); [c[pIdx],c[i - 1]] = [c[i - 1],c[pIdx]]; yield c[i - 1]; } return c; }
Bonus package problem
Version 1 is similar to cutting a cake. Continue to cut the largest one, and the results will be more average
function generate(amount,count){ let ret = [amount]; while(count > 1){ // Pick out the largest for segmentation let cake = Math.max(...ret), idx = ret.indexOf(cake), part = 1 + Math.floor((cake / 2) * Math.random()), rest = cake - part; ret.splice(idx,1,part,rest); count --; } return ret; }
- In version 2, the randomness is large, and there may be large red envelopes
function *draw(cards){ const c = [...cards]; for(let i = c.length ; i > 0 ; i --){ const pIdx = Math.floor(Math.random()*i); [c[pIdx],c[i-1]] = [c[i - 1],c[pIdx]]; yield c[i-1] } return c; } function generate(amount,count){ if(count <= 1) return [amount]; const cards = Array(amount - 1).fill(null).map((_,i) => i+1); const pick = draw(cards); const result = []; for(let i = 0 ; i < count; i++){ result.push(pick.next().value); } result.sort((a,b)=>a-b); for(let i = count - 1 ; i > 0 ; i--){ result[i] = result[i] - result[i - 1]; } return result; }