前端手撕代码

本文深入讲解了JavaScript中数组去重的两种方法(indexOf和ES6 Set),快速排序(冒泡与sort)的实现,以及防抖和节流的实现原理。还涉及浅拷贝和深拷贝的区别,事件委托、instanceof、call/apply/bind的应用,Promise的all、race和allsettled,new关键字的使用,以及发布订阅模式和观察者模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1. 数组去重

1.1 indexOf

1.2 ES6 Set

2. 快速排序

2.1 冒泡排序

2.2 sort

3.  防抖和节流

4. 浅拷贝和深拷贝

5. 事件委托

6. instanceof

7. call、apply、bind

8.  promise

8.1 promise.all

8.2 promise.race

8.3 promise.allsettled

9. new

10. 发布订阅模式

11. 观察者模式

12.函数柯里化

13.图片懒加载

14.数组拉平


1. 数组去重

1.1 indexOf

var arr=[3,5,1,6,1,3,5,6,9];
var result=[];
arr.forEach(item=>{
    if(result.indexOf(item)==-1){
    result.push(item);
    }
})
console.log(result);

1.2 ES6 Set

var arr=[3,5,1,6,1,3,5,6,9];
var result=Array.from(new Set(arr));
console.log(result);

2. 快速排序

2.1 冒泡排序

function bubbleSort(nums){
    //每轮循环都从最后一个元素开始,比较并交换 一次循环会把最小的数顶到最上面
    for(let i=0;i<nums.length-1;i++){//只是控制次数为n-1
        for(let j=nums.length-1;j>i;j--){
            if(nums[j]){
                //交换
                 let temp=nums[j];
                 nums[j]=nums[j-1];
                 nums[j-1]=temp;
             }
         }
}

//test
nums=[5,1,4,3,5];
bubbleSort(nums);
consloe.log(nums);

2.2 sort

升序x-y

var a=[3,5,2,6];
a.sort(function(x,y){
    return x-y;
})
console.log(a);

降序y-x

var a=[32,3,2,5,21];
a.sort(function(x,y){
    return y-x;
});
console.log(a);

3.  防抖和节流

//防抖函数
function debounce(fn,delay){
    var timeout;

    return function(){
        if(timeout)clearTimeout(timeout);
        timeout=setTimeout(function(){
            fn();
        },delay);
    }
}
//节流 定时器
function throttle(fn,wait){
    var timer;

    return function(){
        if(!timer){
            timer=setTimeout(function(){ 
                timer=null;
                fn();
            },wait)
        }
    }
}

4. 浅拷贝和深拷贝

//浅拷贝
var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 18
            }
        };
        var o = {};
        Object.assign(o, obj);
        console.log(o);
        o.msg.age = 20;
        console.log(obj);
//深拷贝
 var obj = {
            id: 1,
            name: 'andy',
            msg: {
                age: 18
            },
            color: ['pink', 'red']
        };
        var o = {};
        // 封装函数 
        function deepCopy(newobj, oldobj) {
            for (var k in oldobj) {
                // 判断我们的属性值属于那种数据类型
                // 1. 获取属性值  oldobj[k]
                var item = oldobj[k];
                // 2. 判断这个值是否是数组
                if (item instanceof Array) {
                    newobj[k] = [];
                    deepCopy(newobj[k], item)
                } else if (item instanceof Object) {
                    // 3. 判断这个值是否是对象
                    newobj[k] = {};
                    deepCopy(newobj[k], item)
                } else {
                    // 4. 属于简单数据类型
                    newobj[k] = item;
                }
 
            }
        }
        deepCopy(o, obj);
        console.log(o);
 
        var arr = [];
        console.log(arr instanceof Object);
        o.msg.age = 20;
        console.log(obj);

5. 事件委托

<ul id="ul">
        <li>0</li>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
</ul>
    <button id="btn">点我添加一个li</button>
