JS常用snippet
跨域处理
// 第一步创建script标签
var scriptStart = document.createElement('script');
// 第二步创建指定script标签的src路径
scriptStart.src = 'http://www.baidu.com';
// 将script标签插入到页面中
document.body.insertBefore(scriptStart, document.body.firstChild);
px 和 rem 的转换
<script>
(function () {
document.addEventListener('DOMContentLoaded', function () {
var html = document.documentElement;
var windowWidth = html.clientWidth;
if(windowWidth > 640) windowWidth = 640;
if(windowWidth < 320) windowWidth = 320;
html.style.fontSize = windowWidth / 7.5 + 'px';
}, false);
})();
</script>
检测屏幕宽度自动刷新
function hengshuping() {
window.location.reload();
}
window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", hengshuping, false);
手机浏览器控制台
//手机浏览器控制台
(function () {
var script = document.createElement('script');
script.src="http://eruda.liriliri.io/eruda.min.js";
document.body.appendChild(script);
script.onload = function () { eruda.init()}
})();
bind 兼容
if (!Function.prototype.bind) {
Function.prototype.bind = function() {
var self = this, // 保存原函数
context = [].shift.call(arguments), // 需要绑定的this上下文
args = [].slice.call(arguments); // 剩余的参数转成数组
return function() { // 返回一个新函数
// 执行新函数时,将传入的上下文context作为新函数的this
// 并且组合两次分别传入的参数,作为新函数的参数
return self.apply(context, [].concat.call(args, [].slice.call(arguments)));
}
};
}
apply/call
apply/call都能改变函数运行时的上下文(context),即改变this的指向(换句话说一个对象可以调用其他对象的方法),如果call、apply第一个参数传递null或者undefined,那么函数中this不改变。
- 用法
apply相较之call,其第二个参数是一个数组
fn.call(obj,arg1,arg2,[...arg])
fn.apply(obj,[arg1,arg2,...arg]) - 简单case
const foo = {
value: 2,
};
function bar(num) {
console.log(this.value + num);
}
// => NaN
bar(2)
// => 4
bar.bind(foo, 2);
// => 4
bar.bind(foo)(2);
// => 4
bar.call(foo, 2);
// => 4
bar.apply(foo, [2]);
- 复杂case
function Stu(){} // 学生类
Stu.prototype = {
greeting: "nice to meet you",
say: function(a,b){
console.log(a+b);
console.log(this) //这里的this指向的是 new Stu()
console.log("hello,"+this.greeting);
}
}
var s = new Stu(); // {}
s.say(1,2); // 3 hello, nice to meet you
var s1 = {
greeting: "nice to meet you,too"
}
s.say.apply(s1,[2,3]); //5 hello,nice to meet you,too 此时this指向s1
s.say.call(s1,4,27); // 31 hello,nice to meet you,too
bind
和call/apply类似,bind返回的是一个方法
取整
~~ 双非按位取反运算符,正数,它向下取整;负数,向上取整;非数字取值为0
~~null; // 0
~~undefined; // 0
~~2.9; // 2
~~-2.9; // -2
~~[]; // 0
~~{}; // 0
~~false; // 0
~~true; // 1
数字前补0
// '009'
Array(3).join('0')+9
数组常用方法
Array.prototype.forEach
- 用法
arr.forEach((currentValue,index,array[,thisArg])=>{ //your iterator }) - 说明
currentValue表示当前元素,index表示当前元素的索引,array表示原数组(被调用数组),thisArg执行callback的对象,可选参数,即this指向的对象,返回值是undefined
案例:
[2, 5, ,9].forEach((currentValue,index,array)=>{
console.log("a["+index+"]="+currentValue)
});
// output a[0]=2,a[1]=5,a[3]=9 a[2]没有值,忽略
Array.prototype.map
- 用法
var new_array = arr.map(
(currentValue,index,array[,thisArg])=>{
//your iterator
}
)
- 说明
callback的参数说明同forEach,但返回值是一个新数组,其每个元素都是回调函数的结果;
案例:
let numbers=[1, 5, 10, 15];
let roots = numbers.map((x) => {
return x * 2;
});
// output roots=[2,10,20,30]
// numbers is still [1, 5, 10, 15]
Array.prototype.filter
- 用法
var new_array = arr.filter(
(currentValue,index,array[,thisArg])=>{
//your iterator
}
)
- 说明
callback的参数说明同forEach,callback的返回值的boolean,若为true,则保留该元素,否则,不保留,返回的值是一个新的的数组,其每个元素都是原数组中通过测试的元素
案例:
// => filtered [12,130.44]
let filtered = [12,5,8,130,44].filter((value)=>{
return value>=10;
});
Array.prototype.reduce
- 用法
arr.reduce((accumulator,currentValue[, index[, array]])=>{
//your iterator
}[,initialValue]
)
- 说明
accumulator表示上一次调用回掉的值,或者是提供的初始值(initialValue),currentValue数组中正在处理的元素,index表示正常处理元素的索引,若提供initialValue,则从0开始,否则,从1开始,array表示原数组,initialValue是可选值,该值用于第一次调用callback的第一个参数,返回值是函数累计处理的结果。 - 注意
回调第一次执行时,accumulator,currentValue的取值有两种情况:调用reduce时提供initialValue,accumulator的取值为initialValue,currentValue取数组中的第一个(即arr[0]);没有提供initialValue,则accumulator取数组中的第一个值arr[0],currentValue取数组中的第二个值(即arr[1]) - 案例
let sum = [0, 1, 2, 3].reduce((acc, val) => {
return acc + val;
}, 0);
// => 6
console.log(sum);
// 执行第一次回调时initiaValue = 0,accumulator =0,currentValue=0
Array.prototype.reduceRight
- 说明
该方法类似reduce,只不过取值是从右边开始
Array.prototype.find
- 说明
该方法返回的是第一个通过测试的元素 - Case
// => 5
[1, 2, 5].find((v) => v > 3)
树形结构转换
function formatTree(arr) {
const map = {};
const tree = [];
arr.forEach(item => {
map[item.departmentId] = item;
});
arr.forEach(item => {
const parent = map[item.parentDepartmentId];
if (parent) {
(parent.children || (parent.children = [])).push(item);
} else {
tree.push(item);
}
});
return tree;
}
function treeData(data){
const cloneData = JSON.parse(JSON.stringify(data))
return cloneData.filter(father=>{
const branchArr = cloneData.filter(child => father.id == child.pid);
if(){
Object.assign(father, { children: branchArr });
}
return father.pid == 0 ;
})
}
图片压缩
function _imgCompress(image, type) {
// 默认按比例压缩
const imageWidth = image.width;
const imageHeight = image.height;
const scaleRatio = imageWidth / imageHeight;
const quality = 0.7; // 默认图片质量为0.7
// 修改图片宽度,改变尺寸
const cvsWidth = Math.min(1000, imageWidth)
const cvsHeight = cvsWidth / scaleRatio;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.setAttribute('width', cvsWidth);
canvas.setAttribute('height', cvsHeight);
ctx.drawImage(image, 0, 0, cvsWidth, cvsHeight);
const base64 = canvas.toDataURL(image, quality);
}
base64转换成blob
function _convertBase64UrlToBlob(urlData) {
const arr = urlData.split(','), mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
双向绑定实现
- Proxy
import "./global";
const app = document.getElementById("app");
app.innerHTML = `
<p>简单mvvm的实现</p>
<input placeholder='请输入' id='txtInput'/>
<p>输入的是:<span id='txtResult'></span></p>
`;
const txtInput = document.getElementById("txtInput") as HTMLInputElement;
const txtResult = document.getElementById("txtResult") as HTMLSpanElement;
const data = {
txt: ""
};
// proxy实现数据劫持
const handler: ProxyHandler<{
txt: string;
}> = {
get(target, p) {
return Reflect.get(target, p);
},
set(target, p, value) {
if (p === "txt") {
txtResult.innerText = value;
txtInput.value = value;
}
return Reflect.set(target, p, value);
}
};
const newData = new Proxy(data, handler);
txtInput.oninput = function(e: any) {
const value = e.target.value;
newData.txt = value;
};
window.newData = newData;
- Object.defineProperty
import "./global";
const app = document.getElementById("app");
app.innerHTML = `
<p>简单mvvm的实现</p>
<input placeholder='请输入' id='txtInput'/>
<p>输入的是:<span id='txtResult'></span></p>
`;
const txtInput = document.getElementById("txtInput") as HTMLInputElement;
const txtResult = document.getElementById("txtResult") as HTMLSpanElement;
const data = {} as {
txt: string;
};
let defaultTxt = "";
Object.defineProperty(data, "txt", {
get() {
return defaultTxt;
},
set(value: string) {
defaultTxt = value;
txtResult.innerText = value;
txtInput.value = value;
}
});
txtInput.oninput = function(e: any) {
const value = e.target.value;
data.txt = value;
};
window.newData = data;
滚动加载
TODO
虚拟滚动
TODO
时间分片
<ul id="container"></ul>
let ul = document.getElementById("container");
let total = 100000;
let once = 50;
let index = 0;
// setTimeout
function loop1(curTotal, curIndex) {
if (curTotal <= 0) {
return false;
}
//每页多少条
let pageCount = Math.min(curTotal, once);
setTimeout(() => {
for (let i = 0; i < pageCount; i++) {
let li = document.createElement("li");
li.innerText = curIndex + i + " : " + ~~(Math.random() * total);
ul.appendChild(li);
}
loop(curTotal - pageCount, curIndex + pageCount);
}, 0);
}
// rAf
function loop(curTotal, curIndex) {
if (curTotal <= 0) {
return false;
}
//每页多少条
let pageCount = Math.min(curTotal, once);
let fragment = document.createDocumentFragment();
window.requestAnimationFrame(function () {
for (let i = 0; i < pageCount; i++) {
let li = document.createElement("li");
li.innerText = curIndex + i + " : " + ~~(Math.random() * total);
fragment.appendChild(li);
}
ul.appendChild(fragment);
loop(curTotal - pageCount, curIndex + pageCount);
});
}
loop(total, index);
节流(throttle)
-
基本定义
当持续的触发事件时,保证一定的时间间隔内只执行一次事件处理函数 -
简单实现
function throttle(fn,interval){
// 记录上一次执行时间
let last = 0;
return ()=>{
// 本次执行时间
let now = +new Date();
// 判断两次执行差是否小于时间间隔的阈值
if(now-last>=interval){
last = now;
fn.apply(this,arguments)
}
}
}
- 总结
它能减少函数执行的频率,常用于按钮点击,滚动事件,拖拽事件,计算鼠标移动的距离
防抖(debounce)
-
基本定义
当持续触发的事件,指定的时间段内只会执行一次,若在指定的时间再次触发,则重新计算时间 -
简单实现
function debounce(fn,delay){
let timer = null;
return ()=>{
if(timer) {
clearTimeout(timer);
}
timer = setTimeout(()=>{
fn.apply(this,arguments)
},delay)
}
}
- 总结
它是将多次操作合并为一次操作执行,只要是维护一个定时器,规定在指定的时间(delay)内触发事件,若指定的时间内再次触发,则清除定时器并重新设置定时器,相当于只有最后一次操作能被触发。常用于浏览器窗口缩放,resize事件,搜索框输入查询,表单验证
函数柯里化
function curry(fn, ...args) {
return (...arr) => {
// 合并参数
const merge = [...args, ...arr];
// 判断函数的参数小于原函数的参数,则递归调用,继续合并参数
if (merge.length < fn.length) {
return curry(fn, ...merge);
}
return fn(...merge);
};
}
function _add(a, b) {
return a + b;
}
const add = curry(_add)
// => 3
add(1,2)
// => 3
add(1)(2)