跳到主要内容

JS 常用方法封装

关于JS的一些总结, 持续更新中……

注:以下是一些常用方法封装,以备不时之需,当然 也可以直接使用 lodash,moment,dayjs 等等这种现成的包实现

1、输入一个值,返回其数据类型

function type(para) {
return Object.prototype.toString.call(para);
}

2、数组去重

function unique1(arr) {
return [...new Set(arr)];
}

function unique2(arr) {
let obj = {};
return arr.filter((ele) => {
if (!obj[ele]) {
obj[ele] = true;
return true;
}
});
}

function unique3(arr) {
let result = [];
arr.forEach((ele) => {
if (result.indexOf(ele) == -1) {
result.push(ele);
}
});
return result;
}

3、字符串去重

String.prototype.unique = function () {
let obj = {},
str = "",
len = this.length;
for (let i = 0; i < len; i++) {
if (!obj[this[i]]) {
str += this[i];
obj[this[i]] = true;
}
}
return str;
};

//去除连续的字符串
function uniq(str) {
return str.replace(/(\w)\1+/g, "$1");
}

4、深拷贝 浅拷贝

//深克隆(深克隆不考虑函数)
function deepClone(obj, result) {
let result = result || {};
for (let prop in obj) {
if (obj.hasOwnProperty(prop)) {
if (typeof obj[prop] == "object" && obj[prop] !== null) {
// 引用值(obj/array)且不为null
if (Object.prototype.toString.call(obj[prop]) == "[object Object]") {
// 对象
result[prop] = {};
} else {
// 数组
result[prop] = [];
}
deepClone(obj[prop], result[prop]);
} else {
// 原始值或func
result[prop] = obj[prop];
}
}
}
return result;
}

// 深浅克隆是针对引用值
function deepClone(target) {
if (typeof target !== "object") {
return target;
}
let result;
if (Object.prototype.toString.call(target) == "[object Array]") {
// 数组
result = [];
} else {
// 对象
result = {};
}
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
result[prop] = deepClone(target[prop]);
}
}
return result;
}
// 无法复制函数
let o1 = jsON.parse(jsON.stringify(obj1));

5、reverse 底层原理和扩展

// 改变原数组
Array.prototype.myReverse = function () {
let len = this.length;
for (let i = 0; i < len; i++) {
let temp = this[i];
this[i] = this[len - 1 - i];
this[len - 1 - i] = temp;
}
return this;
};

6、圣杯模式的继承

function inherit(Target, Origin) {
function F() {}
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
// 最终的原型指向
Target.prop.uber = Origin.prototype;
}

7、找出字符串中第一次只出现一次的字母

String.prototype.firstAppear = function () {
let obj = {},
len = this.length;
for (let i = 0; i < len; i++) {
if (obj[this[i]]) {
obj[this[i]]++;
} else {
obj[this[i]] = 1;
}
}
for (let prop in obj) {
if (obj[prop] == 1) {
return prop;
}
}
};

8、找元素的第 n 级父元素

function parents(ele, n) {
while (ele && n) {
ele = ele.parentElement ? ele.parentElement : ele.parentNode;
n--;
}
return ele;
}

9、 返回元素的第 n 个兄弟节点

function retSibling(e, n) {
while (e && n) {
if (n > 0) {
if (e.nextElementSibling) {
e = e.nextElementSibling;
} else {
for (e = e.nextSibling; e && e.nodeType !== 1; e = e.nextSibling);
}
n--;
} else {
if (e.previousElementSibling) {
e = e.previousElementSibling;
} else {
for (
e = e.previousElementSibling;
e && e.nodeType !== 1;
e = e.previousElementSibling
);
}
n++;
}
}
return e;
}

10、封装 mychildren,解决浏览器的兼容问题

function myChildren(e) {
let children = e.childNodes,
arr = [],
len = children.length;
for (let i = 0; i < len; i++) {
if (children[i].nodeType === 1) {
arr.push(children[i]);
}
}
return arr;
}

11、判断元素有没有子元素

function hasChildren(e) {
let children = e.childNodes,
len = children.length;
for (let i = 0; i < len; i++) {
if (children[i].nodeType === 1) {
return true;
}
}
return false;
}

12、我一个元素插入到另一个元素的后面

Element.prototype.insertAfter = function (target, elen) {
let nextElen = elen.nextElenmentSibling;
if (nextElen == null) {
this.appendChild(target);
} else {
this.insertBefore(target, nextElen);
}
};

