1.手写call方法
var obj = {
name: 456,
}
function say() {
console.log(this.name)
}
Function.prototype.myCall = function (context, ...args) {
//将当前被调用的方法定义在cxt.func上.(为了能以对象调用形式绑定this)
//新建一个唯一的Symbol变量避免重复
let ctx = context || window
let func = Symbol()
ctx[func] = this
args = args ? args : []
//以对象调用形式调用func,此时this指向cxt 也就是传入的需要绑定的this指向
const res = args.length > 0 ? ctx[func](...args) : ctx[func]()
//删除防止传入对象被污染
delete ctx[func]
return res
}
say.myCall(obj)
2.手写apply方法
var obj = {
name: 456,
}
function say(args) {
console.log(this.name)
}
Function.prototype.myApply = function (context, args = []) {
let ctx = context || window
let func = Symbol()
ctx[func] = this
const res = args.length > 0 ? ctx[func](args) : ctx[func]()
delete ctx[func]
return res
}
say.myApply(obj)
3.手写bind
实现方法:
- bind方法不会立即执行,需要返回一个待执行的函数;
- 实现作用于绑定 apply
- 当作为构造函数的时候,进行原型继承
Function.prototype.myBind = function (context, ...args) {
let fn = this
args = args ? args : []
return function newFunc(...args2) {
return fn.apply(context, [...args, ...args2])
}
}
4.实现new
- 创建一个新对象
- 把this指向这个对象
- 返回这个对象
function Parent() {
this.name = "colin";
}
function myNew(Parent, ...args) {
let newObj = Object.create(Parent.prototype);
let retrunResult = Parent.apply(newObj, args);
let isObject = typeof retrunResult === "object" && retrunResult !==null;
let isFunction = typeof ctorReturnResult === "function";
if (isObject || isFunction) {
return retrunResult;
}
return newObj;
}
let p = myNew(Parent);
console.log(p.name);
5.实现instanceof
- 实现instanceof用来判断a是A的实例,a instanceof A ,返回true为实例,否则返回false
- instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性
- 遍历左边变量的原型链,知道找到右边变量的prototype,如果没找到则返回false
function myInstanceOf(a, b) {
let left = a.__proto__
let right = b.prototype
while (true) {
if (left === null) {
return false
}
if (left === right) {
return true
}
left = left.__proto__
}
}
let p = new Parent()
console.log(myInstanceOf(p, Parent))
6.手写函数防抖debounce
适用于点击事件等场景,连续触发多次,只有一次生效
function debounce(fn, time) {
let timer = null;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
fn(...args);
}, time);
};
}
7.手写函数节流throttle
适用于连续滚动等场景,连续触发,每个n时间生效一次
function throttle(fn, time) {
let flag = true
return (...args) => {
if (flag) {
flag = false
setTimeout(() => {
flag = true
fn(...args)
}, time)
}
}
}
8.实现深拷贝
- 判断类型,正则和日期直接返回新对象
- 考虑循环引用的问题,判断如果hash中含有直接返回hash中的值
- 空或者非对象类型,直接返回原始值
- 新建一个相应的new obj.construction 加入hash
- 遍历对象递归
function deepClone(obj, hash = new WeakMap()) {
if (obj instanceof RegExp) return new RegExp(obj);
if (obj instanceof Date) return new Date(obj);
if (obj === null || typeof obj !== "object") return obj;
if (hash.has(obj)) {
return hash.get(obj);
}
let newObj = new obj.construction();
hash.set(obj, newObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key], hash);
}
}
let symbolObj = Object.getOwnPropertySymbols(obj);
for (let i = 0; i < symbolObj.length; i++) {
if (obj.hasOwnProperty(symbolObj[i])) {
constr[symbolObj[i]] = deepClone(obj[symbolObj[i]], hash);
}
}
return newObj;
}
9.数组扁平化
let arr=[1,2[3,4]];
arr.flat(Infinity)
function flat(arr) {
return arr.reduce((prev, cur) => {
return prev.contact(Array.isArray(cur) ? flat(cur) : cur);
}, []);
}
10.函数柯里化实现
function curry() {
let _args = Array.from(arguments);
var _adder = function () {
_args.push(...arguments);
return _adder;
};
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
});
};
return _adder;
}
console.log(curry(1, 2)(4));
11.随机生成数
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
12.数组乱序
let arr = [2, 3, 454, 34, 324, 32]
arr.sort(randomSort)
function randomSort(a, b) {
return Math.random() > 0.5 ? -1 : 1
}
13.对象数组去重
- 写个函数把对象中的key排序,再转成字符串
- 遍历数组利用Set 将字符串后的对象去重
输入: [{a:1,b:2,c:3},{b:2,c:3,a:1},{d:2,c:2}]
输出: [{a:1,b:2,c:3},{d:2,c:2}]
function objSort(obj) {
let newObj = {}
Object.keys(obj)
.sort()
.map((key) => {
newObj[key] = obj[key]
})
return JSON.stringify(newObj)
}
function unique(arr) {
let set = new Set()
for (let i = 0
let str = objSort(arr[i])
set.add(str)
}
arr = [...set].map((item) => {
return JSON.parse(item)
})
return arr
}
14.模板引擎实现
let template = "<div>我是{{name}}</div>,年龄{{age}},性别{{sex}}"
let data = {
name: "colin",
age: 18,
sex: "男",
}
function render(template, data) {
const reg = /\{\{(\w+)\}\}/;
if (reg.test(template)) {
const name = reg.exec(template)[1];
template = template.replace(reg, data[name]);
return render(template, data);
}
return template;
}
function render(template, data) {
let computed = template.replace(/\{\{(\w+)\}\}/g, function (match, key){
return data[key];
});
return computed;
}
console.log(render(template, data));
15.用setTimeOut实现setInterval (带清除定时器)
function setIntervals(fn, t) {
let timer = null;
function interval() {
fn();
timer = setTimeout(interval, t);
}
interval();
return {
cancle: () => {
clearTimeout(timer);
},
};
}
let timeObj = setIntervals(() => {
console.log(6);
}, 1000);
16.用setInterval实现setTimeOut
function mySetTimeOut(fn, t) {
let timer = setInterval(() => {
clearInterval(timer);
fn();
}, t);
}
mySetTimeOut(() => {
console.log(9);
}, 3000);
17.冒泡排序
function bubbleSort(arr) {
let len = arr.length
//外层循环控制从头到尾 比较+交换 需要多少轮
for (var i = 0
//内层循环用于完成每一轮遍历过程中重复的 比较+交换
for (var j = 0
let temp
if (arr[j] > arr[j + 1]) {
temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
}
}
}
return arr
}
console.log(bubbleSort([2, 1, 6, 4, 3]))
18.快速排序
function selectSort(arr) {
let len = arr.length
//缓存当前区间最小值的索引
let minIndex
// i 是当前排序区间的起点
for (var i = 0
// i、j分别定义当前区间的上下界,i是左边界,j是右边界
minIndex = i
for (var j = i
if (arr[j] < arr[minIndex]) {
minIndex = j
}
}
if (minIndex !== i) {
let temp
temp = arr[minIndex]
arr[minIndex] = arr[i]
arr[i] = temp
}
}
return arr
}
console.log(selectSort([2, 1, 6, 4, 3]))
19.快速排序
function quickSort(arr) {
if (arr.length < 2) {
return arr
}
let current = arr[arr.length - 1]
let left = arr.filter((itm, idx) => itm <= current && idx !== arr.length- 1)
let right = arr.filter((itm, idx) => itm > current)
return [...quickSort(left), current, ...quickSort(right)]
}
console.log(quickSort([2, 1, 6, 4, 3]))
20.类数组转化为数组方法
[...arrayLike]
Array.from(arrayLike)
Array.prototype.slice.call(arrayLike)
Array.apply(null, arrayLike)
Array.prototype.concat.apply([], arrayLike)
21.Object.is实现
Object.js 不会转换比较的两个值的类型,与===有些类似,但不同的是:
- NAN在===中是不相同的,在Object.is是相等的
- +0和-0在===是相等的,在Object.js是不等的
Object.is = function (a, b) {
if (a === b) {
//这种情况只有一种是特殊的 即+0 -0
//如果a!==0 返回true
//如果x===0,需要判断+0 和-0 1/+0 ===Infinity 1/-0=== -Infinity
return a !== 0 || 1 / a === 1 / b;
}
//a!==b 的情况下,有一种是两者相等的 即 a,b都是NaN
return a !== a && b !== b;
};
console.log(Object.is(+0, -0));//false
console.log(Object.is(NaN, NaN));//true
22.使用 requestAnimationFrame实现setInterval
- 优点:requestAnimationFrame自带节流功能,基本可以保证在16.6毫秒内只执行一次,延时效果是精确的。
function setInterval(callback, interval) {
let timer
const now = Date.now
let startTime = now()
let endTime = startTime
const loop = () => {
timer = window.requestAnimationFrame(loop)
endTime = now()
if (endTime - startTime >= interval) {
startTime = endTime = now()
callback(timer)
}
}
timer = window.requestAnimationFrame(loop)
return timer
}
let a=0
setInterval((timer) => {
console.log(1);
a++;
if(a==4){
cancelAnimationFrame(timer)
}
}, 1000);
23.请求jsonp数据封装
function fetchJsonp ({ url, data }) {
return new Promise((resolve, reject) => {
let container = document.getElementsByTagName("head")[0]
let script = document.createElement('script')
let params = []
for (let key in data) {
params.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]))
}
url = url.indexOf("?") > 0 ? (url + "&") : (url + "?")
url += params.join("&")
script.src = url
let fnName = data['callback']
window[fnName] = function (res) {
container.removeChild(script)
resolve(res)
}
// 出错处理
script.onerror = function () {
reject()
container.removeChild(scriptNode)
}
script.type = "text/javascript"
container.appendChild(script)
})
}
24.前端大文件切片上传
- 通过File的slice方法对大文件进行切分,关键代码如下:
let inputChange = async (e) => {
var file = e.target.files[0]
let size = 1024 * 50
let fileChunks = [];
let index = 0;
for (let cur = 0; cur < file.size; cur += size) {
fileChunks.push({
hash: index++,
chunk: file.slice(cur, cur + size)
})
}
console.log(fileChunks)
const uploadPromise = fileChunks.map((item, index) => {
let formData = new FormData()
formData.append('filename', file.name)
formData.append('hash', item.hash)
formData.append('chunk', item.chunk)
return axios({
method: 'post',
url: '/upload',
data: formData
})
})
await Promise.all([uploadPromise]);
await axios({
method: 'get',
url: '/merge-image',
params: {
filename: file.name
}
});
}