如何写好JS

news2024/11/26 4:31:56

本节课从实践维度解读在实际编码过程中何种类型的 JavaScript 代码称之为“好代码”,并从 JS 出发,总结其他语言编码可遵循的共性原则,由浅入深,其三大原则是:

  • 各司其职——html,css,js分离
  • 组件封装——组件具备准确性,拓展性,复用性
  • 过程抽象——函数式

各司其职

例子:使用 js 实现的切换背景颜色的例子

//html
 <header>
        <button id="modeBtn">🌞</button> 
        <h1>深浅色模式切换</h1>
</header>
 
//css
#modeBtn {
      font-size: 2rem;
      float: right;
      border: none;
      background: transparent;
}

//js
window.onload=function(){
    document.getElementById("file-btn") 
    const btn = document.getElementById('modeBtn');
    btn.addEventListener('click', (e) => {
      const body = document.body; //获取页面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 来控制 css 属性,很难直观的理解需求的原始含义

修改方案:如下,将直接修改属性值编程修改class的名称

//html
 <header>
        <button id="modeBtn"></button> 
        <h1>深浅色模式切换</h1>
 </header>
 
 //css
    body.night {
      background-color: black;
      color: white;
      transition: all 1s; //美观上做了一些调整,切换时有1秒的延时
    }

    #modeBtn::after { //各司其职,让css来实现图标的切换
      content: '🌞';
    }

    body.night #modeBtn::after {
      content: '🌜';
    }
    
//js
window.onload=function(){
   const btn = document.getElementById('modeBtn');
        btn.addEventListener('click', (e) => {
        const body = document.body;
        if (body.className !== 'night') { //通过className的'night'来显示深色模式,判断上更直观
            body.className = 'night';
        } else {
            body.className = '';
        }
    });
}

继续改进:使用纯粹的 css 来控制,使用 checkbox 来控制切换,因为样式就是 css 的事情,所以尽量使用纯 css 实现样式的事情,使用伪类选择器和隐藏 checkbox 的方式来实现

//html
<input id="modeCheckBox" type="checkbox"> 
  <div class="content">
    <header>
      <label id="modeBtn" for="modeCheckBox"></label>
      <h1>深浅色模式切换</h1>
    </header>
  </div>
  
//css
    .content {
      padding: 10px;
      transition: background-color 1s, color 1s;
    }

    #modeCheckBox {
      display: none; //将大盒子外面的checkbox隐藏起来
    }

    #modeCheckBox:checked+.content { //通过 checkbox 的伪类选择器checked,点击checkbox就会触发这个伪类
      background-color: black;
      color: white;
      transition: all 1s;
    }

    #modeBtn {
      font-size: 2rem;
      float: right;
    }

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

    #modeCheckBox:checked+.content #modeBtn::after {
      content: '🌜';
    }

改进要点:

  1. html/css/js各司其职
  2. 避免不必要的 js 操作样式
  3. 追求纯交互 0 js的样式操作方案
  4. 用不同的 class 来表示不同的状态 (不强求)

组件封装

例子:实现一个轮播图,我们使用 li 列表来实现轮播,之后将列表的切换逻辑封装成一个class

//html
<div id="my-slider" class="slider-list">
      <ul> 
        <li class="slider-list__item--selected"> 
        //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
#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;
  }

