前言
对于很多方法,经常需要我们可以自己手写出来,自己总结了一些手写题,欢迎点评
ajax
//让数据变成对象的格式
function reolveData (data) {
let arr = []
for (const k in data) {
arr.push(k + '=' + data[k])
}
return arr.join('&')
}
function myAjax (options) {
const xhr = new XMLHttpRequest()
const qs = reolveData(options.data)
//判断请求类型
if (options.methods.toUpperCase() === "GET") {
xhr.open(options.methods, options.url + '?' + qs)
xhr.send()
} else if (xhr.methods.toUpperCase() === "POST") {
xhr.open(options.methods, options.url)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send(qs)
}
xhr.onreadystatechange = function() {
if (xhr.readystatus === 4 && xhr.status === 200) {
const result = JSON.parse(xhr.responseText)
option.success(result)
}
}
}
防抖
- 指的是在一段时间内,时间只会触发最后一次,类似于打游戏的回城
function deounce (fn, delay = 300) {
let time = null
return function() {
let _this = this;
clearTimeout(time)
time = setTimeout(() => {
fn.apply(_this, arguments)
}, delay)
}
}
节流
- 一般用于时间,按照一段时间的间隔了进行触发,鼠标移动,滚动条滚动很频繁触发的
function throttle (fn) {
let flag = null
return function() {
if (flag) return
flag = setTimeout(() => {
fn.apply(this.arguments)
flag = null
}, 500)
}
}
深拷贝
const isObj = (val) => typeof val === "Object" && val !== null
//方法一:
function deepClone (obj) {
const newObj = obj instanceof Array ? [] : {}
for (const key in obj) {
const item = obj[key]
newObj[key] = isObj(item) ? deepClone(item) : item
}
return newObj
}
//方法二
function deepClone(obj){
let obj1=JSON.stringify(obj)
let obj2=JSON.parse(obj1)
return obj2
}
intanceof
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。
function myInstanceof (left, right) {
if (typeof left !== 'object' || left === null) return false
let leftPro = left.__proto__, rightPro = right.prototype;
while (true) {
if (leftPro === null) return false
if (leftPro === rightPro) return true
leftPro = leftPro.__proto__
}
}
call
call()方法使用一个指定的this值和单独给出的一个或多个参数来调用一个函数。
Function.prototype.myCall = function(context) {
// 判断调用者是否为函数
if (typeof this !== "function") {
throw new Error('type erroe')
}
// 不传参默认为window
context = context || window
// 使用Symbol 来确定唯一
const fn = Symbol()
// 模拟对象的this指向
context[fn] = this
// 获取参数
const args = [...arguments].splice(1)
// 绑定并执行参数
const result = context[fn](...args)
// 删除定义的this
delete context[fn]
return result
}
apply
apply()方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。
Function.prototype.myApply = function(context) {
if (typeof this !== "function") {
throw new Error("Error")
}
const fn = Symbol()
context[fn] = this
const arge = [...arguments][1]
const result = context[fn](...arge)
delete context[fn]
return result;
}
bind
bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
Function.prototype.myBind = function(context) {
if (typeof this !== "function") {
throw new Error("Error")
}
context = context || window
// 模拟this的指向
const self = this
// 获取参数
const args = [...arguments].splice(1)
// 最后返回一个函数,并绑定this要考虑到使用new去调用,并且bind是可以传参的
return function Fn (...newFnArges) {
if (this instanceof Fn) {
return new self(...args, ...newFnArgs)
} else {
return self.apply(context, [...args, ...newFnArge])
}
}
}
new
function myNew (fn, ...arges) {
let newObj = Object.create(fn.prototype)
let res = fn.call(newObj, ...arges)
return res && typeof res === 'object' ? res : newObj
}
继承
//call实现继承
function Person(uname, age) {
this.uname = uname;
this.age = age;
}
function Son(uname, age) {
Person.call(this, uname, age);
}
var son = new Son("zhang", 12);
console.log(son);
链表
function ListNode (val, next) {
this.val = (val === undefined ? 0 : val)
this.next = (next === undefined ? null : next)
}
二叉树
function TreeNode (val) {
this.val = val
this.left = right = null
}
数组去重
使用Set
不考虑兼容性,这种去重的方法代码最少。
这种方法还无法去掉“{}”空对象,后面的高阶方法会添加去掉重复“{}”的方法。
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log([...new Set(arr)] );
使用双重for循环,然后splice去重
双层循环,外层循环元素,内层循环时比较值。值相同时,则删去这个值。
NaN和{}没有去重,两个null直接消失了
function unique(arr){
for(var i=0; i<arr.length; i++){
for(var j=i+1; j<arr.length; j++){
if(arr[i]==arr[j]){ //第一个等同于第二个,splice方法删除第二个
arr.splice(j,1);
j--;
}
}
}
return arr;
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}]
console.log(unique(arr))
利用indexOf去重
新建一个空的结果数组,for 循环原数组,判断结果数组是否存在当前元素,如果有相同的值则跳过,不相同则push进数组。
NaN、{}没有去重
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var array = [];
for (var i = 0; i < arr.length; i++) {
if (array .indexOf(arr[i]) === -1) {
array .push(arr[i])
}
}
return array;
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}]
console.log(unique(arr))
利用includes
和上面indexOf思想类似
{}没有去重
function unique(arr) {
if (!Array.isArray(arr)) {
console.log('type error!')
return
}
var array =[];
for(var i = 0; i < arr.length; i++) {
if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
array.push(arr[i]);
}
}
return array
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}]
console.log(unique(arr))
利用hasOwnProperty
所有的都去重了
function unique(arr) {
var obj = {};
return arr.filter(function(item, index, arr){
return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true)
})
}
var arr = [1, 1, 'true', 'true', true, true, 15, 15, false, false, undefined, undefined, null, null, NaN, NaN, 'NaN', 0, 0, 'a', 'a', {}, {}]
console.log(unique(arr))
数据的交并集
直接使用 filter、concat 来计算
var a = [1,2,3,4,5]
var b = [2,4,6,8,10]
//交集
var c = a.filter(function(v){ return b.indexOf(v) > -1 })
//差集
var d = a.filter(function(v){ return b.indexOf(v) == -1 })
//补集
var e = a.filter(function(v){ return !(b.indexOf(v) > -1) })
.concat(b.filter(function(v){ return !(a.indexOf(v) > -1)}))
//并集
var f = a.concat(b.filter(function(v){ return !(a.indexOf(v) > -1)}));
ES6 中可以借助扩展运算符( ... )以及 Set 的特性实现相关计算,代码也会更加简单些。
var a = [1,2,3,4,5]
var b = [2,4,6,8,10]
console.log("数组a:", a);
console.log("数组b:", b);
var sa = new Set(a);
var sb = new Set(b);
// 交集
let intersect = a.filter(x => sb.has(x));
// 差集
let minus = a.filter(x => !sb.has(x));
// 补集
let complement = [...a.filter(x => !sb.has(x)), ...b.filter(x => !sa.has(x))];
// 并集
let unionSet = Array.from(new Set([...a, ...b]));
消息订阅与发布
class EventEmitter {
constructor() {
this.events = {}
}
//实现订阅
on(type, callback) {
if (!this.events[type]) {
this.events[type] = [callback]
} else {
this.events[type].push(callback)
}
}
//删除订阅
off(type, callback) {
if (!this.events[type]) return
this.events[type] = this.events[type].filters(item => item !== callback)
}
//只执行一次订阅事件
once(type, callback) {
function fn() {
callback()
this.off(type, fn)
}
this.on(type.fn)
}
//触发事件
emit(type, ...rest) {
this.events[type] && this.events[type].forEach(fn => fn.apply(this, rest))
}
}
// 使用如下
// const event = new EventEmitter();
// const handle = (...rest) => {
// console.log(rest);
// };
// event.on("click", handle);
// event.emit("click", 1, 2, 3, 4);
// event.off("click", handle);
// event.emit("click", 1, 2);
// event.once("dbClick", () => {
// console.log(123456);
// });
// event.emit("dbClick");
// event.emit("dbClick");