最全js手写

阅读: 评论:0

最全js手写

最全js手写

手写题目

vue2数据劫持

function defineReactive(data) {if (!data || String.call(data) !== '[object Object]')return;for (let key in data) {let val = data[key];Object.defineProperty(data, key, {enumerable: true, //可枚举configurable: true, //可配置get: function() {track(data, key);return val;},set: function() {trigger(val, key);},});if (typeof val === "object") {defineReactive(val);}}
}
function trigger(val, key) {console.log("sue set", val, key);
}
function track(val, key) {console.log("sue set", val, key);
}const data = {name:'better',firends:['1','2']
}
defineReactive(data)
console.log(data.name)
console.log(data.firends[1])
console.log(data.firends[0])
console.log(String.call(data))

vue3 proxy

function reactive(obj) {const handler = {get(target, prop, receiver) {track(target, prop);const value =  (...arguments);if(typeof value === 'Object') {reactive(value)}else {return value}},set(target,key, value, receiver) {trigger(target,key, value);return Reflect.set(...arguments);},};return new Proxy(obj,handler)
}
function track(data, key) {console.log("sue set", data, key);
}
function trigger(data, key,value) {console.log("sue set", key,':',value);
}
const dinner = {name:'haochi1'
}
const proxy  =reactive(dinner)
proxy.name
proxy.list = []
proxy.list.push(1)

防抖节流

防抖

使用场景: 输入框输入搜索,拖拽( mousemove )

效果: 不是每次操作后执行函数.在频繁操作的最后一次操作结束后在设置的时间内没有触发操作时才执行回调

两种思路

  1. 立即执行: 在第一次触发事件的时候立即执行当前操作的回调,后面的操作在最后一次操作结束后在设置的时间内没有触发操作时才执行回调
  2. 无立即执行: 按最后一次操作结束后的规定时间执行
function debounce(fn, delay, immediate) {let timer; //利用闭包保存同一个timerreturn function () {let self = this;let arg = arguments;clearTimeout(timer);if (immediate) {const callNow = !timer;timer = setTimeOut(() => {timer = null;}, delay);if (callNow) fn.apply(self.arg);} else {timer = setTimeout(() => {fn.apply(self, arg);}, delay);}};
}

节流

使用场景:滚动条滚动,频繁点击请求接口

效果:预定一个函数只有在大于等于执行周期时才执行

两种思路:

  1. 时间戳,先会立即执行,达到时间周期再执行
function throttle(fn, delay) {let t;return function () {let self = this;let arg = arguments;if (!t || w() - t >= delay) {fn.apply(self, arg);t = new Date();}};
}
  1. 定时器,定时一定时间周期之后去执行,但是在这时间内中不停的调用,不让他的定时器清零重新计时,不会影响当前的结果,还是那时间继续等,等到达时间周期后触发(会出现停止操作还是会触发)
function throttle(fn,delay) {let timerretrun function () {let self = thislet arg = argumentsif(timer) returntimer = setTimeOut(()=> {fn.apply(fn,arg)timer = null},delay)}
}

call apply bind

三者都是用来改变 this 指向的

  1. call
    使用:
 function.call(thisArg,arg1,grg2,...)
  • thisArg 可选参数,function 执行时内部的 this 指向thisArg
  • arg1,arg2,… 可选参数,传递给 function 的参数列表
  • 返回值:在指定的 this 值和所传递的参数下调用此函数的返回结果
    注意:
  1. function 函数将会立即执行
  2. 在执行时,会将函数内部的 this 指向 thisArg
  3. 出 thisArg 外的所有剩余参数将全部传递给 function
  4. 返回 function 函数执行后的结果
Call = function (context, ...arr) {console.log('调用mycall中的this', this);if (context === null || context === undefined) {context = window;} else {context = Object(context);}const specialPrototype = Symbol('特殊属性symbol');context[specialPrototype] = this; // this指向调用者// context[specialPrototype]执行函数调用时this指向contextlet result = context[specialPrototype](...arr);delete context[specialPrototype];return result;
};
// context :{
//   specialPrototype:this->调用者
// }
  1. apply

注意:

  • 使用 apply 只支持两个参数,第一个为 thisArg,第二个是包括多个参数的数组
Apply = function (context, arr) {console.log(this);if (context === null || context === undefined) {context = window;} else {context = Object(context);}const specialPrototype = Symbol('特殊属性symbol');context[specialPrototype] = this;let result = context[specialPrototype](...arr);delete context[specialPrototype];return result;
};
  1. bind
    使用:
 function.bind(thisArg,arg1,grg2,...)
  • thisArg 可选参数,function 执行时会生成一个包裹着function(...)的邦迪函数,并且将function(...)的 this 指向 thisArg,如果使用 new 运算符调用这个生成的绑定函数,则忽略thisArg
  • arg1,arg2,… 可选参数,传递给 function 的参数列表
  • 返回值:在指定的 this 值和所传递的参数下调用此函数的返回结果
    注意:
  1. bind 方法将创建并返回一个新的函数,新函数称为绑定函数,并且此绑定函数包裹着原始函数
  2. 执行时,会显示将原始函数内部的 this 指向了thisArg
  3. 除 thisArg 外所有剩余参数将全部传递给 function
  4. 执行邦定函数时,如果传递了参数,这些参数将全部传递给原始函数 function
  5. 如果使用 new 运算符调用生成的绑定函数,则忽略 thisArg
bind = function () {//判断调用bind的 是不是函数,抛出异常if (typeof this !== 'function') {throw new Error('function.prototype.bind - what is trying to be bound is not  callable',);}// 将类数组的参数转换成数组然后截取第一个参数// const argsArr =Array.prototype.slice.call(arguments)const argsArr = [...arguments];const args = argsArr.shift();const self = this;const fToBind = function () {console.log('返回函数的参数', arguments);const isNew = this instanceof fToBind; // this是否是fToBind的实例 也就是返回的fToBind是否通过new调用const context = isNew ? this : Object(args); // new调用就绑定到this上,否则就绑定到传入的objThis上return self.apply(context, at([...arguments]));};if (self.prototype) {// 复制源函数的prototype给fToBind 一些情况下函数没有prototype,比如箭头函数fToBind.prototype = ate(self.prototype);}return fToBind;
};

函数柯里化

function add() {const _args = [...arguments];function fn() {_args.push(...arguments);return fn;}fn.toString = function() {// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回return _duce((sum, cur) => sum + cur);}return fn;
}const result = add(1)(2,4)
console.log(result)
console.log(`add`, add(1)(2)(3) )  // 6
add(1, 2, 3)(4)             // 10
add(1)(2)(3)(4)(5)          // 15
add(2, 6)(1)                // 9

手写 new 关键字

作用
创建一个用户定义的对象类型的实例或者具有构造函数的内置对象的实例

特点
可以通过 new 一个构造函数的方式来创建一个对象实例,但是构造函数的差异导致创建的实例有一定的不同

构造函数的返回值不同

  1. 无返回值:生成一个实例化对象,构造函数中的 this 指向该对象
  2. 返回一个对象:return 之前的都被覆盖了,new 生成是 return 的对象
  3. 返回一个非对象:跟没有 return 是一样的结果
// new关键字可以创建对象的实例
// 产生一个新的对象
function myNew(fn, ...args) {const obj = new Object();obj._proto_ = fn.prototype;// ate()方法创建一个新对象,使用现有的对象来提供新创建对象的_proto_// const obj = ate(fn.prototype)// 执行fn并把fn中的this指向新创建的对象let res = fn.apply(obj, args);// 判断构造函数的返回值是不是object,是object则使用retrun的对象,不是的话就使用生成的对象return typeof ret === 'object' ? ret || obj : obj;
}

instanceof

function myInstanceof(A, B) {// 遍历链表let p = A;while (p) {p = p.__proto__;// B的 prototype 属性是否出现在A实例对象的原型链上if (p === B.prototype) {return true;}}return false;
}
function Foo() {}
var f = new Foo();
console.log(myInstanceof(f, Foo)); // true
console.log(myInstanceof(f, Object)); // true
console.log(myInstanceof([1, 2], Array)); // true
console.log(myInstanceof({ a: 1 }, Array)); // false
console.log(myInstanceof(Array, Object)); // true
console.log(Array instanceof Object); // true

深浅克隆

function shallowClone(source) {const target = {};for (let key in source) {if (source.hasOwnProperty(key)) {target[key] = source[key];}}return target;
}
// 深拷贝1.0
function deeClone1(source) {if (typeof source === 'object') {let target = Array.isArray(source) ? [] : {};for (let key in source) {if (source.hasOwnProperty(key)) {// 如果属性是对象类型则递归再次遍历赋值target[key] = deeClone1(source[key]);}}return target;} else {return source;}
}
const textObject = {field1: 1,field2: undefined,field3: {child: 'child',},field4: [2, 4, 8],
};
const deepResult = deeClone1(textObject);
const shallowResult = shallowClone(textObject);
console.log('深克隆', deepResult);
console.log('浅克隆', shallowResult);
deepResult.field4.push(1);
console.log('深克隆', deepResult, textObject);// 深拷贝2.0 解决循环引用问题const textObject2 = {field1: 1,field2: undefined,field3: {child: 'child',},field4: [2, 4, 8],
};
Object2 = textObject2;
// 使用1.0克隆克隆循环引用的值会出现爆栈的现象
// const deepResult2 = deeClone1(textObject2);// 深拷贝2.0 使用Map
// 检查map中有无克隆过的对象
// 有 - 直接返回
// 没有 - 将当前对象作为key,克隆对象作为value进行存储
// 继续克隆
function deeCloneMap(source, map = new Map()) {if (typeof source === 'object') {let target = Array.isArray(source) ? [] : {};// 检查map中有无克隆过的对象if ((source)) {// 有 - 直接返回(source);}// 没有 - 将当前对象作为key,克隆对象作为value进行存储map.set(source, target);for (let key in source) {if (source.hasOwnProperty(key)) {// 如果属性是对象类型则递归再次遍历赋值target[key] = deeCloneMap(source[key], map);}}return target;} else {return source;}
}
const deepResult2 = deeCloneMap(textObject2);
console.log('mapClone', deepResult2);
// 深拷贝2.1 使用是WeakMap弱引用关系,当下一次垃圾回收机制执行时,这块内存就会被释放掉。function deeCloneWeakMap(source, map = new WeakMap()) {if (typeof source === 'object') {let target = Array.isArray(source) ? [] : {};// 检查map中有无克隆过的对象if ((source)) {// 有 - 直接返回(source);}// 没有 - 将当前对象作为key,克隆对象作为value进行存储map.set(source, target);for (let key in source) {if (source.hasOwnProperty(key)) {// 如果属性是对象类型则递归再次遍历赋值target[key] = deeCloneMap(source[key], map);}}return target;} else {return source;}
}
// while循环的性能高 使用while来实现一个通用的forEach遍历,iteratee是遍历的回掉函数,他可以接收每次遍历的value和index两个参数:
function forEach(array, iteratee) {let index = -1;const length = array.length;while (++index < length) {iteratee(array[index], index);}return array;
}
// 深拷贝3.0 使用是WeakMap弱引用关系,当下一次垃圾回收机制执行时,这块内存就会被释放掉。function deeCloneWhile(source, map = new WeakMap()) {// 1.判断是否为null 或undefinedif (typeof source == null) return source;// 2.判断是否为日期Dateif (source instanceof Date) return new Date(osourcebj);// 3.判断是否为正则 typeof /d+/ === 'object'if (source instanceof RegExp) return new RegExp(source);if (typeof source === 'object') {const isArray = Array.isArray(source);let target = isArray ? [] : {};// 检查map中有无克隆过的对象if ((source)) {// 有 - 直接返回(source);}// 没有 - 将当前对象作为key,克隆对象作为value进行存储const keys = isArray ? undefined : Object.keys(source);map.set(source, target);forEach(keys || source, (value, key) => {if (keys) {key = value;}target[key] = deeCloneWhile(source[key], map);});// for (let key in source) {//   if (source.hasOwnProperty(key)) {//     // 如果属性是对象类型则递归再次遍历赋值//     target[key] = deeCloneMap(source[key],map);//   }// }return target;} else {return source;}
}
const textObject3 = {field1: 1,field2: undefined,field3: {child: 'child',},field4: [2, 4, 8],f: {f: { f: { f: { f: { f: { f: { f: { f: { f: { f: { f: {} } } } } } } } } } },},
};
Object3 = textObject3;
const deepResult3 = deeCloneWhile(textObject3);
console.log('deeCloneWhile', deepResult3);

promise

Promise.allSettled

特点
接收一个数组作为参数,数组的每一项都是一个Promise对象,返回一个新的Promise对象,只有等到参数数组的所有的Promise对象都发生状态改变,返回的Promise对象才会发生变更

Promise.allSettled = function (arr) {let result = [];return new Promise((resolve, reject) => {arr.forEach((item, index) => {solve(item).then((res) => {result.push({status: 'fulfilled',value: res,});result.length === arr.length && resolve(result);}).catch((error) => {result.push({status: 'rejected',value: error,});result.length === arr.length && resolve(result);});});});
};

Promise.all()

Promise.all()方法用于将多个 promise 实例包装成一个新的 Promise 实例

Promise.all = function (arr) {let index = 0,result = [];return new Promise((resolve, reject) => {arr.forEach((item, i) => {solve(item).then((res) => {index++;result[i] = res;if (index === arr.length) resolve(res);}).catch((error) => reject(error));});});
};

实现链表

//节点类
class Node{constructor(data){this.data =  = null}
}
//链表类
class SinglyLinkedList{constructor(){this.head = new Node() //head指向头节点}//在链表组后添加节点add(data){let node = new Node(data)let current = this.){current = } = node}//添加到指定位置addAt(index, data){let node = new Node(data)let current = this.headlet counter = 1while(current){if(counter === index){ =  = node}current = unter++}}//删除某个位置的节点removeAt(index){let current = this.headlet counter = ){if(counter === index){ = }current = unter++}}//反转链表reverse(){let current = let prev = this.headwhile(current){let next = //反转:改变当前节点指针。若当前节点是第一个(即头节点后面的)节点,//则此节点的next为null,否则next指向他的上一个节点 = prev===this.head ? null : prevprev = currentcurrent = next} = prev}
}

前序

var Traversal = function(root) {const stack = [];while (root || stack.length){while(root){stack.push(root);root = root.left;}root = stack.pop();root = root.right;}return res;
};

中序

// 中序遍历(非递归,迭代)
const inorder2 = (root) => {if (!root) return;const res = []const track = []// let p = rootwhile (track.length ||root) {// 把左子树全入栈while (root) {track.push(root)root = root.left}root = track.pop()res.push(root.val)// 遍历根节点左边的节点的右侧root = root.rightconsole.log('root',root)}return res
};

后序遍历

var postorderTraversal = function(root) {// 初始化数据const res =[];const stack = [];while (root || stack.length){while(root){stack.push(root);res.unshift(root.val);root = root.right;}root = stack.pop();root = root.left;}return res;
};

反转二叉树

// 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
/*** Definition for a binary tree node.* function TreeNode(val, left, right) {*     this.val = (val===undefined ? 0 : val)*     this.left = (left===undefined ? null : left)*     this.right = (right===undefined ? null : right)* }*/
/*** @param {TreeNode} root* @return {TreeNode}*/var invertTree = function(root) {let now = [root]// 广度优先遍历,利用层次遍历// 设置当前存储节点的数组,初始化是根节点while (now.length) {const next = []now.forEach(item => {// 节点为null直接返回if (item === null) return// 定义第三变量交换左右子节点let n = item.leftitem.left = item.rightitem.right = n// 将左右子节点放进数组next.push(item.left)next.push(item.right)})now = next}return root
};

二分查找

// 704. 二分查找
/*** 
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
提示:二分查找算法
1. 定义头尾两个指针 left = 0 ,right = length 中间索引 mid = left + (right - left) / 2
2. 当left <= right 如果mid上的值等于target, 返回mid, 如果小于targt, left = mid + 1(砍掉左半边)
如果大于target , right = mid - 1(砍掉右半边)
3. 如果while 循环结束后都没有找到target , 返回-1*/
const search = (nums, target) => {let i = 0let j = nums.length - 1let midIndex = 0while (i <= j) {midIndex = Math.floor((i + j) / 2)const midValue = nums[ midIndex ]if (midValue === target) {return midIndex} else if (midValue < target) {i = midIndex + 1} else {j = midIndex - 1}}return -1
}
console.log(search([-1,0,3,5,9,12], 9)) // 4

最大子序和

var maxSubArray = function(nums) {let pre = 0, maxAns = nums[0];nums.forEach((x) => {pre = Math.max(pre + x, x);maxAns = Math.max(maxAns, pre);});return maxAns;
};

爬楼梯

function climbStairs(n) {// 因为状态转移只和上一次迭代和上上次迭代的结果有关,所以只用两个变量存储,不需要用数组,减少了空间if (n === 0) return 0;let pre = 0;let cur = 0;let sum = 1;for (let i = 1; i <= n; i++) {pre = cur;cur = sum;sum = pre + cur;}return sum;
}
console.log(climbStairs(4));

快排

/*** @param {number[]} nums* @return {number[]}*/
var sortArray = function (nums) {//   const length = nums.length//   if (length <= 1) {//     return nums//   }//   const midIndex = Math.floor(length / 2)//   const midValue = nums.splice(midIndex, 1)[ 0 ]//   let leftArray = []//   let rightArray = []//   let index = 0//   while (index < length - 1) {//     const curValue = nums[ index ]//     if (curValue <= midValue) {//       leftArray.push(curValue)//     } else {//       rightArray.push(curValue)//     }//     index++//   }//   return sortArray(leftArray).concat([ midValue ], sortArray(rightArray))let left = 0,right = nums.length - 1;//console.time('QuickSort');main(nums, left, right);//console.timeEnd('QuickSort');return nums;function main(nums, left, right) {// 递归结束的条件,直到数组只包含一个元素。if (nums.length === 1) {// 由于是直接修改arr,所以不用返回值。return;}// 获取left指针,准备下一轮分解。let index = partition(nums, left, right);if (left < index - 1) {// 继续分解左边数组。main(nums, left, index - 1);}if (index < right) {// 分解右边数组。main(nums, index, right);}}// 数组分解函数。function partition(nums, left, right) {// 选取中间项为参考点。let pivot = nums[Math.floor((left + right) / 2)];// 循环直到left > right。while (left <= right) {// 持续右移左指针直到其值不小于pivot。while (nums[left] < pivot) {left++;}// 持续左移右指针直到其值不大于pivot。while (nums[right] > pivot) {right--;}// 此时左指针的值不小于pivot,右指针的值不大于pivot。// 如果left仍然不大于right。if (left <= right) {// 交换两者的值,使得不大于pivot的值在其左侧,不小于pivot的值在其右侧。[nums[left], nums[right]] = [nums[right], nums[left]];// 左指针右移,右指针左移准备开始下一轮,防止arr[left]和arr[right]都等于pivot然后导致死循环。left++;right--;}}// 返回左指针作为下一轮分解的依据。return left;}};

本文发布于:2024-02-01 05:56:02,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170673816434386.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:最全   js
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23