常用函数
深拷贝与浅拷贝
表头 | 和原数据是否指向同一对象 | 第一层数据为基本数据类型 | 原数据中包含子对象 |
---|---|---|---|
赋值 | 是 | 改变会使原数据一同改变 | 改变会使原数据一同改变 |
浅拷贝 | 否 | 改变不会使原数据一同改变 | 改变会使原数据一同改变 |
深拷贝 | 否 | 改变不会使原数据一同改变 | 改变不会使原数据一同改变 |
浅拷贝实现
function shallowClone(source) {
if (!source || typeof source !== "object") {
throw new Error("error arguments");
}
// 判断是数组还是 对象
var targetObj = source.constructor === Array ? [] : {};
for (var keys in source) {
if (source.hasOwnProperty(keys)) {
targetObj[keys] = source[keys];
}
}
return targetObj;
}
浅拷贝其他方法
var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"
深拷贝实现
如果觉得深拷贝写的太简单的,可以点击查看lodash 的深拷贝实现,比较详细
function deepClone(source) {
if (!source || typeof source !== "object") {
throw new Error("error arguments");
}
var targetObj = source.constructor === Array ? [] : {};
for (var keys in source) {
if (source.hasOwnProperty(keys)) {
if (source[keys] && typeof source[keys] === "object") {
// 赋予 初始值
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
} else {
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
深拷贝其他方法
var obj1 = {a:'a'};
var obj2 = JSON.parse(JSON.stringify(obj1));
深拷贝应用场景
当时要把原数据A保存,然后把新数据B遍历更改指定属性,最后A和B对比,将B中属性和A对比判断提交数据C中push A还是B。当时数据A嵌套有三层,最后就用了深拷贝解决的。
防抖节流
防抖(Debounce): 在指定时间A内,连续调用的时间间隔小于A,前面的调用都会被取消,最后一次调用被执行。
- 移动端 上拉刷新
- 模糊查询搜索框的 ajax请求
节流(throttle): 在指定时间A内,该函数只会被调用一次。等待时间A过了函数可以被再次调用且执行最后一次调用。
- ajax请求的时候,规定时间内设置请求次数,可以减少ajax请求。
- resize或者鼠标移动事件,防止浏览器频繁响应事件,严重拉低性能。
防抖(Debounce)
利用延时器就可以结局
function debounce(fn, delay) {
let timer = null;
return function() {
let context = this;
let args = arguments;
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args);
}, delay);
};
}
节流(throttle)
// 时间戳形式表现
function throttle(fn,delay){
let preDate = new Date();
return function(){
const args = arguments;
const context = this;
if(preDate + delay <= new Date()){
fn.apply(context,args);
preDate = new Date();
}
}
}
// setTimeout表现
function throttle(fn, delay) {
let timer = null;
return function() {
const args = arguments;
const context = this;
if (timer) retun;
timer = setTimeout(() => {
fn.apply(context, args);
clearTimeout(timer);
timer = null;
}, delay);
};
}
模拟实现 call、apply、bind
call: 在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法。
apply: 在使用一个指定的 this 值和一个包含多个参数的数组的前提下调用某个函数或方法。
bind: 接收绑定this的对象
call
Function.prototype.call = function(context,...args) {
var context = context || window;
context.fn = this;
const result = context.fn(...args);
delete context.fn;
return result
}
apply
Function.prototype.apply = function(context,argsArray) {
var context = context || window;
context.fn = this;
const result = context.fn(...argsArray);
delete context.fn;
return result
}
bind
// 这是我自己根据上面改的版本
Function.prototype.bind = function(context, ...args) {
context.fn = this;
const bindFn = (...arg) =>{
const result = context.fn(...arg);
delete context.fn;
return result
}
return bindFn;
};
// 这是网上看的版本
Function.prototype.bind2 = function (context) {
if (typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var context = this;
var args = Array.prototype.slice.call(arguments, 1);
var fNOP = function () {};
var fbound = function () {
context.apply(this instanceof context ? this : context, args.concat(Array.prototype.slice.call(arguments)));
}
fNOP.prototype = this.prototype;
fbound.prototype = new fNOP();
return fbound;
}
算法排序
排序经常用在数据处理上,我觉得挺重要的,经常用,都已经耳熟能详了
去重
indexOf
function sort (arr) {
let result = [];
arr.forEach((a)=>{
if(result.indexOf(a) === -1){
result.push(a)
}
})
return result;
}
sort排序
const sort = (arr) => arr.sort((a,b)=>a-b).reduce((target,current)=>{
if(target[target.length - 1] !== current) {
target.push(current)
}
return target;
},[])
Set
var unique = a => [...new Set(a)];
Map
var unique = arr => {
const seen = new Map();
return arr.filter(a => !seen.has(a) && seen.set(a,1))
}
冒泡排序
依次比较相邻的两个数 小的向前放 大的向后放
function bubbleSort(arr) {
var max = arr.length - 1;
for (let j = 0; j < max; j++) {
for (let i = 0; i < max - j; i++) {
if (arr[i] > arr[i + 1]) {
var temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
}
return arr
}
快速排序
去中间项center,小的放左边left,大的放右边right。递归left、right,返回left,center,right合并项
function quickSort(arr) {
if (arr.length < 2) {
return arr;
}
var centerNum = Math.floor(arr.length / 2);
var center = arr.splice(centerNum, 1);
var left = [];
var right = [];
arr.forEach(a => {
if (a < center) {
left.push(a);
} else {
right.push(a);
}
});
return quickSort(left).concat(center, quickSort(right));
}
插入排序
将一个元素插入到其它已经有序的牌中的适当位置,因此其他所有元素在插入之前都向右移动一位,为新元素腾出空间
function insertSort(array) {
for (var i = 1; i < array.length; i++) {
var key = array[i];
var j = i - 1;
while (j >= 0 && array[j] > key) {
array[j + 1] = array[j];
j--;
}
array[j + 1] = key;
}
return array;
}
选择排序
找到数组最小的元素,将它和数组中第一个元素交换位置,接下来,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置,往复如此,直到将整个数组排序。基本点就是不断地选择剩余元素之中的最小者。
function select(array){
var len=array.length;
for(var i=0;i<len-1;i++){
var minnum=array[i];
for(var j=i+1;j<len;j++){ // j=i+1是把与自己比较的情况给省略掉
if(array[j]<minnum){
var c;
c=minnum;
minnum=array[j]; //交换两个值
array[j]=c;
}
}
array[i]=minnum;
}
return array;
}
二分查找
给定一个有序的数组,查找某个数是否在数组中 如果有则返回index 否则返回-1
function bsearch(array,target,low = 0,high= array.length-1)
{
if(low > high) {
return -1
}
var mid = Math.floor((low + high)/2) ;
var midValue = array[mid]
if(midValue > target){
return bsearch2(array,target,low,mid-1)
}else if(midValue < target){
return bsearch2(array,target,mid+1,high)
}else {
return mid;
}
}
数据结构
链表
react fiber 里的经典问题
创建单向链表
//节点应用类型
function Node(data){
this.data=data;
this.next=null;
}
//链表引用类型
function List(){
//哨兵节点
this.head=new Node();
this.size=0;
}
List.prototype={
//在链表尾部添加节点
add: function(data){
var current=this.head;
while(current.next!=null){
current=current.next;
}
current.next=new Node(data);
this.size++;
},
//遍历链表,不对链表元素操作都可以调用此方法
forEach: function(callback){
for(var current=this.head.next;current!=null;current=current.next){
callback(current.data);
}
},
//打印链表中所有元素
print: function(){
this.forEach(function(item){
console.log(item);
})
},
//查找链表元素的位置
indexOf: function(data){
var pos=0;
var current=this.head.next;
while(current!=null){
if(current.data===data){
break;
}
current=current.next;
pos++;
}
return pos;
},
/**
* 在位置pos处插入节点值为data
* 若成功则返回插入的值,若失败则返回null
*/
insert: function(pos,data){
if(pos<0 || pos>this.size-1){
return null;
}
//插入位置的上一个节点
var last=this.head;
for(var i=0;i<pos;i++){
last=last.next;
}
//保存下一个节点的引用
var ready=last.next;
last.next=new Node(data);
last.next.next=ready;
this.size++;
return data;
},
/**
* 删除指定位置的元素
* 若成功则返回删除的值,若失败则返回null
*/
removeAt: function(index){
if(index<0 || index>this.size-1){
return null;
}
var current=this.head.next;
var last=this.head;
for(var i=0;i<index;i++){
last=current;
current=current.next;
}
last.next=current.next;
this.size--;
return current.data;
},
//删除相应元素
remove: function(data){
var current=this.head.next;
var last=this.head;
while(current!=null){
if(current.data===data){
last.next=current.next;
//已删除节点
this.size--;
break;
}
last=current;
current=current.next;
}
}
};
var list=new List();
list.add(1);
list.add(2);
list.add(3);
list.insert(1,2);
console.log(list.indexOf(2)); //2
list.remove(3);
list.removeAt(1);
console.log(list.size); //2
list.print(); //1 2
二叉树
在node处理文件操作,diff算法,动态路由 里常见
深度优先遍历
function deepTraversal(node) {
var nodes = [];
if (node != null) {
nodes.push(node);
var children = node.children;
for (var i = 0; i < children.length; i++)
deepTraversal(children[i]);
}
return nodes;
}
广度优先遍历
function wideTraversal(node) {
var nodes = [];
var i = 0;
if (!(node == null)) {
nodes.push(node);
wideTraversal(node.nextElementSibling);
node = nodes[i++];
wideTraversal(node.firstElementChild);
}
return nodes;
}
删除二叉树某一个的子节点
//删除最小值
function delMinNode (root){
if(!root) {
return false;
}
var current = root;
if (current.left == null) {
var rightNode = current.right;
return rightNode;
}
current.left = delMinNode(current.left);
return current.left;
}
//删除最大值
function delMaxNode (root) {
if(!root) {
return false;
}
var current = root;
if(current.right == null) {
var leftNode = current.left;
return leftNode;
}
current.right = delMaxNode(current.right)
return current.right;
}
吐槽
其实搞不懂为什么要面试写这些代码,真的很烦 !!!
遇到面试要做笔试题的,通常都溜了 !!!
觉得形式主义很烦人!!!
但是迫于淫威 我还是写了 (ಥ_ಥ) , 流下了底层人民的辛酸泪。
欢迎观众老爷们给我提评论,我可以做补充,如果有提议,此博文会一直继续!!