一些小功能总结

一、从字符串中提取数字

1、利用  parseFloat() 方法提取字符串中的数字。

parseFloat() 方法提取字符串中的数字,有很多的限制。它只能提取开头为数字的字符串中的数字,如果字符串的开头第一个字符为非数字,则会提取失败。

例:

console.log(parseFloat('1234feiniaomy.com')) //1234
console.log(parseFloat('123.4feiniaomy.com')) // 123.4
console.log(parseFloat('feiniaomy.com1234')); // NaN
console.log(parseFloat('m123.5')); //NaN

2、JS 使用正则提取字符串中的数字

例1:

可以利用正则的方法将字符串中非数字的字符给去掉,留下的就是数字。但要注意,如果是要想提取数字中有非整数的部份(带有小数点的数),则无法提取小数点。

var num = '1234feiniaomy.com'.replace(/[^\d]/g, ""); 
console.log(num); //1234
var num2 = '123.4feiniaomy.com'.replace(/[^\d]/g, ""); 
console.log(num2); //1234
var num3 = 'feiniaomy.com1234'.replace(/[^\d]/g, ""); 
console.log(num3); //1234
var num4 = 'm123.5'.replace(/[^\d]/g, ""); 
console.log(num4); //1235

例2:

通过上面的示例,我们可以修改一下正则表达式,并使用 match 方法来调用它。

var num = '1234feiniaomy.com'.match(/\d+(\.\d+)?/g); 
console.log(num); //['1234']
var num2 = '123.4feiniaomy.com'.match(/\d+(\.\d+)?/g); 
console.log(num2); //['123.4']
var num3 = 'feiniaomy.com1234'.match(/\d+(\.\d+)?/g); 
console.log(num3); //['1234']
var num4 = 'm123.5'.match(/\d+(\.\d+)?/g); 
console.log(num4); //['123.5]
var num4 = 'm123.55sd58sdf56sdf85sdf6e8f5sd6f'.match(/\d+(\.\d+)?/g); 
console.log(num4); //['123.55', '58', '56', '85', '6', '8', '5', '6']

二、js数组、数组嵌套根据某个字段排序(sort)

1、根据某个字段排序:

var arr = [
   {name:'张三',age:15},
   {name:'李四',age:18},
   {name:'王五',age:28}
];

function compare(property){
    return function(a,b){
        var value1 = a[property];
        var value2 = b[property];
        return value1 - value2;  //降序只需要  return value2- value1
    }
}
console.log(arr.sort(compare('age')))

2、根据某两个字段

var arr = [
    {name:'张三',age:15,num:13},
    {name:'李四',age:15,num:16},
    {name:'王五',age:28,num:18},
     {name:'木子李',age:18,num:18}
];

compare (property, p2) {
          return function (a, b) {
        var value1 = a[property];
        var value2 = b[property];
        if (value1 != value2) {
          return value1 - value2;
        } else {
          return a[p2] - b[p2];
        }
      }
    }
    console.log(arr.sort(compare('age','num')))
2

三、浮点数精度问题

首先在控制台分别打印 0.1 + 0.2, 和 0.1, 会发现如下图:

 或者有些面试会问到:

0.1 + 0.2 === 0.3 //false

像这种就是Javascript 数字精度丢失的问题,造成这个问题的原因:

javascript语言中,0.1 和 0.2 都是转化成二进制后再进行运算的

// 0.1 + 0.2转化成二进制后再进行运算
0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111

// 转成十进制 0.30000000000000004

解决方案:

  • 数据要展示时, 使用 toPrecision 凑整并 parseFloat 转成数字后再显示
function strip(num, precision = 12) {
  return +parseFloat(num.toPrecision(precision));
}
  •  运算类操作, 把小数转成整数后再运算。
function add(num1, num2) {
  const num1Digits = (num1.toString().split('.')[1] || '').length;
  const num2Digits = (num2.toString().split('.')[1] || '').length;
  const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits));
  return (num1 * baseNum + num2 * baseNum) / baseNum;
}

 四、防抖和节流

  • 函数防抖(debounce):触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。

