这是我参与「第四届青训营」笔记创作活动的第2天
一、什么是JS
js是JavaScript的简称,是一种直译式脚本语言,它是web前端中不可缺少的一部分,它用于增强HTML页面,通常可以嵌入HTML代码中,JavaScript以交互式和动态的方式呈现网页,这允许页面对事件做出反应,展示特殊效果。
JS是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言。它经常和web前端放在一起,那么web前端js是什么呢?
web前端js
Javascript(JS)主要用于 Web。它用于增强 HTML 页面,通常可以嵌入 HTML 代码中。JavaScript 以交互式和动态的方式呈现网页。这允许页面对事件做出反应,展示特殊效果,接受可变文本,验证数据,创建 cookie,检测用户的浏览器等。
二、写好JS的原则
-
各司其职
让HTML、CSS和JavaScript职能分离。
-
组件封装
好的UI组件具备正确性、扩展性、复用性。
-
过程抽象
应用函数式编程思想
各司其职原则
写一段JS,控制一个网页,让他支持浅色和深色两种浏览模式。如果是你来实现,你会怎么做?
代表一:入门级选手
分析:该版本通过JS代码改变颜色,难以让其他人读懂代码。
代表二:进阶选手
分析:通过修改body元素的class属性改变颜色,较好。
代表三:终极选手
分析:样式效果完全由CSS实现,实现了各司其职原则,该代码更好。
结论
- HTML/CSS/JS 各司其责
- 应当避免不必要的由 JS 直接操作样式
- 可以用 class 来表示状态
- 纯展示类交互寻求零 JS 方案
组件封装原则
组件是指Web页面上抽出来一个个包含模版(HTML)、功能(JS)和样式(CSS)的单元。好的组件具备封装性、正确性、扩展性、复用性。
用原生 JS 写一个电商网站的轮播图,应该怎么实现?
分析1:结构HTML
轮播图是一个典型的列表结构,我们可以使用无序列表ul元素来实现。
<div id="my-slider" class="slider-list">
<ul>
<li class="slider-list__item--selected">
<img src="https://p5.ssl.qhimg.com/t0119c74624763dd070.png">
</li>
<li class="slider-list__item">
<img src="https://p4.ssl.qhimg.com/t01adbe3351db853eb3.jpg">
</li>
<li class="slider-list__item">
<img src="https://p2.ssl.qhimg.com/t01645cd5ba0c3b60cb.jpg">
</li>
<li class="slider-list__item">
<img src="https://p4.ssl.qhimg.com/t01331ac159b58f5478.jpg">
</li>
</ul>
</div>
分析2:表现CSS
- 使用 CSS 绝对定位将图片重叠在同一个位置
- 轮播图切换的状态使用修饰符(modifier)
- 轮播图的切换动画使用 CSS transition
#my-slider{
position: relative;
width: 790px;
}
.slider-list ul{
list-style-type:none;
position: relative;
padding: 0;
margin: 0;
}
.slider-list__item,
.slider-list__item--selected{
position: absolute;
transition: opacity 1s;
opacity: 0;
text-align: center;
}
.slider-list__item--selected{
transition: opacity 1s;
opacity: 1;
}
分析3:行为JS
-
API:Slider
- +getSelectedItem()
- +getSelectedItemIndex()
- +slideTo()
- +slideNext()
- +slidePrevious()
-
控制流:
- 使用自定义事件来解耦。
-
重构:插件化
解耦
- 将控制元素抽取成插件
- 插件与组件之间通过依赖注入方式建立联系
-
重构:模板化
解耦
- 将HTML模板化,更易于扩展
-
组件框架
抽象
- 将组件通用模型抽象出来
class Slider{
constructor(id){
this.container = document.getElementById(id);
this.items = this.container
.querySelectorAll('.slider-list__item, .slider-list__item--selected');
}
getSelectedItem(){
const selected = this.container
.querySelector('.slider-list__item--selected');
return selected
}
getSelectedItemIndex(){
return Array.from(this.items).indexOf(this.getSelectedItem());
}
slideTo(idx){
const selected = this.getSelectedItem();
if(selected){
selected.className = 'slider-list__item';
}
const item = this.items[idx];
if(item){
item.className = 'slider-list__item--selected';
}
}
slideNext(){
const currentIdx = this.getSelectedItemIndex();
const nextIdx = (currentIdx + 1) % this.items.length;
this.slideTo(nextIdx);
}
slidePrevious(){
const currentIdx = this.getSelectedItemIndex();
const previousIdx = (this.items.length + currentIdx - 1)
% this.items.length;
this.slideTo(previousIdx);
}
}
const slider = new Slider('my-slider');
slider.slideTo(3);
结论
-
组件设计的原则:封装性、正确性、扩展性、复用性
-
实现组件的步骤:结构设计、展现效果、行为设计
-
三次重构
- 插件化
- 模板化
- 抽象化(组件框架)
过程抽象原则
- 用来处理局部细节控制的一些方法
- 函数式编程思想的基础应用
例如:操作次数限制
- 一些异步交互
- 一次性的HTTP请求
const list = document.querySelector('ul');
const buttons = list.querySelectorAll('button');
buttons.forEach((button) => {
button.addEventListener('click', (evt) => {
const target = evt.target;
target.parentNode.className = 'completed';
setTimeout(() => {
list.removeChild(target.parentNode);
}, 2000);
});
});
高阶函数
JavaScript的函数其实都指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
- Once
为了能够让“只执行一次“的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为过程抽象。
function once(fn) {
return function(...args) {
if(fn) {
const ret = fn.apply(this, args);
fn = null;
return ret;
}
}
}
- HOF
- 以函数作为参数
- 以函数作为返回值
- 常用于作为函数装饰器
function HOF0(fn) {
return function(...args) {
return fn.apply(this, args);
}
}