//js
class Slider{
    constructor(id){
      this.container = document.getElementById(id);
      this.items = this.container
      .querySelectorAll('.slider-list__item, .slider-list__item--selected');
    }
    getSelectedItem(){ 
    //得到当前轮播图正在显示的li
      const selected = this.container
        .querySelector('.slider-list__item--selected');
      return selected
    }
    getSelectedItemIndex(){ 
    //获取当前轮播图的显示li的下标
      return Array.from(this.items).indexOf(this.getSelectedItem());
    }
    slideTo(idx){ 
    //轮播到指定的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);
  
//这样就可以通过手动调用API来使用轮播图了

const slider = new Slider('my-slider');
slider.slideTo(1);
slider.slideTo(2);
slider.slideNext();
slider.slidePrevious();

//还可以直接定义一个定时器,让他自动播放

const slider = new Slider('my-slider');
setInterval(() => { 
    slider.slideNext(); 
}, 1000);

实现的效果如下:

请添加图片描述

之后我们需要为我们的添加左右切换按钮和下方的状态按钮,使用自定义事件来解耦合

//html
  <a class="slide-list__next"></a>
  <a class="slide-list__previous"></a>
  <div class="slide-list__control">
    <span class="slide-list__control-buttons--selected"></span>
    <span class="slide-list__control-buttons"></span>
    <span class="slide-list__control-buttons"></span>
    <span class="slide-list__control-buttons"></span>
  </div>

//css
.slide-list__control-buttons, 
.slide-list__control-buttons--selected
{ 
  display: inline-block; 
  width: 15px; 
  height: 15px; 
  border-radius: 50%; 
  margin: 0 5px; 
  background-color: white; 
  cursor: pointer;  
}
.slide-list__control-buttons--selected 
{ 
  background-color: red; 
}

//js
      // 鼠标经过某个小圆点,就将此圆点对应的图片显示出来,并且停止循环轮播
      controller.addEventListener('mouseover', evt=>{
        const idx = Array.from(buttons).indexOf(evt.target);
        if(idx >= 0){
          this.slideTo(idx);
          this.stop();
        }
      });
      
      // 鼠标移开小圆点,就继续开始循环轮播
      controller.addEventListener('mouseout', evt=>{
        this.start();
      });
      
      // 注册slide事件,将选中的图片和小圆点设置为selected状态
      this.container.addEventListener('slide', evt => {
        const idx = evt.detail.index
        const selected = controller.querySelector('.slide-list__control-buttons--selected');
        if(selected) selected.className = 'slide-list__control-buttons';
        buttons[idx].className = 'slide-list__control-buttons--selected';
      })
    
    // 点击左边小箭头,翻到前一页
    const previous = this.container.querySelector('.slide-list__previous');
    if(previous){
      previous.addEventListener('click', evt => {
        this.stop();
        this.slidePrevious();
        this.start();
        evt.preventDefault();
      });
    }
    // 点击右边小箭头,翻到后一页
    const next = this.container.querySelector('.slide-list__next');
    if(next){
      next.addEventListener('click', evt => {
        this.stop();
        this.slideNext();
        this.start();
        evt.preventDefault();
      });
    }

实现效果如下:

请添加图片描述

上文的例子存在的问题是:它不够灵活,控制点和组件本身是绑定在一起的,如果你需要修改一个点,比如把需要去掉左右切换的箭头按钮,需要修改很多东西

改进方案是:将组件插件化——将控制元素抽取成插件,插件与组件之间通过依赖注入的方式建立联系

 function pluginController(slider){
     const controller = slider.container.querySelector('.slide-list__control');
     if(controller){
       const buttons = controller.querySelectorAll('.slide-list__control-buttons, .slide-list__control-buttons--selected');
       controller.addEventListener('mouseover', evt=>{
         const idx = Array.from(buttons).indexOf(evt.target);
         if(idx >= 0){
           slider.slideTo(idx);
           slider.stop();
         }
       });
       controller.addEventListener('mouseout', evt=>{
         slider.start();
       });
       slider.addEventListener('slide', evt => {
         const idx = evt.detail.index
         const selected = controller.querySelector('.slide-list__control-buttons--selected');
         if(selected) selected.className = 'slide-list__control-buttons';
         buttons[idx].className = 'slide-list__control-buttons--selected';
       });
     }  
   }
   function pluginPrevious(slider){
     const previous = slider.container.querySelector('.slide-list__previous');
     if(previous){
       previous.addEventListener('click', evt => {
         slider.stop();
         slider.slidePrevious();
         slider.start();
         evt.preventDefault();
       });
     }  
   }
   function pluginNext(slider){
     const next = slider.container.querySelector('.slide-list__next');
     if(next){
       next.addEventListener('click', evt => {
         slider.stop();
         slider.slideNext();
         slider.start();
         evt.preventDefault();
       });
     }  
   }

这样的好处是,我就算新增加一个插件,只要把组件注册到代码里就行了。

缺点是:如果我们不要某个插件,还要把对应的代码删掉

改进:HTML模板化,我们用 js 去写一个组件的模板,我们只需要传递对应的数据就可以生成对应的html,这样改进以后 html 仅需要一行代码就可以生成我们的组件

//html
<div id="my-slider"> </div>
//js
class Slider{
     constructor(id, opts = {images:[], cycle: 3000}){
       this.container = document.getElementById(id);
       this.options = opts;
       this.container.innerHTML = this.render();
       this.items = this.container.querySelectorAll('.slider-list__item, .slider-list__item--selected');
       this.cycle = opts.cycle || 3000;
       this.slideTo(0);
     }
     render(){
       const images = this.options.images;
       const content = images.map(image => `
         <li class="slider-list__item">
           <img src="${image}">
         </li>    
       `.trim());
       
       return `<ul>${content.join('')}</ul>`;
     }
     ...
   }

最后一次改进:抽象,我们可以将每个部分公共的部分抽取出来,编写一个抽象的顶层,让其他组件继承它,然后重载重写其中的核心方法来实现自己的功能

class Component{
     constructor(id, opts = {name, data:[]}){
         this.container = document.getElementById(id);
         this.options = opts;
         this.container.innerHTML = this.render(opts.data);
     }
     registerPlugins(...plugins){
         plugins.forEach(plugin => {
             const pluginContainer = document.createElement('div');
             pluginContainer.className = `.${name}__plugin`;
             pluginContainer.innerHTML = plugin.render(this.options.data);
             this.container.appendChild(pluginContainer);
             plugin.action(this);
         });
     }
     render(data) {
         /* abstract */
         return ''
     }
 }

请添加图片描述

这样做的好处是:即插即用,形成了一个抽象的通用的模型

总结:我们实现一个组件的步骤是:结构设计、展现效果、行为设计

三次重构:1.插件化 2. 模板化 3. 抽象化

注意:这样做并不改变各司其职的原则,虽然最后一次重构后将 html 生成放在了 js 中,但是本质上 html 还是做着生成页面元素的工作,js 还是做着控制交互的工作,并没有改变职能。

过程抽象

过程抽象是,用来控制局部细节的方法,是函数式编程的基础,典型例子是 react hooks,

还是通过应该例子来讲说:有一个列表,生成了一系列的 buttons ,点击按钮可以将它从列表中移除,但是他有一个2秒的动画,但是如果我们点击多次一个按钮,可能就会产生问题,所以我们希望我们的请求只执行一次,我们可以封装一个只执行一次的函数:

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

这个函数是一个高阶函数,它传入一个函数,返回一个函数,如果函数第一次调用,我们用当前的函数和参数来调用它,并且返回结果,再将函数的值设置为 null,当我们再次调用的时候函数已经不存在了,所以可以控制我们的函数只调用一次。

常用的高阶函数有:

节流函数:控制事件的触发频率,如果按钮的响应频率等

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

防抖函数:比如我们要实现一个自动保存的功能,但是每次变化都保存会有一个很大的负担,我们需要等内容不变化才保存:

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

为什么要使用高阶函数?纯函数是结果可以预期的函数,如果我们输入 a 返回一定是 b ,并且没用副作用,即不会改变外部状态,如果给他们写测试会非常麻烦,但是高阶函数一般是纯函数,使用高阶函数可以提高代码的可维护性。

  • 编程范式

命令式:让代码去做一件事,并告诉他怎么去做。

let array = [1,2,3];
for(i=0;i<array.length;i++)
  console.log(array[i]);

声明式:让代码去做一类事(做什么),具体的方法(如何去做)被抽象到高阶函数中。

let array = [1,2,3];
array.forEach((e)=>console.log(e);)

声明式更加简介,使用纯函数不会改变外部环境也不依赖外部变量

代码优化

代码优化方向有很多:

  • 风格
  • 效率
  • 约定
  • 使用场景
  • 设计

以下是几个例子来说明不同方向的优化:

  • Leftpad 的优化

还是用一个例子来说,如下是 npm 里的一个模块 Leftpad :

function leftpad(str, len, ch) {
  str = String(str);
  var i = -1;
  if(!ch && ch !== 0) ch = ' ';
  while(++i < len){
      str = ch + str;
  }
  return str;
} 

它效率低, 时间复杂度O(n),但通用,使用场景性能优化空间有限,我们可以使用如下的方式来优化它,它的时间复杂度就到了 O(logn):

function leftpad(str, len, ch) {
  str = "" + str;
  const padLen = len - str.length;
  if(padLen <= 0) {
    return str;
  }
  return (""+ch).repeat(padLen)+str;
}
function repeat(string,count){
  var n=count;
  if(n<0||n==Infinity){
    throw RangeError("string.ptototype.repeat argument must be greater than or equal to 0 and Infinity")
  }
  var result='';
  while(n){
    if(n%2==1){
      result+=string;
    }
    if(n>1){
      string+=string
    }
    n>>=1;
  }
  return result;
}

但是实际上,在这样一个小部件里进行优化,对于这个函数的使用场景来说,只有在字符串特别大的时候还有明显效果,意义不大。

  • 红绿灯的例子

我们需要将红绿灯每隔一秒钟进行切换,但是实现出来的代码十分臃肿:

(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);
})();

