[前端]JS学习(1)


学习JS(1)

从原则,风格上讨论。

JavaScript -> Behavioral

CSS -> Presentational

HTML -> Structural

各司其责、组件封装、过程抽象

1. 白天黑夜模式转换

写一段JS,控制一个网页,让它支持浅色和深色两种浏览模式。

在这里插入图片描述

版本1
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 = '🌞';
  }
});
版本2
const btn = document.getElementById('modeBtn');
btn.addEventListener('click', (e) => {
	const body = document.body;
	if(body.className !== 'night'){
		body.className = 'night';
	} else {
		body.className = '';
	}
});
版本3

在这里插入图片描述

<body>
	<input id="modeCheckBox" type="checkbox">
	<div class="content">
		<header>
			<label id="modeBtn" for="modeCheckBox"></label>
			<h1>深夜食堂</h1>
		</header>
		<main>
		...
		</main>
	</div>
</body>

html的body中content前用隐藏的CheckBox,content下header中label可以for一个input元素,点击label时浏览器直接操作到CheckBox;状态伪类+兄弟选择器:checked状态+.content来控制选中的样式。

#modeCheckBox {
	display: none;
}
#modeCheckBox: checked + .content {
	background-color: black;
	color: white;
	transition: all 1s;
}

太阳月亮是按钮

#modeBtn::after {
	content:'🌞';
}

在这里插入图片描述

评价

版本1:正确性没有问题,但用js做了css应该做的事情,功能扩展改动逻辑不清楚。(重构为状态切换)

版本2:显而易见两种状态切换。

版本3:纯视觉展示类的功能可以不用js(js负责行为)实现,可以用纯html/css实现

版本3适应性没有2好,但是没有bug

总结

HTML/CSS/JS各司其责

避免不必要的由 JS 直接操作样式;

可以用class表示状态;

纯展示类交互寻求零JS方案。

2. 组件封装 - 原生JS写轮播图

用原生JS 写一个电商网站的轮播图,应该怎么实现?

在这里插入图片描述

组件:

组件是指Web页面上抽出来一个个包含模版(HTML)、功能(JS)和样式(CSS)的单元。

好的组件具备封装性、正确性、扩展性、复用性。

现在:

响应式框架都有默认组件原则、扩展组件机制;

设计系统也都有组件的设计原则、范式、有封装好的组件;

因而忽视原生组件;

抽象过程会更好理解框架、设计系统等

结构设计

(列表结构)

结构:HTML
轮播图是一个典型的列表结构,我们可以使用无序列表

  • 元素来实现。

<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>
展现效果

表现: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;
}
行为设计:API

行为:JS
API设计应保证原子操作,职责单一,满足灵活性。

在这里插入图片描述

设计Slider(当前选中的哪一个、当前是第几张、轮播到、下一张、前一张)

在这里插入图片描述

一个构造器 + 五个api实现操作

使用时new一个Slider

const slider = new slider('my-slider');
slider.slideTo(1);//显示第一张图

在这里插入图片描述

接下来加控制的点

行为:控制流

使用自定义事件来解耦。

添加左右两个a标签,下面四个点切换

在这里插入图片描述

在这里插入图片描述

四个点的操作与轮播是解耦的,便于修改需求。

四个小点注册mouseover方法,移走鼠标调用start方法。

start(){
	this.stop();
	this._timer = setInterval(()=>this.slideNext.(),this.cycle);
}
stop(){
	clearInterval(this._timer) ;
}

写start与stop两个方法,start用setInterval到下一张图,stop清掉定时器。

自动轮播与小圆点保持同步:监听slide自定义事件,idx更新。

更新:在slideTo中派发slide事件,浏览器中支持CustomEvent方法,将‘slide’当作参数派发,然后用container容器去dispatch这个event,上面就可以监听到。

在这里插入图片描述
在这里插入图片描述

总结:基本方法

结构设计;展现效果;行为设计【API(功能);Event(控制流)】

改进:更好的扩展性、复用性

重构:插件化

解耦:

将控制元素抽取成插件
插件与组件之间通过依赖注入方式建立联系