// 防抖 立即执行版本
export function debounce(func, wait, immediate) {
    let timeout, args, context, timestamp, result

    const later = function() {
        // 据上一次触发时间间隔
        const last = +new Date() - timestamp

        // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
        if (last < wait && last > 0) {
            timeout = setTimeout(later, wait - last)
        } else {
            timeout = null
            // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
            if (!immediate) {
                result = func.apply(context, args)
                if (!timeout) context = args = null
            }
        }
    }

    return function(...args) {
        context = this
        timestamp = +new Date()
        const callNow = immediate && !timeout
        // 如果延时不存在,重新设定延时
        if (!timeout) timeout = setTimeout(later, wait)
        if (callNow) {
            result = func.apply(context, args)
            context = args = null
        }

        return result
    }
}
// 处理函数
function handle() {
    console.log('防抖:', Math.random());
}

// 应用: 滚动事件
window.addEventListener('scroll', debounce(handle, 500, false));
  • 函数节流(throttle):高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率。

//节流 定时器版本:
function throttle(fn,delay) {
    let canRun = true; // 通过闭包保存一个标记
    return function () {
         // 在函数开头判断标记是否为true,不为true则return
        if (!canRun) return;
         // 立即设置为false
        canRun = false;
        // 将外部传入的函数的执行放在setTimeout中
        setTimeout(() => { 
        // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。
        // 当定时器没有执行的时候标记永远是false,在开头被return掉
            fn.apply(this, arguments);
            canRun = true;
        }, delay);
    };
}
 
function sayHi(e) {
    console.log('节流:', e.target.innerWidth, e.target.innerHeight);
}

/*
应用
1.射击游戏的 mousedown/keydown 事件(单位时间只能发射一颗子弹)
2.搜索联想(keyup)
3.监听滚动事件判断是否到页面底部自动加载更多:
4.给 scroll 加了 debounce (防抖动函数)后,只有用户停止滚动后,才会判断是否到了页面底部;
如果是 throttle(节流函数) 的话,只要页面滚动就会间隔一段时间判断一次
*/

五、深拷贝

1. 满足一般使用场景,但无法实现对象中方法(function)的深拷贝 

JSON.parse(JSON.stringify(obj))

2. Object.assign(obj1, obj2)只有一级属性为深拷贝,二级属性后就是浅拷贝

let obj = {
	id: 1,
	name: '张三',
	age: 10,
}
let newObj = Object.assign({}, obj)

3.扩展运算符 只有一级属性为深拷贝,二级属性后就是浅拷贝 

// 对象
let pager = {
    size: 10,
    current: 1
}
 
let params = {
    name:'123',
    ...pager
};

// 数组
let arr1 = [{name: "小明", age: 18}, {name: "小芳", age: 20}];
let arr2 = [...arr1];

4. 递归

export function deepClone(source) {
  if (!source && typeof source !== 'object') {
    throw new Error('error arguments', 'deepClone')
  }
  const targetObj = source.constructor === Array ? [] : {}
  Object.keys(source).forEach(keys => {
    if (source[keys] && typeof source[keys] === 'object') {
      targetObj[keys] = deepClone(source[keys])
    } else {
      targetObj[keys] = source[keys]
    }
  })
  return targetObj
}

5. 数组使用数组方法进行深拷贝(concat、slice),只有一级属性为深拷贝,二级属性后就是浅拷贝,如[1,2,3,[1,2,3]] 

let arr1 = [1, 2, 3, 4]
let arr2 = arr1.concat()
let arr3 = arr1.slice(1)

6. 使用Vue提供的观察者模式实现数组深度复制

let arr1 = [{name: "小明", age: 18}, {name: "小芳", age: 20}];
let arr2 = Vue.util.extend([], arr1);

六、文字向右滚动(网站公告信息滚动显示), vue3.0

const state: State = reactive({
  message: '学习,创新,铸造优良产品',
  marqueeVar: null,
  speed1: 0,
  speed2: 0
});
// 消息公告
const moveMarquee = () => {
  // 获取内容区宽度
  let width1: any = (document.getElementById('marquee1')?.getBoundingClientRect().width)
  //width为第二个span标签尾部到盒子尾部的距离,500为外层盒子的宽度
  let width = width1 - 424
  let marquee1: any = document.getElementById("marquee1");
  state.speed1 = state.speed1 - 2;
  state.speed2 -= 1;
  // 如果speed2的尾部移到盒子的尾部并且speed1的移动距离不超过文字的距离
  if ( -state.speed2 >= width && -state.speed1 >= width1 ) {
    //让speed1重新在原位置移动
    state.speed1 = 424;
  }
  if (-state.speed2 >= width1) {
    //如果speed2的尾部移动到盒子的头部,让speed2跟在speed1之后移动
    state.speed2 = 0
  }
  if (marquee1){
    marquee1.style.transform = "translateX(" + state.speed1 / 20 + "rem)";
  }
 
}
const runMarquee = () => {
  let marquee1: any = document.getElementById("marquee1");
  state.marqueeVar = setInterval(() => moveMarquee(), 30);
  //监听鼠标移入,清空定时器
  marquee1.addEventListener('mouseenter', () => {
    clearInterval(state.marqueeVar);
    state.marqueeVar = null
  })
  //监听鼠标移开,重启定时器
  marquee1.addEventListener('mouseleave', () => {
      state.marqueeVar = setInterval(() => moveMarquee(), 30);
    }
  )
}
onMounted(() => {
  // 消息公告
  nextTick(()=>{
    runMarquee()
  })
})

