这是我参与「第四届青训营」笔记创作活动的第五天
二、如何写好JavaScript(2)
当年的Leftpad事件
function leftpad(str, len, ch) {
str = String(str);
var i = -1;
if (!ch && ch !== 0) ch = ' ';
len = len - str.length;
while (++i < len) {
str = ch + str;
}
return str;
}
事件本身的槽点:
- NPM 模块粒度
- 代码风格
- 代码质量/效率
新版本
function leftpad(str, len, ch) {
str = "" + str;
const padLen = len - str.length;
if(padLen <= 0) {
return str;
}
return (""+ch).repeat(padLen)+str;
}
- 代码更简洁
- 效率提升
/*! https://mths.be/repeat v1.0.0 by @mathias */
'use strict';
var RequireObjectCoercible = require('es-abstract/2019/RequireObjectCoercible');
var ToString = require('es-abstract/2019/ToString');
var ToInteger = require('es-abstract/2019/ToInteger');
module.exports = function repeat(count) {
var O = RequireObjectCoercible(this);
var string = ToString(O);
var n = ToInteger(count);
// Account for out-of-bounds indices
if (n < 0 || n == Infinity) {
throw RangeError('String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity');
}
var result = '';
while (n) {
if (n % 2 == 1) {
result += string;
}
if (n > 1) {
string += string;
}
n >>= 1;
}
return result;
};
- 性能更好
/**
* String.prototype.repeat() polyfill
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat#Polyfill
*/
if (!String.prototype.repeat) {
String.prototype.repeat = function(count) {
'use strict';
if (this == null)
throw new TypeError('can\'t convert ' + this + ' to object');
var str = '' + this;
// To convert string to integer.
count = +count;
// Check NaN
if (count != count)
count = 0;
if (count < 0)
throw new RangeError('repeat count must be non-negative');
if (count == Infinity)
throw new RangeError('repeat count must be less than infinity');
count = Math.floor(count);
if (str.length == 0 || count == 0)
return '';
// Ensuring count is a 31-bit integer allows us to heavily optimize the
// main part. But anyway, most current (August 2014) browsers can't handle
// strings 1 << 28 chars or longer, so:
if (str.length * count >= 1 << 28)
throw new RangeError('repeat count must not overflow maximum string size');
var maxCount = str.length * count;
count = Math.floor(Math.log(count) / Math.log(2));
while (count) {
str += str;
count--;
}
str += str.substring(0, maxCount - str.length);
return str;
}
}
- 性能更好
交通灯状态切换
实现一个切换多个交通灯状态切换的功能
交通灯:版本一
html:
<ul id="traffic" class="wait">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
css:
#traffic {
display: flex;
flex-direction: column;
}
#traffic li{
list-style: none;
width: 50px;
height: 50px;
background-color: gray;
margin: 5px;
border-radius: 50%;
}
#traffic.s1 li:nth-child(1) {
background-color: #a00;
}
#traffic.s2 li:nth-child(2) {
background-color: #aa0;
}
#traffic.s3 li:nth-child(3) {
background-color: #0a0;
}
#traffic.s4 li:nth-child(4) {
background-color: #a0a;
}
#traffic.s5 li:nth-child(5) {
background-color: #0aa;
}
js:
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);
})();
交通灯:版本二(数据抽象)
html:
<ul id="traffic" class="wait">
<li></li>
<li></li>
<li></li>
</ul>
css:
#traffic {
display: flex;
flex-direction: column;
}
#traffic li {
display: inline-block;
width: 50px;
height: 50px;
background-color: gray;
margin: 5px;
border-radius: 50%;
}
#traffic.stop li:nth-child(1) {
background-color: #a00;
}
#traffic.wait li:nth-child(2) {
background-color: #aa0;
}
#traffic.pass li:nth-child(3) {
background-color: #0a0;
}
js:
const traffic = document.getElementById('traffic');
const stateList = [ {state: 'wait', last: 1000}, {state: 'stop', last: 3000}, {state: 'pass', last: 3000},];
function start(traffic, stateList){
function applyState(stateIdx) {
const {state, last} = stateList[stateIdx];
traffic.className = state;
setTimeout(() => {
applyState((stateIdx + 1) % stateList.length);
}, last)
}
applyState(0);
}
start(traffic, stateList);
交通灯:版本三(过程抽象)
html:
<ul id="traffic" class="wait">
<li></li>
<li></li>
<li></li>
</ul>
css:
#traffic {
display: flex;
flex-direction: column;
}
#traffic li{
display: inline-block;
width: 50px;
height: 50px;
background-color: gray;
margin: 5px;
border-radius: 50%;
}
#traffic.stop li:nth-child(1) {
background-color: #a00;
}
#traffic.wait li:nth-child(2) {
background-color: #aa0;
}
#traffic.pass li:nth-child(3) {
background-color: #0a0;
}
js:
const traffic = document.getElementById('traffic');
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function poll(...fnList){
let stateIndex = 0;
return async function(...args){
let fn = fnList[stateIndex++ % fnList.length];
return await fn.apply(this, args);
}
}
async function setState(state, ms){
traffic.className = state;
await wait(ms);
}
let trafficStatePoll = poll(setState.bind(null, 'wait', 1000),
setState.bind(null, 'stop', 3000),
setState.bind(null, 'pass', 3000));
(async function() {
// noprotect
while(1) {
await trafficStatePoll();
}
}());
交通灯:版本四(异步+函数式)
html:
<ul id="traffic" class="wait">
<li></li>
<li></li>
<li></li>
</ul>
css:
#traffic {
display: flex;
flex-direction: column;
}
#traffic li{
display: inline-block;
width: 50px;
height: 50px;
background-color: gray;
margin: 5px;
border-radius: 50%;
}
#traffic.stop li:nth-child(1) {
background-color: #a00;
}
#traffic.wait li:nth-child(2) {
background-color: #aa0;
}
#traffic.pass li:nth-child(3) {
background-color: #0a0;
}
js:
const traffic = document.getElementById('traffic');
function wait(time){
return new Promise(resolve => setTimeout(resolve, time));
}
function setState(state){
traffic.className = state;
}
async function start(){
//noprotect
while(1){
setState('wait');
await wait(1000);
setState('stop');
await wait(3000);
setState('pass');
await wait(3000);
}
}
start();
判断是否是4的幂
html:
<input id="num" value="65536"></input>
<button id="checkBtn">判断</check>
css:
#num {
color: black;
}
#num.yes {
color: green;
}
#num.no {
color: red;
}
js:
// function isPowerOfFour(num) {
// num = parseInt(num);
// while(num > 1) {
// if(num % 4) return false;
// num /= 4;
// }
// return num === 1;
// }
// function isPowerOfFour(num) {
// num = parseInt(num);
// while(num > 1) {
// if(num & 0b11) return false;
// num >>>=2;
// }
// return num === 1;
// }
// function isPowerOfFour(num) {
// num = parseInt(num).toString(2);
// return /^1(?:00)*$/.test(num);
// }
function isPowerOfFour(num){
num = parseInt(num);
return num > 0 &&
(num & (num - 1)) === 0 &&
(num & 0xAAAAAAAAAAAAA) === 0;
}
num.addEventListener('input', function(){
num.className = '';
});
checkBtn.addEventListener('click', function(){
let value = num.value;
num.className = isPowerOfFour(value) ? 'yes' : 'no';
});
洗牌
洗牌-错误写法
html:
<div id="app">洗牌-错误写法</div>
<hr/>
<div id="log"></div>
<script>
window.console = JCode.logger(log);
</script>
js:
const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
function shuffle(cards) {
return [...cards].sort(() => Math.random() > 0.5 ? -1 : 1);
}
console.log(shuffle(cards));
const result = Array(10).fill(0);
for(let i = 0; i < 1000000; i++) {
const c = shuffle(cards);
for(let j = 0; j < 10; j++) {
result[j] += c[j];
}
}
console.table(result);
洗牌-正确写法
html:
<div id="app">洗牌-正确写法</div>
<hr/>
<div id="log"></div>
<script>
window.console = JCode.logger(log);
</script>
const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
function shuffle(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]];
}
return c;
}
console.log(shuffle(cards));
const result = Array(10).fill(0);
for(let i = 0; i < 10000; i++) {
const c = shuffle(cards);
for(let j = 0; j < 10; j++) {
result[j] += c[j];
}
}
console.table(result);
洗牌-使用生成器
html:
<div id="app">洗牌-生成器</div>
<hr/>
<div id="log"></div>
<script>
window.console = JCode.logger(log);
</script>
js:
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];
}
}
const result = draw(cards);
console.log([...result]);
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —