这些年来,随着 HTML5 和 Node.js 的发展,JavaScript 在各个领域遍地开花,已经从“世界上最被误解的语言”变成了“世界上最流行的语言”。但是由于历史原因,JavaScript 语言设计中还是有一些糟粕和鸡肋,比如:全局变量、自动插入分号、typeof、NaN、假值、==、eval 等等,并不能被语言移除。 本次青训营的学习中,我了解到一些写好javascript的一些原则:
- html、css、javascript各司其职
1.HTML来操作页面结构,CSS来操作页面样式,JS来操作页面功能。
2.能用css直接操作样式就尽可能不要使用js来实现样式的改变
3.纯展示页面就不要使用js,尽可能使用高级的css属性来实现
例子
我们需要写一段JavaScript代码来控制一个网页的样式,让它支持浅色🌞和深色🌛两种浏览模式。我们容易想到的是使用 addEventListener() 方法,针对 click 事件传入一个保存待执行动作的函数:
btn.addEventListener("click", (e) =\> {
const main = document.getElementById("main");
if (e.target.innerHTML === "🌞") {
main.style.backgroundColor = "black";
main.style.color = "white";
e.target.innerHTML = "🌛";
} else {
main.style.backgroundColor = "white";
main.style.color = "black";
e.target.innerHTML = "🌞";
}
});
这段JavaScript代码已经能够满足我们切换不同模式的需要。那么是否还有更好的方案?
-
改进
如果设计师对于不同模式的样式有更改的需求,比如说将深色模式下的背景色变换为深蓝色🌀、字体颜色变换为淡黄色🍋,我们需要如何改动已有的代码?
在已有代码的基础上,我们只能改动JavaScript中的代码才能满足设计师的样式升级需求。但是这样似乎太麻烦了,因为样式改动的越多,对于我们修改代码就越不利。
为了提升代码的可维护性,我们可以使用下面的方法:
btn.addEventListener('click', (e) =\> {
const main = document.getElementById("main");
if(main.className !== 'night') {
main.className = 'night';
} else {
main.className = '';
}
});
这里通过给具体的模式绑上对应类名的方式,如给深色模式绑定上 night 类,让模式切换时带动类名的切换,通过不同类名绑定不同的CSS样式,达到样式变更的目的。
这样一来,对样式变更的需求只需要修改对应类的CSS代码即可满足,项目的可维护性也得到了提升。这样似乎已经是一个很不错的解决方案了,但是否还能继续改进?
-
再改进
看下面的一个解决方案:
\<div id="main"\>
\<div id="title"\>
\<h1\>The Title\</h1\>
\<label for="btn"\>\</label\>
\</div\>
\<p\>🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵🌵\<p\>
\<p\>
HTML中有多种多样的标签,可以根据标签及其属性的名字理解这一标签的作用与意义
——这就是标签的语义化。在开发过程中,可能有时会陷入注重样式而忽略了内容本身
的误区,从而与标签语义化的初衷背道而驰......
\</div\>
\#btn{
display: none;
}
\#btn:checked+\#main {
color: white;
background-color: black;
}
\#main label::before {
content: '🌞';
}
\#btn:checked+\#main label::before {
content: '🌛';
}
在这一版本的解决方案中,样式的更改在HTML的基础上完全依靠CSS实现,没有使用任何一行JavaScript代码。
这是因为使用了HTML的复选框和CSS中的伪类选择器,并且使用了 <label></label> 标签将代表模式的小图标和复选框关联了起来。当复选框被选中时,CSS中的伪类选择器便会给页面换上对应的样式;复选框取消选中时,样式又会恢复初始时的样子。
| 这个方案没有用到JavaScript,但可维护性也没有下降。比起前两个方案,真正体现了 HTML\CSS\JavaScript 的各司其职——HTML对应结构、CSS负责样式、JavaScript管理行为。 |
|---|
-
组件封装
好的UI组件具备正确性、扩展性、复用性
1.实现组件的步骤:
结构设计(布局好组件的HTML)
展现效果(布局好组件的CSS)
行为设计(布局好组件的JS)
2.组件的设计原则
封装性(可以直接引用)
正确性(组件功能齐全)
扩展性(组件需要可以拓展功能)
复用性(组件需要有许多的应用场景)
3.三次重构
1. 插件化重构(解耦)
-将控制元素抽取成插件
-插件与组件之间通过依赖注入方式建立联系
2. 模板化重构
-将HTML模板化,更易于扩展
3. 抽象化重构(组件框架)
-将组件通过模板抽象出来
-
过程抽象
diff
复制代码
应用函数式编程思想。
- 提倡组合(composition),组合是王道。
- 每个函数尽可能完成单一的功能。
- 屏蔽细节,告诉计算机我要做什么,而不是怎么做。我们看 filter / map,它们并未暴露自身的细节。一个 filter 函数的实现,在单核 CPU 上可能是一个循环,在多核 CPU 上可能是一个 dispatcher 和 aggregator,但我们可以暂时忽略它的实现细节,只需了解它的功能即可。
- 尽可能不引入或者少引入状态。
1.使用有意义,可读性好的变量名
2.使用 ES6 的 const let 定义常量
3.使用易于检索的名称
4.使用说明性的变量(即有意义的变量名)
5.方法:保持函数功能的单一性
6.函数名应明确表明其功能(见名知意)
7.使用默认变量替代短路运算或条件
8.函数参数 (理想情况下应不超过 2 个)
9.移除重复代码
这些特点运用得当的话,能够为软件带来:
- 更好的设计和实现。
- 更加清晰可读的代码。由于状态被大大减少,代码更容易维护,也带来更强的稳定性。
- 在分布式系统下有更好的性能。函数式编程一般都在一个较高的层次进行抽象,map / filter / reduce 就是其基础指令,如果这些指令为分布式而优化,那么系统无需做任何改动,就可以提高性能。
- 使得惰性运算成为可能。在命令式编程中,由于你明确告诉了 CPU 一步步该怎么操作,CPU 只能俯首听命,优化的空间已经被挤压;而在函数式编程里,每个函数只是封装了运算,一组数据从输入经历一系列运算到输出,如果没有人处理这些输出,则运算不会被真正执行。