如两边框,四个点。

组件一般不超过十四行代码,最多二三十行,再多就需要重构拆分。

在这里插入图片描述

上述重构插件化:

constructor只有三行代码:

在这里插入图片描述

增加一个registerPlugins方法,注册插件,插件要依赖于主件Slider,但是不希望Slider知道插件的存在,依赖注入,将this实例当作参数传入插件。

这样将插件的逻辑从constructor中独立出来。

在这里插入图片描述
在这里插入图片描述

三个独立插件方法,注册三个插件再start。

以后不想使用插件可以注释掉,就不能使用了,相关html需要删掉,不影响其他功能。
在这里插入图片描述

重构:模板化

将HTML模板化,更易于扩展。

在这里插入图片描述
在这里插入图片描述

在主件和插件中都使用render方法实现。

HTML:只剩下单一元素

<div id="my-slider" class="slider-list"> </div>

内容:用JS模板化渲染,数据抽象,images封装为数组,根据数据渲染HTML。

插件修改:插件也有插件的容器,然后action初始化操作。

在这里插入图片描述
在这里插入图片描述

最终new一个Slider时把image传入,注册三个插件,start。
在这里插入图片描述

好处:现在如果不想要下面的圆点,只需要将组件注释掉,不需要修改其他地方即可。

(css也可以模板化)

重构:组件框架

再进一步

抽象:将通用的组件模型抽象出来

在这里插入图片描述

Slider继承Component,实现独有方法

抽象出一个Component类:(很小的组件框架)

在这里插入图片描述

主件、插件分开,可以有层级的,主件一级一级组合、父组件子组件等。

总结

组件设计的原则:封装性、正确性、扩展性、复用性。

实现组件的步骤︰结构设计、展现效果、行为设计。

三次重构:插件化;模板化;抽象化(组件框架)。

改进:CSS模板化;主件框架多层级,父组件子组件间状态同步,消息通信等

3. 过程抽象

用来处理局部细节控制的一些方法;
函数式编程思想的基础应用。

一个方法可以看作一个输入输出的黑盒,抽象。

操作次数限制

一些异步交互
一次性的HTTP请求

在这里插入图片描述

这种情况多次点击会报错,对只执行一次的过程抽象。

Once

为了能够让“只执行一次“的需求覆盖不同的事件处理,我们可以将这个需求剥离出来。这个过程我们称为过程抽象。
在这里插入图片描述

function once(fn){
	return function (...args) {
		if(fn) {
			const ret = fn.apply(this,args);
			fn = null;
			return ret;
		}
	};
}

const list = document.querySelector('ul');
const buttons = list.queryselectorAll('button') ;
buttons.forEach((button) => {
	button.addEventListener('click', once((evt) => {
	const target = evt.target;
	target.parentNode.className = 'completed';
	setTimeout(() => {
		list.removeChild(target.parentNode);
	},2000);
}));
});

只执行一次例子:

const foo = once(() => {
	console.log('bar');
});
foo();foo();foo();

调用三次,但每次只执行一次。

高阶函数

以函数作为参数;

以函数作为返回值;

常用于作为函数装饰器。

在这里插入图片描述

等价范式:

function HOF0( fn) {
	return function( ...args ) {
	return fn.apply( this, args ) ;}
}
常用高阶函数

Once;Throttle;Debounce;Consumer/2;Iterative.

Throttle(节流函数)

没有必要将大量数据传到后台,每隔一段时间传一次。

对原始函数包装,在五百毫秒后才调用。

function throttle(fn, time = 500){
  let timer;
  return function(...args){
    if(timer == null){
      fn.apply(this,  args);
      timer = setTimeout(() => {
        timer = null;
      }, time)
    }
  }
}

btn.onclick = throttle(function(e){
  circle.innerHTML = parseInt(circle.innerHTML) + 1;
  circle.className = 'fade';
  setTimeout(() => circle.className = '', 250);
});

在这里插入图片描述

不管点击多快,五百毫秒记录一次。

在这里插入图片描述
在这里插入图片描述

Debounce(防抖函数)

停顿才调用方法。