13、返回当前的时间(年月日时分秒)

function getDateTime() {
let date = new Date(),
year = date.getFullYear(),
month = date.getMonth() + 1,
day = date.getDate(),
hour = date.getHours() + 1,
minute = date.getMinutes(),
second = date.getSeconds();
month = checkTime(month);
day = checkTime(day);
hour = checkTime(hour);
minute = checkTime(minute);
second = checkTime(second);
function checkTime(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
return (
"" +
year +
"年" +
month +
"月" +
day +
"日" +
hour +
"时" +
minute +
"分" +
second +
"秒"
);
}

14、获得滚动条的滚动距离

function getScrollOffset() {
if (window.pageXOffset) {
return {
x: window.pageXOffset,
y: window.pageYOffset,
};
} else {
return {
x: document.body.scrollLeft + document.documentElement.scrollLeft,
y: document.body.scrollTop + document.documentElement.scrollTop,
};
}
}

15、获得视口的尺寸

function getViewportOffset() {
if (window.innerWidth) {
return {
w: window.innerWidth,
h: window.innerHeight,
};
} else {
// ie8及其以下
if (document.compatMode === "BackCompat") {
// 怪异模式
return {
w: document.body.clientWidth,
h: document.body.clientHeight,
};
} else {
// 标准模式
return {
w: document.documentElement.clientWidth,
h: document.documentElement.clientHeight,
};
}
}
}

16、获取任一元素的任意属性

function getStyle(elem, prop) {
return window.getComputedStyle
? window.getComputedStyle(elem, null)[prop]
: elem.currentStyle[prop];
}

17、绑定事件的兼容代码

function addEvent(elem, type, handle) {
if (elem.addEventListener) {
//非ie和非ie9
elem.addEventListener(type, handle, false);
} else if (elem.attachEvent) {
//ie6到ie8
elem.attachEvent("on" + type, function () {
handle.call(elem);
});
} else {
elem["on" + type] = handle;
}
}

18、解绑事件

function removeEvent(elem, type, handle) {
if (elem.removeEventListener) {
//非ie和非ie9
elem.removeEventListener(type, handle, false);
} else if (elem.detachEvent) {
//ie6到ie8
elem.detachEvent("on" + type, handle);
} else {
elem["on" + type] = null;
}
}

19、取消冒泡的兼容代码

function stopBubble(e) {
if (e && e.stopPropagation) {
e.stopPropagation();
} else {
window.event.cancelBubble = true;
}
}

20、检验字符串是否是回文

function isPalina(str) {
if (Object.prototype.toString.call(str) !== "[object String]") {
return false;
}
let len = str.length;
for (let i = 0; i < len / 2; i++) {
if (str[i] != str[len - 1 - i]) {
return false;
}
}
return true;
}

21、检验字符串是否是回文

function isPalindrome(str) {
str = str.replace(/\W/g, "").toLowerCase();
console.log(str);
return str == str.split("").reverse().join("");
}

22、兼容 getElementsByClassName 方法

Element.prototype.getElementsByClassName =
Document.prototype.getElementsByClassName = function (_className) {
let allDomArray = document.getElementsByTagName("*");
let lastDomArray = [];
function trimSpace(strClass) {
let reg = /\s+/g;
return strClass.replace(reg, " ").trim();
}
for (let i = 0; i < allDomArray.length; i++) {
let classArray = trimSpace(allDomArray[i].className).split(" ");
for (let j = 0; j < classArray.length; j++) {
if (classArray[j] == _className) {
lastDomArray.push(allDomArray[i]);
break;
}
}
}
return lastDomArray;
};

23、运动函数

function animate(obj, json, callback) {
clearInterval(obj.timer);
let speed, current;
obj.timer = setInterval(function () {
let lock = true;
for (let prop in json) {
if (prop == "opacity") {
current = parseFloat(window.getComputedStyle(obj, null)[prop]) * 100;
} else {
current = parseInt(window.getComputedStyle(obj, null)[prop]);
}
speed = (json[prop] - current) / 7;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

if (prop == "opacity") {
obj.style[prop] = (current + speed) / 100;
} else {
obj.style[prop] = current + speed + "px";
}
if (current != json[prop]) {
lock = false;
}
}
if (lock) {
clearInterval(obj.timer);
typeof callback == "function" ? callback() : "";
}
}, 30);
}

24、弹性运动

function ElasticMovement(obj, target) {
clearInterval(obj.timer);
let iSpeed = 40,
a,
u = 0.8;
obj.timer = setInterval(function () {
a = (target - obj.offsetLeft) / 8;
iSpeed = iSpeed + a;
iSpeed = iSpeed * u;
if (Math.abs(iSpeed) <= 1 && Math.abs(a) <= 1) {
console.log("over");
clearInterval(obj.timer);
obj.style.left = target + "px";
} else {
obj.style.left = obj.offsetLeft + iSpeed + "px";
}
}, 30);
}

25、封装自己的 forEach 方法

Array.prototype.myForEach = function (func, obj) {
let len = this.length;
let _this = arguments[1] ? arguments[1] : window;
// let _this=arguments[1]||window;
for (let i = 0; i < len; i++) {
func.call(_this, this[i], i, this);
}
};

26、封装自己的 filter 方法

Array.prototype.myFilter = function (func, obj) {
let len = this.length;
let arr = [];
let _this = arguments[1] || window;
for (let i = 0; i < len; i++) {
func.call(_this, this[i], i, this) && arr.push(this[i]);
}
return arr;
};

27、数组 map 方法

Array.prototype.myMap = function (func) {
let arr = [];
let len = this.length;
let _this = arguments[1] || window;
for (let i = 0; i < len; i++) {
arr.push(func.call(_this, this[i], i, this));
}
return arr;
};

28、数组 every 方法

Array.prototype.myEvery = function (func) {
let flag = true;
let len = this.length;
let _this = arguments[1] || window;
for (let i = 0; i < len; i++) {
if (func.apply(_this, [this[i], i, this]) == false) {
flag = false;
break;
}
}
return flag;
};

29、数组 reduce 方法

Array.prototype.myReduce = function (func, initialValue) {
let len = this.length,
nextValue,
i;
if (!initialValue) {
// 没有传第二个参数
nextValue = this[0];
i = 1;
} else {
// 传了第二个参数
nextValue = initialValue;
i = 0;
}
for (; i < len; i++) {
nextValue = func(nextValue, this[i], i, this);
}
return nextValue;
};

30、获取 url 中的参数

function getWindonHref() {
let sHref = window.location.href;
let args = sHref.split("?");
if (args[0] === sHref) {
return "";
}
let hrefarr = args[1].split("#")[0].split("&");
let obj = {};
for (let i = 0; i < hrefarr.length; i++) {
hrefarr[i] = hrefarr[i].split("=");
obj[hrefarr[i][0]] = hrefarr[i][1];
}
return obj;
}

31、数组排序

// 快排 [left] + min + [right]
function quickArr(arr) {
if (arr.length <= 1) {
return arr;
}
let left = [],
right = [];
let pIndex = Math.floor(arr.length / 2);
let p = arr.splice(pIndex, 1)[0];
for (let i = 0; i < arr.length; i++) {
if (arr[i] <= p) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
// 递归
return quickArr(left).concat([p], quickArr(right));
}

// 冒泡
function bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}

function bubbleSort(arr) {
let len = arr.length;
for (let i = 0; i < len - 1; i++) {
for (let j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}

32、遍历 Dom 树

// 给定页面上的DOM元素,将访问元素本身及其所有后代(不仅仅是它的直接子元素)
// 对于每个访问的元素,函数讲元素传递给提供的回调函数
function traverse(element, callback) {
callback(element);
let list = element.children;
for (let i = 0; i < list.length; i++) {
traverse(list[i], callback);
}
}

33、原生 js 封装 ajax

function ajax(method, url, callback, data, flag) {
let xhr;
flag = flag || true;
method = method.toUpperCase();
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject("Microsoft.XMLHttp");
}
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(2);
callback(xhr.responseText);
}
};

if (method == "GET") {
let date = new Date(),
timer = date.getTime();
xhr.open("GET", url + "?" + data + "&timer" + timer, flag);
xhr.send();
} else if (method == "POST") {
xhr.open("POST", url, flag);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(data);
}
}