七、文字持续向上滚动

网页上一块相同相似的内容的区域放在一个层里,然后将内容从下往上滚动,一般用于网页公告或新闻展示。

<div class="content_right_top_box_t" id="scroll-container">
    <p id="scroll-dom1">豫章故郡,洪都新府。星分翼轸,地接衡庐。襟三江而带五湖,
控蛮荆而引瓯越。物华天宝,龙光射牛斗之墟;人杰地灵,徐孺下陈蕃之榻。雄州雾列,俊采
星驰。台隍枕夷夏之交,宾主尽东南之美。都督阎公之雅望,棨戟遥临;宇文新州之懿范,襜
帷暂驻。十旬休假,胜友如云;千里逢迎,高朋满座。腾蛟起凤,孟学士之词宗;紫电青霜,
王将军之武库。家君作宰,路出名区;童子何知,躬逢胜饯。</p>
    <p id="scroll-dom2" style="margin-top: 5rem;"></p>
</div>
let MyMarTimer: NodeJS.Timer | undefined;
// 文字向上滚动
const scrollText = () => {
  let container: any = document.getElementById('scroll-container')
  let dom1: any = document.getElementById('scroll-dom1')
  let dom2: any = document.getElementById('scroll-dom2')
  dom2.innerHTML = dom1.innerHTML  //复制第一个容器内容到第二个容器,形成滚动的状态
  function Marquee() {
    // 增加容器的scrolltop 文字向上滚动
    // 当滚动到第二个容器文字时, 重新滚动scrollTop = 0, 无缝衔接
    console.log(container.scrollTop, dom1.offsetHeight + (rowH * 20 * 5))
    if (container.scrollTop >= dom1.offsetHeight + (rowH * 20 * 5)) {
      container.scrollTop = 0
    } else {
      container.scrollTop = container.scrollTop + 1 * rowH
    }
  }
  MyMarTimer = setInterval(Marquee, 80)
  container.onmouseover = () => {
    MyMarTimer && clearInterval(MyMarTimer) // 鼠标停止时暂停滚动
  }
  container.onmouseout = () => {
    MyMarTimer = setInterval(Marquee, 80) // 鼠标停止时暂停滚动
  }
}

八、3d-词云(直接上代码)

<template>
	<div class="wordCloud">
		<div id="cloud" ref="cloudRef">
			<span class="cloud-item" v-for="(item, index) in state.nameArr" :key="'ciyun_' + index">{{ item.name }}</span>
		</div>
	</div>
</template>
    
<script setup lang="ts">
import { onMounted, onBeforeUnmount, reactive, ref } from "vue";