我们可以将数据的切换过程进行抽象,然后实现一个列表保存我们要切换的过程,将数据传入函数即可完成切换:

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);

我们也可以使用过程抽象实现这个功能,我们将所有的功能进行抽象,然后将要执行的内容传入函数,它写的代码很多,但是抽取了通用的方法,其中的方法可以使用在其他地方,具有复用性和通用性:

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();
  }
}());

如上的内容可以通过异步和函数式编程的方法简化:

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 的幂
  1. 最简单的一种方式就是不断的除以4,如果最后等于1则是4的幂,否则则不是4的幂,原理比较简,但是性能不优秀
function isPowerOfFour(int num) {
     //负数不可能是4的幂
     if (num <= 0)
         return false;
     //1是4的0次幂
     if (num == 1)
         return true;
     //如果不能够被4整除,肯定不是4的幂
     if (num % 4 != 0)
        return false;
    //如果能被4整除,除以4然后递归调用
    return isPowerOfFour(num / 4);
}
  1. 二进制位运算实现,在32位的二进制中,只要有一个位置是1(不能是符号位),其他位置都是0,那么这个数就是2的幂,如果一个数是2的幂,并且二进制从右边数奇数位是1的一定是4的幂
return num > 0 && (num & (num - 1)) == 0 && (num & 0x55555555) == num;
  1. 基于我们是 js 二进制转字符串,正则判断
  2. 最后我们可以使用公式来计算,我们来观察一下4的幂次方的一些特点,4的幂次方不好观察,我们来研究一下4的幂次方减1,研究这个特点之前一定要明白这样一条定律:任何连续的n个自然数的乘积一定能被n整除。如果一个数是2的幂,并且减1还能被3整除,那么这个数一定是4的幂:
 return num > 0 && (num & (num - 1)) == 0 && (num - 1) % 3 == 0;