34、异步加载 script

function loadScript(url, callback) {
let oscript = document.createElement("script");
if (oscript.readyState) {
// ie8及以下版本
oscript.onreadystatechange = function () {
if (
oscript.readyState === "complete" ||
oscript.readyState === "loaded"
) {
callback();
}
};
} else {
oscript.onload = function () {
callback();
};
}
oscript.src = url;
document.body.appendChild(oscript);
}

35、cookie 管理

let cookie = {
set: function (name, value, time) {
document.cookie = name + "=" + value + "; max-age=" + time;
return this;
},
remove: function (name) {
return this.setCookie(name, "", -1);
},
get: function (name, callback) {
let allCookieArr = document.cookie.split("; ");
for (let i = 0; i < allCookieArr.length; i++) {
let itemCookieArr = allCookieArr[i].split("=");
if (itemCookieArr[0] === name) {
return itemCookieArr[1];
}
}
return undefined;
},
};

36、实现 bind()方法

Function.prototype.myBind = function (target) {
let target = target || window;
let _args1 = [].slice.call(arguments, 1);
let self = this;
let temp = function () {};
let F = function () {
let _args2 = [].slice.call(arguments, 0);
let parasArr = _args1.concat(_args2);
return self.apply(this instanceof temp ? this : target, parasArr);
};
temp.prototype = self.prototype;
F.prototype = new temp();
return F;
};