declare const window: any;
const cloudRef = ref()
const rowH = window.rowH
let ciyunTimer: NodeJS.Timer | undefined;
const state: any = reactive({
	nameArr: [
		{
			name: '理想汽车',
			value: 1,
		},
		{
			name: '中兴汽车',
			value: 2,
		},
		{
			name: '蔚来汽车',
			value: 3,
		},
		{
			name: '上海大通',
			value: 4,
		},
		{
			name: '威马汽车',
			value: 5,
		},
		{
			name: '菲亚特',
			value: 6,
		},
		{
			name: '华人运通',
			value: 7,
		},
		{
			name: '马自达',
			value: 8,
		},
		{
			name: '北京现代',
			value: 9,
		},
		{
			name: '极氮汽车',
			value: 10,
		},
		{
			name: '长城汽车',
			value: 11,
		},
		{
			name: '吉利汽车',
			value: 12,
		},
		{
			name: '洛轲汽车',
			value: 13,
		},
		{
			name: '上海汽车',
			value: 14,
		},
		{
			name: '宝马',
			value: 15,
		},
		{
			name: '小鹏汽车',
			value: 16,
		},
		{
			name: '奔驰',
			value: 17,
		},
		{
			name: '大众奥迪',
			value: 18,
		},
		{
			name: '铃木',
			value: 19,
		},
		{
			name: '沃尔沃',
			value: 20,
		},
		{
			name: '福特',
			value: 21,
		},
		{
			name: '通用',
			value: 22,
		},
	],
	// -- 动态词云 --
	radius: 100 * rowH,
	dtr: Math.PI / 180,
	d: 300 * rowH,
	mcList: [],
	active: true,
	lasta: 1,
	lastb: 1,
	distr: true,
	tspeed: 1,
	size: 200,
	mouseX: 0,
	mouseY: 0,
	howElliptical: 1,
	aA: null,
	oDiv: null,
	sa: null,
	ca: null,
	sb: null,
	cb: null,
	sc: null,
	cc: null
})
onMounted(() => {
	init()
})
onBeforeUnmount(() => {
	ciyunTimer && clearInterval(ciyunTimer);
});
const init = () => {
	let oTag = null;
	state.oDiv = cloudRef.value;
	state.aA = document.querySelectorAll('.cloud-item')

	for (let i = 0; i < state.aA.length; i++) {
		oTag = {
			offsetWidth: null,
			offsetHeight: null,
			name: null
		};
		oTag.offsetWidth = state.aA[i].offsetWidth;
		oTag.offsetHeight = state.aA[i].offsetHeight;
		oTag.name = state.aA[i].innerHTML;

		state.mcList.push(oTag);
	}

	sineCosine(0, 0, 0);

	positionAll();

	state.oDiv.onmouseover = function () {
		state.active = false;
	};

	state.oDiv.onmouseout = function () {
		state.active = true;
	};

	autoplay()
	ciyunTimer = setInterval(update, 30);
}

const autoplay = () => {
	state.mouseX = state.oDiv.offsetLeft + state.oDiv.offsetWidth / 2;
	state.mouseY = state.oDiv.offsetTop + state.oDiv.offsetHeight / 2

	state.mouseX /= 20;
	state.mouseY /= 20;
}

