秋招前端部分手撕题

本文整理了前端面试中常见的手撕代码题目,包括数字分组、对象和数组扁平化、括号匹配、事件委托、URL验证、全排列等。还涉及JavaScript中的函数实现,如_objectCreate、_call、_bind,以及继承模式如原型链继承、构造函数继承、组合继承等。此外,还涵盖了发布订阅模式、观察者模式、二叉树问题和排序算法等核心知识点。
摘要由CSDN通过智能技术生成

1.将1-52中的所有数字随机分为四组

function cutArray(arry) {
    let a = [],
        b = [],
        c = [],
        d = [];
    while (a.length < 13) {
        let i = Math.floor(Math.random() * arry.length + 1); //产生随机数组下标。为啥不写52,而是写arry.length是因为arry是变化的,看下面代码。
        a.push(arry[i-1]);
        arry.splice(i-1, 1); //改变arry,将已分配的数据删除
    }
    while (b.length < 13) {
        let i = Math.floor(Math.random() * arry.length + 1);
        b.push(arry[i-1]);
        arry.splice(i-1, 1);
    }
    while (c.length < 13) {
        let i = Math.floor(Math.random() * arry.length + 1);
        c.push(arry[i-1]);
        arry.splice(i-1, 1);
    }
    d = arry;
}

2.对象扁平化

function objectFlat(data) {
    let newObj = {};
    let process = (key, value) => {
        if (Object(value) !== value) {
            //普通类型、null、undefind
            newObj[key] = value;
        } else {
            //引用类型
            if (Array.isArray(value)) {
                value.forEach((childVal, childIndex) => {
                    process(`${key}[${childIndex}]`, childVal);
                });
                //空数组赋值空数组
                if (value.length === 0) newObj[key] = [];
            } else {
                const keyArr = Object.keys(value);
                keyArr.forEach((childKey) => {
                    process(`${key}.${childKey}`, value[childKey]);
                });
                //为空赋值空对象
                if (keyArr.length === 0) newObj[key] = {};
            }
        }
    };
    Object.keys(data).forEach((key) => process(key, data[key]));
    return newObj;
}

3.数组扁平化

const _flatten = arr => {
    var newarr = []
    arr.forEach(i=>{
        if(Array.isArray(i)){
            newarr = newarr.concat(_flatten(i))
        }
        else newarr.push(i)
    })
    return newarr
}

4.括号匹配

function isValid(str){
    let strArr = str.split(''),
        left = [];// 空栈
    for(let i=0;i<strArr.length;i++){
        if(strArr[i] == '(' || strArr[i] == '[' || strArr[i] == '{'){
            left.push(strArr[i]) //左括号入栈
        }else{
            if(strArr[i] == ')' && left.pop() != '('){
                return false //结束循环
            }
            if(strArr[i] == ']' && left.pop() != '['){
                return false 
            }
            if(strArr[i] == '}' && left.pop() != '{'){
                return false
            }
        }
    }
    return left.length == 0
}

5.事件委托

  1. 给"ul"标签添加点击事件
  2. 当点击某"li"标签时,该标签内容拼接".“符号。如:某"li"标签被点击时,该标签内容为”…"
    注意:
  3. 必须使用DOM0级标准事件(onclick)
<!DOCTYPE html>
<html>
    <head>
        <meta charset=utf-8>
    </head>
    <body>
        <ul>
            <li>.</li>
            <li>.</li>
            <li>.</li>
        </ul>
        <script type="text/javascript">
            // 补全代码
            document.querySelector('ul').onclick = function(e){
                e.target.innerHTML+='.'
            }
        </script>
    </body>
</html>

6.合法的URL

要求以Boolean的形式返回字符串参数是否为合法的URL格式。
注意:1. 协议仅为HTTP(S)