这个例子的道理是:我们可以针对不同语言的 api 选择不同的优化方式

  • 洗牌算法

有几张牌张牌,用js来进行乱序排列,要保持公平性

  1. 这里用sort方法,用随机数控制是不是交换,但是实际上结果是:越小的数值排布在前面的概率越大,因为这个算法,越在前面的数,交换到最后的概率是越低的,它每次交换到下一个位置的概率都是递减的,所以算法是不科学的
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));
  1. 这里的思路是抽取随机一张牌,放在最后一张牌的后面,在剩下的牌里进行抽取,继续放到最后一张牌的后面。这样每张牌抽到每个位置的概率是一样的:
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));
  1. 对于上述的方法,我们可以用生成器来做,每次抽取其中一张牌,把抽出的牌通过 yield 方法进行抽出去,做成一个可迭代对象,最后通过 … 展开操作符进行展开。
1const 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]);

这里例子是道理是:你需要先保证代码的正确性,再进行代码的优化

  • 分红包的算法

给定红包金额和红包数量,公平随机给红包分配金额数

  1. 切西瓜:每次把金额分成两部分,挑选出大的切两份,在所有的里面找最大的,以此类推,每次分最大的红包,时间复杂度是 O( nm )

    这样的缺点是:这样分出来的都比较均匀,不够刺激

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;
}
generateBtn.onclick = function(){
  let amount = Math.round(parseFloat(amountEl.value) * 100);
  let count = parseInt(countEl.value);
  
  let output = [];
  
  if(isNaN(amount) || isNaN(count) 
     || amount <= 0 || count <= 0){
    output.push('输入格式不正确!');
  }else if(amount < count){
    output.push('钱不够分')
  }else{
    output.push(...generate(amount, count));
    output = output.map(m => (m / 100).toFixed(2));
  }
  resultEl.innerHTML = '<li>' + 
                        output.join('</li><li>') +
                       '</li>';
}
  1. 切竹子:假设 100 元,从1号到9999号(以分为单位),取10个位置切,更容易随机到更大的数

    利用生成器:

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];
  }
}