const update = () => {
	let a: any;
	let b: any;

	if (state.active) {
		a = (-Math.min(Math.max(-state.mouseY, -state.size), state.size) / state.radius) * state.tspeed;
		b = (Math.min(Math.max(-state.mouseX, -state.size), state.size) / state.radius) * state.tspeed;
	} else {
		a = state.lasta * 0.98;
		b = state.lastb * 0.98;
	}

	state.lasta = a;
	state.lastb = b;

	if (Math.abs(a) <= 0.01 && Math.abs(b) <= 0.01) {
		return;
	}

	let c = 0;
	sineCosine(a, b, c);
	for (let j = 0; j < state.mcList.length; j++) {
		let rx1 = state.mcList[j].cx;
		let ry1 = state.mcList[j].cy * state.ca + state.mcList[j].cz * (-state.sa);
		let rz1 = state.mcList[j].cy * state.sa + state.mcList[j].cz * state.ca;

		let rx2 = rx1 * state.cb + rz1 * state.sb;
		let ry2 = ry1;
		let rz2 = rx1 * (-state.sb) + rz1 * state.cb;

		let rx3 = rx2 * state.cc + ry2 * (-state.sc);
		let ry3 = rx2 * state.sc + ry2 * state.cc;
		let rz3 = rz2;

		state.mcList[j].cx = rx3;
		state.mcList[j].cy = ry3;
		state.mcList[j].cz = rz3;

		let per = state.d / (state.d + rz3);

		state.mcList[j].x = (state.howElliptical * rx3 * per) - (state.howElliptical * 2);
		state.mcList[j].y = ry3 * per;
		state.mcList[j].scale = per;

		state.mcList[j].alpha = per;

		state.mcList[j].alpha = (state.mcList[j].alpha - 0.6) * (10 / 6);
	}

	doPosition();
	depthSort();
}
const depthSort = () => {
	let aTmp = [];

	for (let i = 0; i < state.aA.length; i++) {
		aTmp.push(state.aA[i]);
	}

	aTmp.sort((vItem1, vItem2) => {
		if (vItem1.cz > vItem2.cz) {
			return -1;
		} else if (vItem1.cz < vItem2.cz) {
			return 1;
		} else {
			return 0;
		}
	});

	for (let j = 0; j < aTmp.length; j++) {
		aTmp[j].style.zIndex = j;
	}
}
const positionAll = () => {
	let phi = 0;
	let theta = 0;
	let max = state.mcList.length;

	let aTmp = [];
	let oFragment = document.createDocumentFragment();
	//随机排序
	for (let i = 0; i < state.aA.length; i++) {
		aTmp.push(state.aA[i]);
	}

	aTmp.sort(() => {
		return Math.random() < 0.5 ? 1 : -1;
	});

	for (let l = 0; l < aTmp.length; l++) {
		oFragment.appendChild(aTmp[l]);
	}

	state.oDiv.appendChild(oFragment);

	for (let j = 1; j < max + 1; j++) {
		if (state.distr) {
			phi = Math.acos(-1 + (2 * j - 1) / max);
			theta = Math.sqrt(max * Math.PI) * phi;
		} else {
			phi = Math.random() * (Math.PI);
			theta = Math.random() * (2 * Math.PI);
		}
		//坐标变换
		state.mcList[j - 1].cx = state.radius * Math.cos(theta) * Math.sin(phi) * 1.5;
		state.mcList[j - 1].cy = state.radius * Math.sin(theta) * Math.sin(phi);
		state.mcList[j - 1].cz = state.radius * Math.cos(phi);

		// state.aA[j - 1].style.left = (state.mcList[j - 1].cx + state.oDiv.offsetWidth / 2 - state.mcList[j - 1].offsetWidth / 2) / 20 + 'rem';
		// state.aA[j - 1].style.top = (state.mcList[j - 1].cy + state.oDiv.offsetHeight / 2 - state.mcList[j - 1].offsetHeight / 2) / 20 +'rem';
		state.aA[j - 1].style.left = state.mcList[j - 1].cx + state.oDiv.offsetWidth / 2 - state.mcList[j - 1].offsetWidth / 2 + 'px';
		state.aA[j - 1].style.top = state.mcList[j - 1].cy + state.oDiv.offsetHeight / 2 - state.mcList[j - 1].offsetHeight / 2 + 'px';
	}
}
const doPosition = () => {
	let l = state.oDiv.offsetWidth / 2;
	let t = state.oDiv.offsetHeight / 2;
	for (let i = 0; i < state.mcList.length; i++) {
		// state.aA[i].style.left = (state.mcList[i].cx + l - state.mcList[i].offsetWidth / 2) / 20+'rem';
		// state.aA[i].style.top = (state.mcList[i].cy + t - state.mcList[i].offsetHeight / 2) / 20+'rem';
		state.aA[i].style.left = state.mcList[i].cx + l - state.mcList[i].offsetWidth / 2 + 'px';
		state.aA[i].style.top = state.mcList[i].cy + t - state.mcList[i].offsetHeight / 2 + 'px';

		state.aA[i].style.fontSize = (Math.ceil(12 * state.mcList[i].scale / 2) + 8) / 15 + 'rem';
		// state.aA[i].style.fontSize = Math.ceil(12 * state.mcList[i].scale / 2) + 8 + 'px';

		// state.aA[i].style.filter = "alpha(opacity=" + 100 * state.mcList[i].alpha + ")";
		state.aA[i].style.opacity = state.mcList[i].alpha;
	}
}
const sineCosine = (a: any, b: any, c: any) => {
	state.sa = Math.sin(a * state.dtr);
	state.ca = Math.cos(a * state.dtr);
	state.sb = Math.sin(b * state.dtr);
	state.cb = Math.cos(b * state.dtr);
	state.sc = Math.sin(c * state.dtr);
	state.cc = Math.cos(c * state.dtr);
}

</script>
	
<style lang="scss" scoped>
.wordCloud {
	width: 100%;
	height: 100%;

	#cloud {
		height: 100%;
		width: 100%;
		position: relative;
		cursor: pointer;

		span {
			position: absolute;
			top: 0;
			left: 0;
			color: #fff;
			font-weight: bold;
			font-size: 1rem;
			padding: .187rem .375rem;
			text-shadow: .05rem .05rem .1rem #1F4BEA;
			// #
		}
	}

	// #div1 a {

	// }

	// #div1 a:hover {
	//     border: 1px solid #eee;
	//     background: #000;
	//     border-radius: 5px;
	// }
}
</style>
    

持续更新中.......

  • 17
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值