// 或者
Function.prototype.myBind = function (context) {
// 判断调用对象是否为函数
if (typeof this !== "function") {
throw new Error("Type error");
}
// 获取参数
const args = [...arguments].slice(1),
const fn = this;
return function Fn() {
return fn.apply(
this instanceof Fn ? this : context,
// 当前的这个 arguments 是指 Fn 的参数
args.concat(...arguments)
);
};
};

37、实现 call()方法

Function.prototype.myCall = function (context) {
// 判断调用对象
if (typeof this !== "function") {
throw new Error("Type error");
}
// 首先获取参数
let args = [...arguments].slice(1);
let result = null;
// 判断 context 是否传入,如果没有传就设置为 window
context = context || window;
// 将被调用的方法设置为 context 的属性
// this 即为我们要调用的方法
context.fn = this;
// 执行要被调用的方法
result = context.fn(...args);
// 删除手动增加的属性方法
delete context.fn;
// 将执行结果返回
return result;
};

38、实现 apply()方法

// Function.prototype.myApply = function () {
// let ctx = arguments[0] || window;
// ctx.fn = this;
// if (!arguments[1]) {
// let result = ctx.fn();
// delete ctx.fn;
// return result;
// }
// let result = ctx.fn(...arguments[1]);
// delete ctx.fn;
// return result;
// };

Function.prototype.myApply = function (context) {
if (typeof this !== "function") {
throw new Error("Type error");
}
let result = null;
context = context || window;
// 与上面代码相比,这里使用 Symbol 来保证属性唯一
// 也就是保证不会重写用户自己原来定义在 context 中的同名属性
const fnSymbol = Symbol();
context[fnSymbol] = this;
// 执行要被调用的方法
if (arguments[1]) {
result = context[fnSymbol](...arguments[1]);
} else {
result = context[fnSymbol]();
}
delete context[fnSymbol];
return result;
};

39、防抖

function debounce(handle, delay) {
let timer = null;
return function () {
let _self = this,
_args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
handle.apply(_self, _args);
}, delay);
};
}

40、节流

function throttle(handler, wait) {
let lastTime = 0;
return function (e) {
let nowTime = new Date().getTime();
if (nowTime - lastTime > wait) {
handler.apply(this, arguments);
lastTime = nowTime;
}
};
}

41、requestAnimFrame 兼容性方法

window.requestAnimFrame = (function () {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
}
);
})();

42、cancelAnimFrame 兼容性方法

window.cancelAnimFrame = (function () {
return (
window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
function (id) {
window.clearTimeout(id);
}
);
})();

43、jsonp 底层方法

function jsonp(url, callback) {
let oscript = document.createElement("script");
if (oscript.readyState) {
// ie8及以下版本
oscript.onreadystatechange = function () {
if (
oscript.readyState === "complete" ||
oscript.readyState === "loaded"
) {
callback();
}
};
} else {
oscript.onload = function () {
callback();
};
}
oscript.src = url;
document.body.appendChild(oscript);
}

44、获取 url 上的参数