function generate(amount, count){
  if(count <= 1) return [amount];
  const cards = Array(amount - 1).fill(0).map((_, i) => i + 1);
  const pick = draw(cards);
  const result = [];
  for(let i = 0; i < count; i++) {
    result.push(pick.next().value);
  }
  result.sort((a, b) => a - b);
  for(let i = count - 1; i > 0; i--) {
    result[i] = result[i] - result[i - 1];
  }
  return result;
}

总结

  1. 写代码的时候要根据使用场景来评估一个代码的好坏
  2. 不论是前端后端客户端,算法都是需要的,需要自己的算法意识和数学基础,来提高自己算法的效率,用对方法一个复杂的问题会变的很简单

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/167392.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通达信接口QQ是什么端口?

可以将通达信接口QQ视为使用通达信市场软件作为数据库&#xff0c;然后将信息整合为策略的前提&#xff0c;所有行为都是自动化的。通达信接口的优势在于交易策略是事先制定的。是否基于市场波动&#xff0c;不受个人情绪的影响&#xff0c;可以大大降低个人原因造成的错误。 …

[ACTF2020 新生赛]BackupFile

目录 信息收集 思路 构造payload 知识补充 信息收集 从题目来看应该是让扫描备份文件(backupfile) 进入页面就一句话 Try to find out source file! 先用dirbuster模糊扫描一下目录 常见的如下 index.phps index.php.swp index.php.swo index.php.php~ index.php.bak ind…

有哪些数据恢复软件?13个好用的数据恢复工具分享

个人编辑开发了此资源&#xff0c;以帮助购买者寻找最好的免费数据恢复软件来满足其组织的需求。选择合适的供应商和工具可能是一个复杂的过程&#xff0c;需要深入研究&#xff0c;而且往往不仅仅取决于工具及其技术能力。为了让您的搜索更轻松一些&#xff0c;我们在一个地方…

【C++】stack和queue的使用

文章目录Stackstack容器的定义方式:接口函数queuequeue容器的定义方式接口函数栈OJ题目最小栈栈的压入,弹出序列逆波兰表达式求值(后缀表达式)中缀表达式->后缀表达式用两个栈实现队列队列OJ题用队列实现栈使用两个队列实现栈使用一个队列实现栈二叉树的层序遍历I二叉树的层…

k8s之挂载本地磁盘到POD中

写在前面 本文一起看下如何挂载本地的磁盘到POD中。 1&#xff1a;都需要哪些API对象 现实世界中的存储设备有非常非常多的种类&#xff0c;如本文要分析的计算机磁盘&#xff0c;还包括NFS(一种网络磁盘存储协议)&#xff0c;Ceph&#xff08;一种分布式的文件存储系统&…

Web测试的各个测试点

1.什么是Web测试&#xff1f; Web测试测试Web或Web应用程序的潜在错误。它是在上线前对基于网络的应用程序进行完整的测试。 UI测试 功能测试 数据库测试 性能测试 兼容性测试 安全测试 自动化测试 2.WEB测试主要测试场景 1.UI测试 界面是否美观&#xff0c;风格、字体、…

【青训营】Go的并发编程

本文章整理自——字节跳动青年训练营&#xff08;第五届&#xff09;后端组 1.线程和协程 操作系统中有三个重要的概念&#xff0c;分别是进程、线程和协程。其中进程和线程的区别请移步操作系统专栏&#xff0c;现在主要叙述线程和协程的区别。 简单来说&#xff0c;协程又称…

看我们网络故障分析系统如何发现系统500报错

背景 汽车配件电子图册系统是某汽车集团的重要业务系统。业务部门反映&#xff0c;汽车配件电子图册调用图纸时&#xff0c;出现访问慢现象。 汽车集团总部已部署NetInside流量分析系统&#xff0c;使用流量分析系统提供实时和历史原始流量。本次分析重点针对汽车配件电子图册…

Python学习笔记-网络爬虫基础

一、网络爬虫概述网络爬虫概述网络爬虫又称网络蜘蛛、网络机器人&#xff0c;在某社区中经常被称为网页追逐者。网络爬虫可以按照指定规则自动浏览或抓取网络中的信息&#xff0c;python可以很轻松的编写爬虫程序或脚本。网络爬虫基本工作流程&#xff1a;网络爬虫的常用技术2.…

【QT5 实现“上图下文”,带图标的按键样式-toolbutton-学习笔记-记录-基础样例】实现方式之一