// 事件委托具体实现
var ul = document.getElementById("ul");
    ul.onclick = function (event) {
        event = event || window.event;
        var target = event.target;
        // 获取目标元素
        if (target.nodeName == 'LI') {
            alert(target.innerHTML);
        }
    }
    // 为按钮绑定点击事件
    var btn = document.getElementById('btn');
    btn.onclick = function () {
        var li = document.createElement('li');
        // 新增li的内容为ul当前子元素的个数
        li.textContent = ul.children.length;
        ul.appendChild(li);

6. instanceof

instanceof:左边是对象,右边是类;

  • 当对象是右边类或子类所创建对象时,返回true;否则,返false。
  • 类的实例包含本身的实例,以及所有直接或间接子类的实例
  • instanceof左边显式声明的类型与右边操作元必须是同种类或存在继承关系,也就是说需要位于同一个继承树,否则会编译错误

function myInstanceof(target,Fn){
  let proto = target.__proto__;
  while(true){
    if(proto===Fn.prototype){
      return true;
    }
    if(proto===null){
      return false;
    }
    proto =  proto.__proto__;
  }

7. call、apply、bind

他们每个都有自己的特点,比如call和apply是立即执行,而bind是绑定this后不立即执行。再有就是apply是第二个参数是数组,而call是依次传入参数。

//call
​​​​​​​Function.prototype.myCall = function(cont){
    let context = cont ||window
    let args = [...arguments].slice(1)
    context._fn = this;
    let res = context._fn(...args);
    delete context._fn;
    return res;
}
//apply
Function.prototype.myCall = function(cont){
    let context = cont ||window
    let args = [...arguments].slice(1)
    context._fn = this;
    let res = context._fn(...args);
    delete context._fn;
    return res;
}
//bind
Function.prototype.MyApply = function (cont){
  let context = cont || window;
  if(typeof this != 'function'){
    throw new Error('this is not a function!')
  }
  const _this  = this;
  return function (){
    _this.call(context,...arguments);
  }

8.  promise

all 、 race 、allsettled 。

  • all 是所有promise完成;
  • race是一个完成,但是有rejected 他俩就结束;
  • allsettled是所有都结束,不管是哪个状态。

8.1 promise.all

//promise.all
function myPromiseAll (promiseArr){
  return new Promise(function(resolve,reject){
    let result = []
    let count = 0;
    for(let i =0;i<promiseArr.length;i++){
      promiseArr[i].then(res=>{
        //注意这里是按照输入数组的顺序,而不是push
        result[i]=res;
        count++;
        if(count==promiseArr.length){
          resolve(result);
        }
      },err=>{
        reject(err)
      })
    }
  })
}

8.2 promise.race

function myPromiseRace (promiseArr){
  return new Promise(function(resolve,reject){
      for(let i =0;i<promiseArr.length;i++){
        promiseArr[i].then(res=>{
          resolve(res)
        },err=>{
          reject(err)
        })
      }
  })
}

8.3 promise.allsettled


function myAllSetteld (promiseArr){
  return new Promise(function(resolve,reject){
    let count = 0;
    let result = [];
    for(let i =0;i<promiseArr.length;i++){
      promiseArr[i].then(res=>{
        result[i]= {'status':'fulfilled','value':res};
        count++;
        if(count==promiseArr.length) resolve(result)
      },err=>{
        result[i] = {'status':'rejected','reason':err};
        count++;
        if(count==promiseArr.length) resolve(result)
      })
    }
  })
}

9. new


function myNew (constructor,...args){
  /* 1. 创建一个新对象
     2. 将构造函数原型上的方法复制给新对象
     3. 传入参数执行构造函数,并获取执行结果
     4. 判断返回值是是否为对象,不为对象返回执行结果 
  */
 let newObj = Object.create(constructor.prototype);
 let result = constructor.apply(newObj,args);
 return typeof result ==='object' ? result : newObj;

10. 发布订阅模式

class EventEmitter {
  constructor(){
    // 是一个对象来记录事件名和相应的触发函数
    this.events = {}
  }
  // 绑定一个事件的事件处理函数
  on(event,fcCallBack){
    if(!this.events[event]){
      this.events[event] = [fcCallBack]
    }else{
      this.events[event].push(fcCallBack)
    }
  }
 
 
  //触发函数
  emit(event,...args){
    if(this.events[event]){
      this.events[event].forEach(el=>{
        el.apply(this,args);
      })
    }
  }
 
 
  //停止某个事件的事件处理函数
  off (event,fcCallBack){
    if(!this.events[event]) return;
    this.events[event].filter(item=> item!= fcCallBack)
  }
 
 
  //绑定单次执行事件处理函数
  once (event,fcCallBack){
    function fn(){
      fcCallBack();
      this.off(event,fcCallBack)
    }
    this.on(event,fn)
  }
 
}

11. 观察者模式


/*
描述
    请补全JavaScript代码,完成"Observer"、"Observerd"类实现观察者模式。要求如下:
    1. 被观察者构造函数需要包含"name"属性和"state"属性且"state"初始值为"走路"
    2. 被观察者创建"setObserver"函数用于保存观察者们
    3. 被观察者创建"setState"函数用于设置该观察者"state"并且通知所有观察者
    4. 观察者创建"update"函数用于被观察者进行消息通知,该函数需要打印(console.log)数据,数据格式为:小明正在走路。其中"小明"为被观察者的"name"属性,"走路"为被观察者的"state"属性
    注意:
    1. "Observer"为观察者,"Observerd"为被观察者
*/
class Observerd {
    constructor(name,state='走路'){
        this.name = name;
        this.state = state;
        this.observers = [];
    } 
    setObserver(observer){
        this.observers.push(observer);
    }
    setState(state){
        this.state = state;
        this.observers.forEach(item=>item.update(this))
    }
 }
 
 
 class Observer {
     update(observerd){
         console.log(`${observerd.name}正在${observerd.state}`)
     }
 }

12.函数柯里化

实现累加

function add(a,b,c){
    console.log(a+b+c)
}
function curry (fn,...args){
    let len = fn.length;
    return function(){
        let _args= [...args,...arguments]
        if(_args.length<len){
            return curry.call(this,fn,..._args);
        }
        return fn.apply(this,_args)
    }
}
let tmp = curry(add);
tmp(3)(1)(4);

13.图片懒加载

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        img {
            display: block;
            width: 100%;
            height: 300px;
            margin-bottom: 10px;
        }
    </style>
</head>

<body>
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
    <img data-src="mao.jpg" alt="">
</body>
<script>
    var imgs = document.querySelectorAll('img');

    //获得元素距离页面顶部的距离
    function getTop(e) {
        var T = e.offsetTop;
        while (e = e.offsetParent) {
            T += e.offsetTop;
        }
        return T;
    }

    function lazyLoad(imgs) {
        //获取可视区高度
        var H = window.innerHeight || document.documentElement.clientHeight;
        //获取被卷页面高度
        var S = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
        for (let i = 0; i < imgs.length; i++) {
            if (H + S > getTop(imgs[i])) {
                imgs[i].src = imgs[i].getAttribute('data-src');
            }
        }
    }

    window.onload = window.onscroll = function () {
        lazyLoad(imgs);
    }
</script>

</html>

14.数组拉平

es6的flat

[1, [2, [3]]].flat(Infinity);  // [1, 2, 3]

 arr.toString().split(’,’)

var arr= [11, [22, 33], [44, 55], 66];
var newArr=arr.toString().split(',');  //["11", "22", "33", "44", "55", "66"]

arr.join().split(’,’)

const arr = [11, [22, 33], [44, 55], 66];
const flatArr = arr.join().split(','); // ["11", "22", "33", "44", "55", "66"]

### 华为OD面试代码准备方法 #### 了解考试形式与范围 对于华为OD面试的代码环节,理解其具体的形式和考察重点至关重要。该环节不仅限于后端开发和算法岗,还包括前端、运维、测试以及数据分析等多个领域[^1]。 #### 掌握常见编程语言 熟悉至少一种主流编程语言是必不可少的技能之一。推荐深入学习并掌握C/C++、Java、Python或JavaScript中的一种或多门语言。每种语言都有各自的特点,在不同场景下可能更适用某些类型的解决方案[^2]。 #### 练习典型算法题目 针对可能出现的各种类型问题进行充分练习非常重要。可以从以下几个方面入: - **基础数据结构**:数组、链表、栈队列等基本概念及其操作; - **经典排序查找算法**:快速排序、二分查找等高效实现方式; - **动态规划与贪心策略**:解决最优化类问题的有效段; - **图论基础知识**:遍历、连通性检测等相关理论应用; ```python def binary_search(arr, target): low, high = 0, len(arr)-1 while low <= high: mid = (low + high) // 2 if arr[mid] == target: return mid elif arr[mid] < target: low = mid + 1 else: high = mid -1 ``` 此段代码展示了经典的二分查找算法实现,适用于有序列表中的元素定位任务。 #### 提升编码效率与质量 编写清晰易懂且高效的程序同样不可忽视。这包括但不限于良好的变量命名习惯、合理的函数划分逻辑、简洁明了的注释说明等方面。此外,还应注重时间空间复杂度分析能力培养,力求写出性能最优解法。 #### 参加模拟实战演练 最后但并非不重要的是参与尽可能多的真实环境下的模拟测试。通过不断尝试过往真题来积累经验教训,并逐步调整自己的答题节奏与思维方式直至形成稳定发挥状态为止。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值