这是我参与「第四届青训营 」笔记创作活动的的第2天
一、课堂重点内容
写好JS的原则
- 各司其职:让HTML、CSS和JavaScript职能分离
- 组件封装:好的UI组件具备正确性、拓展性、复用性
- 过程抽象:应用函数式编程思想
算法
- Leftpad事件
- 洗牌算法
- 分红包-切西瓜法
- 分红包-抽牌法
二、详细知识点介绍
各司其职
- HTML/CSS/JS 各司其责
- 应当避免不必要的由 JS 直接操作样式
- 可以用 class 来表示状态
- 纯展示类交互寻求零 JS 方案
组件封装
- 组件设计的原则:封装性、正确性、扩展性、复用性
- 实现组件的步骤:结构设计、展现效果、行为设计
- 三次重构
- 插件化
- 模板化
- 抽象化(组件框架)
过程抽象
洗牌算法
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;
}
即每次随机抽取一个数放在最后一位,然后最后一位的标记往前移动一次。通过高中数学的排列组合知识可以知道,每个数出现在每个位置的概率相同,即实现了洗牌算法的公平性。
分红包-切西瓜法:
function generate(amount, count){
let ret = [amount];
while(count > 1){
//挑选出最大一块进行切分
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;
}
第一次随机切,然后每一次从剩余中的最大瓜切一刀。 相对均匀,不够刺激。
分红包-抽牌法: 从一个序列中随机插入几个断点将整体分成目标份数。 比如说有100元分给十个人,以0.01元为最小单位,100元包含10000份,然后用洗牌算法抽出十个值作为断点,然后排序,比如说第一个断点是49,第二个断点是160,那么第一份就是49*0.01=0.49元,第二份为(160-49)*0。01=1.11元。该方法充分体现了发红包的随机性,但缺点是空间复杂度较高。
三、实践联系例子
各司其职
例:写一段JS,控制一个网页,让它支持浅色和深色两种浏览模式。
版本一:通过JS控制该页面样式,属于反面教材,虽然达到了效果,但是通过js修改样式,后期需求变更会有麻烦
const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e) => {
const body = document.body;
if(e.target.innerHTML === '🌞') {
body.style.backgroundColor = 'black';
body.style.color = 'white';
e.target.innerHTML = '🌜';
} else {
body.style.backgroundColor = 'white';
body.style.color = 'black';
e.target.innerHTML = '🌞';
}
});
版本二:通过JS修改body类名,在CSS中设置该类的样式
const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e) => {
const body = document.body;
if(body.className !== 'night') {
body.className = 'night';
} else {
body.className = '';
}
});
版本三:通过修改checkbox的状态实现更新页面,但此处可以避免使用js
<input id="modeCheckBox" type="checkbox">
<div class="content">
<header>
<label id="modeBtn" for="modeCheckBox"></label>
<h1>深夜食堂</h1>
</header>
<main>
<div class="pic">
<img src="https://p2.ssl.qhimg.com/t0120cc20854dc91c1e.jpg">
</div>
<div class="description">
<p>
这是一间营业时间从午夜十二点到早上七点的特殊食堂。这里的老板,不太爱说话,却总叫人吃得热泪盈
眶。在这里,自卑的舞蹈演员偶遇隐退多年舞界前辈,前辈不惜讲述自己不堪回首的经历不断鼓舞年轻人,最终令其重拾自信;轻言绝交的闺蜜因为吃到共同喜爱的美食,回忆起从前的友谊,重归于好;乐观的绝症患者遇到同命相连的女孩,两人相爱并相互给予力量,陪伴彼此完美地走过了最后一程;一味追求事业成功的白领,在这里结交了真正暖心的朋友,发现真情比成功更有意义。食物、故事、真情,汇聚了整部剧的主题,教会人们坦然面对得失,对生活充满期许和热情。每一个故事背后都饱含深情,情节跌宕起伏,令人流连忘返 [6] 。
</p>
</div>
</main>
</div>
#modeCheckBox {
display: none;
}
#modeCheckBox:checked + .content {
background-color: black;
color: white;
transition: all 1s;
}
四、课后个人总结
关于洗牌算法
网络上搜到的洗牌算法经常是以下思路
const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
function shuffle(cards) {
return [...cards].sort(() => Math.random() > 0.5 ? -1 : 1);
}
即采用sort方式,通过随机数与0,5比较决定是否交换位置,但是这样的方式会出现问题,因为每个数字出现在不同位次的几率不同
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);
通过如上方式对1000000个数据进行了测试。
可以看到,index越小值越小。因为,拿0举例,它被交换到第9位的概率低于它保持原位的概率。根据正态分布也能得到这个结论。
关于各司其责
各司其责的原则不代表不同的代码必须写在不同的地方,而是不同的代码负责不同的工作。比如在html里写了一段js,该js只负责一个行为,那么也是复合各司其责的原则的