【QT5 实现“上图下文”&#xff0c;带图标的按键样式-toolbutton-学习笔记-记录-基础样例】1、前言2、实验环境3、效果展示4、实验步骤第一步&#xff1a;新建工程-并运行。第二步&#xff1a;上网找图标文件第四步&#xff1a;&#xff08;非必须&#xff09;为了对比图标不同…

23种设计模式(三)——观察者模式【组件协作】

文章目录意图什么时候使用观察者使用观察者模式也有两个重点问题要解决&#xff1a;1&#xff09;广播链的问题2&#xff09;异步处理问题真实世界类比观察者模式的实现观察者模式的优缺点亦称&#xff1a;事件订阅者、监听者、Event-Subscriber、Listener、Observer 意图 在许…

mybatis之动态SQL测试环境的搭建以及if语句的使用

动态SQL&#xff1a; 动态 SQL 是 MyBatis 的强大特性之一&#xff0c;如果你使用过 JDBC 或其它类似的框架&#xff0c;你应该能理解根据不同条件拼接 SQL 语句有多痛苦&#xff0c;例如拼接时要确保不能忘记添加必要的空格&#xff0c;还要注意去掉列表最后一个列名的逗号&a…

Vue CLI

介绍 Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统&#xff0c;提供&#xff1a; 通过 vue/cli 实现的交互式的项目脚手架。 通过 vue/cli vue/cli-service-global实现的零配置原型开发。 一个运行时依赖 (vue/cli-service)&#xff0c;该依赖&#xff1a; 可升级&a…

腾讯安全发布《2022年DDoS攻击威胁报告》:DDoS威胁4年持续增长

随着全球数字化蓬勃发展&#xff0c;互联网的应用范围不断扩大&#xff0c;并逐渐普及到各行各业的生产、管理、运营等方面&#xff0c;网络设备可用带宽伴随应用需求的增加而增加&#xff0c;方便了企业业务开展的同时也扩大了安全威胁面&#xff0c;引来黑产的觊觎。DDoS攻击…

Java使用流去除集合中某个字段为空的对象

文章目录0 写在前面1 情景复刻2 解决方案3 写在最后0 写在前面 最近写了一些业务逻辑&#xff0c;调试的时候总会报空指针异常。 Java中空指针异常是危险恐怖分子&#xff0c;最好不要碰见他。所以有些时候&#xff0c;处理集合中的数据时&#xff0c;特定情况下需要略过一些数…

十五天学会Autodesk Inventor,看完这一系列就够了(二),软件界面

众所周知&#xff0c;Autocad是一款用于二维绘图、详细绘制、设计文档和基本三维设计&#xff0c;现已经成为国际上广为流行的绘图工具。Autodesk Inventor软件也是美国AutoDesk公司推出的三维可视化实体模拟软件。因为很多人都熟悉Autocad&#xff0c;所以再学习Inventor&…

RK3568工业级核心板高温运行测试

Rockchip RK3568 是一款通用型MPU&#xff0c;产品集成GPU、NPU&#xff0c;支持4K、HDMI、LVDS、MIPI、PCIe3.0、USB3.0、千兆以太网、CAN-BUS、UART等丰富外设接口。 RK3568的高温工作情况如何呢&#xff1f;本文将基于万象奥科HD-RK3568-CORE 系列核心板做详细高温测试&…

接口幂等性设计

幂等性: 对于同一个操作发起一次请求或者多次请求&#xff0c;得到的结果都是一样的&#xff0c;不会因为请求多次而出现异常现象。 场景: 用户多次请求&#xff0c;比如重复点击页面上的按钮网络异常&#xff0c;右移网络原因导致在一定时间内未返回调用成功的信息&#xff…

《JavaScript 核心原理解析》学习笔记 Day 1 delete 引用与值

关于引用与值&#xff1a;在 javaScript 中一个表达式的值或者说结果&#xff0c;可能是引用 / 值。所以 x x &#xff0c;是将右侧表达式x的值赋值给左侧表达式x所指的引用。注意此处的引用并非为到具体内存地址的指向&#xff0c;而是指表达式与其值的一种关联。 这一关联即…

Android 音视频——直播推流技术指南

一、推流架构 推流SDK客户端的模块主要有三个&#xff0c;推流采集端、队列控制模块、推流端。其中每个模块的主要流程如下&#xff0c;本文的主要目的就是拆分推流流程&#xff0c; 1.1 采集端 视频采集&#xff1a;通过Camera采集视频。 音频采集&#xff1a;通过麦克风采…