<!DOCTYPE html>
<html>
    <head>
        <meta charset=utf-8>
    </head>
    <body>
        <script type="text/javascript">
            const _isUrl = url => {
                // 补全代码
                let reg = /^((http|https):\/\/)?(([a-zA-Z0-9]+-[a-zA-Z0-9]+|[a-zA-Z0-9]+)\.)+([a-zA-Z]+)(:\d+)?(\/.*)?(\?.*)?(#.*)?/
                return reg.test(url)
            }
        </script>
    </body>
</html>

7.全排列

const _permute = string => {
    // 补全代码
    var res = []
    if (string.length == 1) {
        res.push(string)
    } else if (string.length > 1) {
        for (let i = 0; i < string.length; i++) {
            var current = string[i]
            var rest = string.slice(0, i) + string.slice(i + 1, string.length)
            var preRes = _permute(rest)
            for (let m = 0; m < preRes.length; m++) {
                var tmp = current + preRes[m]
                res.push(tmp)
            }
        }
    }
    return res
}

8._objectCreate

要求实现Object.create函数的功能且该新函数命名为"_objectCreate"

const _objectCreate = proto => {
    // 补全代码
    if(typeof proto !=='Object' || proto ==null) return
    const fn = function(){}
    fn.prototype = proto
    return new fn()
}

9._call函数

要求实现Function.call函数的功能且该新函数命名为"_call"。

Function.prototype._call = function(target=window){
    target['fn']=this
    const result = target['fn']([...arguments].shift())
    delete target['fn']
    return result 
}

10.Function.bind

要求实现Function.bind函数的功能且该新函数命名为"_bind"。

Function.prototype._bind = function(target,...arguments1){
    const _this = this
    return function(...arguments2) {
        return _this.apply(target,arguments1.concat(arguments2))
    }
}

11.实现new操作符

const _new = function() {
    // 补全代码
    var obj = {}
    var fn = [...arguments].shift()
    obj.__proto__ = fn.prototype
    const obj2 = fn.apply(obj,arguments)
    return obj2 instanceof Object? obj2:obj
}

12. Object.freeze

const _objectFreeze = object => {
    // 补全代码
    const keys = Object.getOwnPropertyNames(object)
    const symbols=Object.getOwnPropertySymbols(object)
    ;[...keys,...symbols].forEach(key=>{
        Object.defineProperty(object,key,{
            configurable:false,
            writable:false
        })
    })
    Object.seal(object)
}

13.浅拷贝

const _shallowClone = target => {
    // 补全代码
    return Object.assign({},target)
}

14.简易深拷贝

要求实现对象参数的深拷贝并返回拷贝之后的新对象。
注意:1. 参数对象和参数对象的每个数据项的数据类型范围仅在数组、普通对象({})、基本数据类型中2. 无需考虑循环引用问题

const _sampleDeepClone = target => {
    // 补全代码
    var a= JSON.parse(JSON.stringify(target))
    return a
}

15.深拷贝

要求实现对象参数的深拷贝并返回拷贝之后的新对象。
注意:1. 需要考虑函数、正则、日期、ES6新对象2. 需要考虑循环引用问题

const _completeDeepClone = (target, map = new Map()) => {
    // 补全代码
    // 直接返回基础数据类型及函数类型
    if (target === null || typeof target !== 'object') return target
    if (target.constructor === Date) return new Date(target)
    if (target.constructor === RegExp) return new RegExp(target)
    //循环引用
    if (map.has(target)) return map.get(target)
    //保证原型链与属性的特性一致
    let clone = Object.create(Object.getPrototypeOf(target), Object.getOwnPropertyDescriptors(target))
    //添加到map
    map.set(target, clone)
    //遍历属性
    const keys = Object.getOwnPropertyNames(target)
    const symbols = Object.getOwnPropertySymbols(target);
    [...keys, ...symbols].forEach(key => {
        clone[key] = _completeDeepClone(clone[key], map)
    })
    return clone
}

16. 寄生组合式继承

要求通过寄生组合式继承使"Chinese"构造函数继承于"Human"构造函数。要求如下:

  1. 给"Human"构造函数的原型上添加"getName"函数,该函数返回调用该函数对象的"name"属性2. 给"Chinese"构造函数的原型上添加"getAge"函数,该函数返回调用该函数对象的"age"属性
function Human(name) {
    this.name = name
    this.kingdom = 'animal'
    this.color = ['yellow', 'white', 'brown', 'black']
}
Human.prototype.getName = function(){
    return this.name
}
function Chinese(name,age) {
    Human.call(this,name)
    this.age = age
    this.color = 'yellow'
}
Chinese.prototype = Object.create(Human.prototype)
Chinese.prototype.constructor = Chinese
Chinese.prototype.getAge = function(){
    return this.age
}

17. 发布订阅模式

完成"EventEmitter"类实现发布订阅模式。
注意:1. 同一名称事件可能有多个不同的执行函数2. 通过"on"函数添加事件3. 通过"emit"函数触发事件

class EventEmitter {
    constructor(){
        this.events = {}
    }
    on(e,fn){
        if(!this.events[e]){
            this.events[e]=[fn]
        }else{
            this.events[e].push(fn)
        }
    }
    emit(e){
        if(this.events[e]){
            this.events[e].forEach((callback)=>{
                callback()
            })
        }
    }
}

18. 观察者模式

完成"Observer"、"Observerd"类实现观察者模式。要求如下:

  1. 被观察者构造函数需要包含"name"属性和"state"属性且"state"初始值为"走路"2. 被观察者创建"setObserver"函数用于保存观察者们3. 被观察者创建"setState"函数用于设置该观察者"state"并且通知所有观察者4. 观察者创建"update"函数用于被观察者进行消息通知,该函数需要打印(console.log)数据,数据格式为:小明正在走路。其中"小明"为被观察者的"name"属性,"走路"为被观察者的"state"属性
    注意:1. "Observer"为观察者,"Observerd"为被观察者
class Observerd {
    constructor(name){
        this.name = name
        this.state = '走路'
        this.observers = []
    }
    setObserver(observer){
        this.observers.push(observer)
    }
    setState(state){
        this.state = state
        this.observers.forEach(observer=>{
            observer.update(this)
        })
    }
}
class Observer {
    constructor(){}
    update(observerd){
        console.log(observerd.name+'正在'+observerd.state)
    }
}

19. 二叉树最底层最左边的值

  var findBottomLeftValue = function (root) {
    let res, maxLevel = 0;
    function dfs(node, level) {
        if (!node) return;
        if (level > maxLevel) res = node, maxLevel = level;
        dfs(node.left, level + 1);
        dfs(node.right, level + 1);
    }
    dfs(root, 1);
    return res && res.val;
};

20. 快速排序

function quictSort(arr){
    if(arr.length<=1) return arr
    var pointIndex = Math.floor(arr.length/2)
    var point = arr.splice(pointIndex,1)[0]
    var left = []
    var right = []
    for(var i=0;i<arr.length;i++){
        if(arr[i]<point) left.push(arr[i])
        else right.push(arr[i])
    }
    return quictSort(left).concat([point],quictSort(right))
}

21. 手写防抖

var debounce = function(fn,delay){
    let timer=null
    return function(){
        clearTimeout(timer)
        let args = arguments
        timer=setTimeout(()=>{
            fn.apply(this,args)
        },delay)
    }
}

22. 手写节流

function jieliu(fn,time){
    var start =0
    return function(){
        var end = new Date()
        if(end-start>time){
            fn.apply(this,arguments)
            start = end
        }
    }
}

23. 原型链继承

缺点:Parent 中的引用属性会被每个子类示例共享

function Parent(){
    this.parentPrototype = 'parent prototype'
    //验证这种继承方法的确定,如果父类示例中存在一个引用类型的属性,将会被所有子类共享
    this.parentObj = {
        info:'缺点:Parent 中的引用属性会被每个子类示例共享'
    }
}
function Children(){}
//将Children的原型对象指定为Parent的示例,通过原型链,将Parent中的属性赋值给Children示例
Children.prototype = new Parent()
const a = new Children()
console.log(a.parentPrototype);// parent prototype
//缺点
const b = new Children()
//在a示例中改动继承的引用属性
a.parentObj.info = "我是a示例中 引用属性parentObj中的 info"
//b与a示例共享引用属性
console.log(b.parentObj.info); // 我是a示例中 引用属性parentObj中的 info

24. 借用构造函数继承

优点:
1避免了子类实例共享引用属性的情况
2可以在实例化时给Parent构造函数传递参数
缺点:
1如果Parent中存在一个函数,那么每次实例化Children的时候,都会创建一个同样函数,函数的复用性就难以体现

function Parent() {
    this.parentPrototype = "parent prototype"
    this.obj = {
        info: "parent obj info"
    }
    this.fn = function () {
        console.log("打印功能")
    }
}
function Children() {
    Parent.call(this);
}
const a = new Children();
console.log(a.parentPrototype); // parent ptototype
//缺点 此时Parent()会再次创建一个fn函数,这个是没有必要的
const b = new Children();
a.obj.info = "a obj info";
//优点 避免了子类实例共享引用属性
console.log(b.obj.info) // parent obj info;

25. 组合继承:原型链 + 构造函数 (这是js中最常见的继承方式)

优点:避免了子类共享引用属性同时避免了父类构造函数重复对function属性的创建

function Parent() {
    this.parentPrototype = "我是Parent 中的属性"
}
//Parent中的方法,在原型上定义
Parent.prototype.pFn = function () {
    console.log('我是Parent中的方法');
}
function Children() {
    //Parent中的属性仍然在构造函数中继承
    Parent.call(this);
}
//将Children的原型对象赋值为 Parent实例,这样Parent中的方法也能够被Children继承
Children.prototype = new Parent();
const c = new Children();
console.log(c.parentPrototype); //我是Parent 中的属性
c.pFn(); //我是Parent中的方法

26. 原型式继承 (注意:是原型式而非原型链,这种方法使用较少)

缺点:和原型链继承一样,后代实例会共享父类引用属性

function objFn(o) {
    o.objFnPrototype = "我是 objFnPrototype"
    function F() {}
    F.prototype = o;
    return new F();
}
let a = objFn({
    name: "name1"
});
console.log(a.name); //name1
console.log(a.objFnPrototype); //我是 objFnPrototype

27. 寄生式继承

缺点:和原型链继承一样,parent中的引用属性,会被所有示例共享

function createObje(obj) {
    let clone = Object.assign(obj); //接受到对象后,原封不动的创建一个新对象
    clone.prototype1 = "我是新增的prototype1"; //在新对象上新增属性,这就是所谓的寄生
    return clone; //返回新对象
}
const parent = {
    parentPrototype: "parentPrototype"
}
//c实例,就继承了parent的所有属性
let c = createObje(parent);
console.log(c.parentPrototype); //parentPrototype

28寄生组合式继承 (寄生+组合(原型链+借用构造函数)

优点:和组合继承一样,只不过没有组合继承的调用两次父类构造函数的缺点

function inherProto(superType, subType) {
    //拷贝一个超类的原型副本
    let proto = {
        ...superType.prototype
    };
    //将原型的超类副本作为子类的原型对象,也就是第一种中的原型链继承方式,只不过继承的是超类原型的副本
    subType.prototype = proto;
    //这一步比较迷,官方的说法是,我们在拷贝超类的原型的时候,拷贝的proto对象,将会丢失默认自己的构造函数,也就是superType,
    //所以我们这里将它的构造函数补全为subType。貌似不做这一步也没啥问题,但是缺了点东西可能会有其他的副作用,所以还是补上
    proto.constructor = subType;

}
function Super() {
    this.superProto = "super proto";
    this.colors = ["red", "yelloy"];
}
function Sub() {
    this.subProto = "sub proto";
    this.name = "sub name";
    //这里还是借用构造函数的套路
    Super.call(this);
}
Super.prototype.getName = function () {
    console.log(this.name);
}
//这里要在定义完Super的属性后执行,因为继承的是超类原型的副本,与Super.prototype是两个对象,在这之后再改变Super.prototype,就已经不会在影响到Sub所继承的副本超类原型对象了
inherProto(Super, Sub);
let a = new Sub();
console.log(a.getName);

29. es6继承

//class 相当于es5中构造函数
//class中定义方法时,前后不能加function,全部定义在class的protopyte属性中
//class中定义的所有方法是不可枚举的
//class中只能定义方法,不能定义对象,变量等
//class和方法内默认都是严格模式
//es5中constructor为隐式属性
class People{
    constructor(name='wang',age='27'){
      this.name = name;
      this.age = age;
    }
    eat(){
      console.log(`${this.name} ${this.age} eat food`)
    }
  }
  //继承父类
  class Woman extends People{ 
     constructor(name = 'ren',age = '27'){ 
       //继承父类属性
       super(name, age); 
     } 
      eat(){ 
       //继承父类方法
        super.eat() 
      } 
  } 
  let wonmanObj=new Woman('xiaoxiami'); 
  wonmanObj.eat();
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Verilog中,撕代码是指在面试或招聘过程中,被要求现场编写Verilog代码来解决某个问题。根据引用\[1\]和引用\[3\]中的描述,设计一个FIFO(First-In-First-Out)缓冲区的Verilog代码可以包括以下几个关键点: 1. 生成读写控制信号(ren和wen):根据外部输入的wr_en和当前FIFO的状态(full和empty),可以使用逻辑运算符来生成读写控制信号。例如,wen的生成可以使用逻辑与运算符和逻辑非运算符,即wen = !full && wr_en。ren的生成类似,即ren = !empty && rd_en。 2. 读地址(raddr)和写地址(waddr)的更新:在时钟上升沿到来时,根据读写使能信号(ren和wen)的有效性,更新读地址和写地址。例如,可以使用always块和if语句来实现写地址的更新,即always @(posedge clk) if (reset) waddr <= 5'b0; else if (wen) waddr <= waddr + 1。 3. 其他功能实现:根据具体需求,还可以添加其他功能,如状态产生、数据存储和读取等。 需要注意的是,以上只是一个简单的示例,实际的Verilog代码可能会更加复杂,具体实现还需要根据具体的需求和设计要求进行调整和完善。 #### 引用[.reference_title] - *1* [秋招撕代码:同步fifo verilog代码及解析](https://blog.csdn.net/qq_38374491/article/details/119393043)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [秋招撕代码:异步fifo(verilog)及代码解析](https://blog.csdn.net/qq_38374491/article/details/119513023)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值