每次动后clear掉timeout,停下来超时才调用。

在这里插入图片描述

var i = 0;
setInterval(function(){
  bird.className = "sprite" + 'bird' + ((i++) % 3);
}, 1000/10);

function debounce(fn, dur){
  dur = dur || 100;
  var timer;
  return function(){
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, arguments);
    }, dur);
  }
}

document.addEventListener('mousemove', debounce(function(evt){
  var x = evt.clientX,
      y = evt.clientY,
      x0 = bird.offsetLeft,
      y0 = bird.offsetTop;
  
  console.log(x, y);
  
  var a1 = new Animator(1000, function(ep){
    bird.style.top = y0 + ep * (y - y0) + 'px';
    bird.style.left = x0 + ep * (x - x0) + 'px';
  }, p => p * p);
  
  a1.animate();
}, 100));
Consumer

执行很多次很快的操作;

例子1:

每隔一秒计算并在控制台输出

在这里插入图片描述

例子2:

执行很多次很快的操作;将它们存在列表中,遍历列表,每隔一段时间执行一次,返回一个结果。
在这里插入图片描述

Iterative(批量操作元素)

批量操作元素,可迭代的操作

原本只操作一个参数,传入一个列表,可以批量操作。

在这里插入图片描述

iterative第一个参数,判断是否可迭代,如果可以,取出每一个参数,依次调用,把结果push到结果。

为什么要使用高阶函数?

高阶函数本身是纯函数。

纯函数(输入输出)比较好做单元测试,直接调用就可以,不需要构建运行时环境。

function add(x,y){	//Pure function
	return x + y;
}

不管什么时候调用add(1,2)结果为3,不改变,只要传参数确定,返回值就确定。

例子:(不是纯函数)
function setColors(els, color) {//impure function
	for(const el of els){
		el.style.color = color;
	}
}

const els = document.querySelectorAll( 'li:nth-child(2n+1)');
const els2 = document.querySelectorAll( 'li:nth-child(3n+1)');

setColors(els2,'blue' ) ;
setColors (els,'red' ) ;

结果:

在这里插入图片描述
在这里插入图片描述

调用次序不一样,结果不同。

改写

可以通过高阶函数减少非纯函数的数量,使系统的稳定性、可靠性增加。

改写:系统中只剩setColor一个非纯函数

在这里插入图片描述

测试:iterative

可以用add方法进行测试

const addList = iterative(add);
console.log(addList([1,2,3,4],1));

在这里插入图片描述

[1,2,3,4]每个数+1,得到[2,3,4,5],正确。

4. 编程范式

包括Javascript在内的很多现代脚本语言:混合的编程范式。(更加灵活)

命令式与声明式

命令式(过程式、面向对象)与声明式(逻辑式、函数式)

在这里插入图片描述

命令式与声明式没有优劣之分,都有适合的使用场景;

命令式:强调过程(怎么做);

声明式:强调是什么。

命令式(关心循环执行过程)

let list = [1,2,3,4];
let map1 = [];
for(let i = 0;i < list.length;i++){
	map1.push(list[i] * 2);
}

声明式

let list = [1,2,3,4];
const double = x => x * 2;
list.map(double);

命令式关心循环执行过程,而声明式不关心过程。

例子
命令式

在这里插入图片描述
点击按钮切换

在这里插入图片描述

声明式

在这里插入图片描述

很多状态

如果有很多个状态,命令式需要扩展if/else分支,用声明式写法toggle不用改动,只添加状态即可。

在这里插入图片描述

点击按钮切换
在这里插入图片描述

总结

过程抽象/HOF(高阶函数)/装饰器;

命令式/声明式

在这里插入图片描述

函数式编程

思考:

黑夜白天切换的一些细节

Once允许执行一次,扩展进行两次三次如何实现(一个参数limit限制执行n次)

实现批量操作元素的方法,除了Iterative其他的思路

练习,回顾写过的代码


总结

这里对文章进行总结:
例如:以上就是今天的笔记内容,本文仅仅简单从原则、风格上讨论JS的写法,更多有关前端的知识参考后续文章。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值