function getUrlParam(sUrl, sKey) {
let result = {};
sUrl.replace(/(\w+)=(\w+)(?=[&|#])/g, function (ele, key, val) {
if (!result[key]) {
result[key] = val;
} else {
let temp = result[key];
result[key] = [].concat(temp, val);
}
});
if (!sKey) {
return result;
} else {
return result[sKey] || "";
}
}

45、格式化时间

function formatDate(t, str) {
let obj = {
yyyy: t.getFullYear(),
yy: ("" + t.getFullYear()).slice(-2),
M: t.getMonth() + 1,
MM: ("0" + (t.getMonth() + 1)).slice(-2),
d: t.getDate(),
dd: ("0" + t.getDate()).slice(-2),
H: t.getHours(),
HH: ("0" + t.getHours()).slice(-2),
h: t.getHours() % 12,
hh: ("0" + (t.getHours() % 12)).slice(-2),
m: t.getMinutes(),
mm: ("0" + t.getMinutes()).slice(-2),
s: t.getSeconds(),
ss: ("0" + t.getSeconds()).slice(-2),
w: ["日", "一", "二", "三", "四", "五", "六"][t.getDay()],
};
return str.replace(/([a-z]+)/gi, function ($1) {
return obj[$1];
});
}

46、验证邮箱的正则表达式

function isAvailableEmail(sEmail) {
let reg = /^([\w+\.])+@\w+([.]\w+)+$/;
return reg.test(sEmail);
}

47、函数柯里化

//是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术

function curryIt(fn) {
let length = fn.length,
args = [];
let result = function (arg) {
args.push(arg);
length--;
if (length <= 0) {
return fn.apply(this, args);
} else {
return result;
}
};
return result;
}

48、大数相加

function sumBigNumber(a, b) {
let res = "", //结果
temp = 0; //按位加的结果及进位
a = a.split("");
b = b.split("");
while (a.length || b.length || temp) {
//~~按位非 1.类型转换,转换成数字 2.~~undefined==0
temp += ~~a.pop() + ~~b.pop();
res = (temp % 10) + res;
temp = temp > 9;
}
return res.replace(/^0+/, "");
}

49、单例模式

function getSingle(func) {
let result;
return function () {
if (!result) {
result = new func(arguments);
}
return result;
};
}

50、从 RGB 转换为 HEX

const rgbToHex = (r, g, b) => {
const toHex = (num) => {
// 转换为16进制
const hex = num.toString(16);

return hex.length === 1 ? `0${hex}` : hex;
};

return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
return;
};

console.log(rgbToHex(46, 32, 67)); // #2e2043

51、判断两个字符串是否为互相排列

const isAnagram = (str1, str2) => {
const normalize = (str) =>
str.toLowerCase().normalize("NFD").split("").sort().join("");
return normalize(str1) === normalize(str2);
};

console.log(isAnagram("anagram", "naagram")); // true
console.log(isAnagram("car", "rat")); // false
console.log(isAnagram("heArt", "traEH")); // true

52、判断两个字符串是否为相反回文字符串

const isReverse = (str1, str2) => {
const normalize = (str) =>
str.toLowerCase().normalize("NFD").split("").reverse().join("");
return normalize(str1) === str2;
};

console.log(isReverse("anagram", "naagram")); // false
console.log(isReverse("tar", "rat")); // true

53、手写 promise.all

function promiseAll(promises) {
return new Promise((resolve, reject) => {
if(promises && !Array.isArray(promises)) {
throw new TypeError("args must be a array!");
}

let count = 0;
let length = promises.length;
let results = [];

if(!length) return resolve([])

for(let i = 0;i < length;i++) {
Promise.resolve(promises[i]).then((val) => {
count++;
results.push(val);
if(count == length) {
return resolve(results);
}
}, err => {
return reject(err);
})
}
})
}

54、Symbol 迭代器

  • 创建一个迭代器,for...of...遍历
const LPL = {
name: "TES",
players: ["JackeyLove", "Karsa", "knight"],
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.players.length) {
const res = { value: this.players[index], done: false };
index++;
return res;
} else {
return { value: undefined, done: true };
}
},
};
},
};
for (let item of LPL) { //JackeyLove
console.log(item); //Karsa
} //knight

55、实现new

function myNew(context) { 
// 设置新的对象
let obj = new Object();
// 指定_proto_ 为构造函数 prototype
obj.__proto__ = context.prototype;
// 重新指向this
// [...arguments].slice(1) 去除第一个参数的其他剩余参数 通过apply 重新指向新的对象
const result = context.apply(obj, [...arguments].slice(1));

return typeof context === "object" ? res : obj;
}