当前位置: 首页 > news >正文

js手撕代码

1、实现instanceof运算符

instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上,运算符左侧是实例对象,右侧是构造函数。

const isInstanceof = function(left,right){let proto = Object.getPrototypeOf(left);while(true){if(proto === null) return false;if(proto === right.prototype) return true;proto = Object.getPrototypeOf(proto);}
};
// Object.getPrototypeOf(obj1)方法返回指定对象obj1的原型,如果没有继承属性,则返回null。

2、实现new操作符

new执行过程:

  1. 创建一个新对象;
  2. 新对象的[[prototype]] 特性指向构造函数的prototype属性;
  3. 构造函数内部的this指向新对象;
  4. 执行构造函数;
  5. 如果构造函数返回非空对象,则返回该对象;否则返回新对象;

代码如下:

const isNew = function(fn,...arg){let instance = Object.create(fn.prototype);let res = fn.apply(instance,arg);return res !== null && (typeof res ==='Object'||typeof res==='Function') ? res:instance;
}

3、实现bind方法

改变函数内的this的值并传参,返回一个函数。

const iSBind = function(thisObj,...args) {const originFunc = this;const boundFunc = function(...args1){// 解决bind之后对返回函数new的问题if(new.target){if(originFunc.prototype){bounfFunc.prototype = originFunc.prototype;}const res = originFunc.apply(this,args.concat(args1));return res !== null && (typeof res ==='object'||typeof res === 'function')?res:this;}else{return originFunc.apply(thisObj,args.concat(args1));}};//解决length 和name属性的问题const desc = Object.getOwnPropertyDescriptors(originFunc);Object.defineProperties(boundFunc,{length:Object.assign(desc.length,{value:desc.length<args.length?0:(desc.length-args.length)}),name:Object.assign(desc.name,{value:`bound${desc.name.value}`})		});return boundFunc;
}
// 保持bind的数据属性一致
Object.defineProperty(Function.prototype,'isBind',{value:isBind,enumerable:false,configurable:true,writable:true
})

实现函数的bind方法核心是利用call绑定this的指向,同时考虑了一些其它的情况,例如:

bind返回的函数被new调用作为构造函数时,绑定的值会失效并且改为new指定的对象
定义了绑定后函数的length属性和name属性(不可枚举性)
绑定后函数的prototype需指向原函数的prototype(真实情况中绑定后的函数是没有prototype的,取而代之在绑定后的函数中有个内部属性[[TargetFunction]]保存原函数,当将绑定后的函数作为构造函数时,将创建的实例的__proto__指向[[TargetFunction]]的prototype,这里无法模拟内部属性,所以直接声明了一个prototype属性)

4、实现call方法

用指定的this值和参数来调用函数

const isCall = function(thisObj,...args){thisObj=(thisObj === undefined || thisObj === null)?window:Object(thisObj);let fn = Symbol('fn');thisObj[fn] = this;let res = thisObj[fn](...args);delete thisObj[fn];return res;
}
// 保持call的数据属性一致
Object.defineProperty(Function.prototype,'isCall',{value:isCall,enumerable:false,configurable:true,writable:true,
});

原理就是将函数作为传入的上下文参数(context)的属性执行,这里为了防止属性冲突使用了ES6的Symbol类型

5、函数柯里化

将一个多参数函数转化为多个嵌套的单参数函数。

const curry = function(targetFn) {return function fn(...rest){if(targetFn.length === rest.length) {return targetFn.apply(null,rest);}else{return fn.bind(null,...rest);}};
};
// 用法
function add(a,b,c,d){return a*b*c*d
}
console.log('柯里化:',curry(add)(1)(2)(3)(4))

6、发布订阅

class EventBus {constructor() {Object.defineProperty(this,'handles',{value:{}});}on(eventName,listener) {if(typeof listener !=='function') {console.error('请传入正确的回调函数');return;}if(!this.handles[eventName]) {this.handles[eventName] = [];}this.handles[eventName].push(listener);}emit(eventName,...args) {let listeners = this.handles[eventName];if(!listeners) {console.warn(`${eventName}事件不存在`);return;}for(const listener of listeners) {listener(...args);}}off(eventName,listener) {if(!listener) {delete this.handles[eventName];return;}let listeners = this.handles[eventName];if(listeners $$ listeners.length) {let index =listeners.findIndex(item => item === listener);listeners.splice(index,1);}}once(eventName,listener){if(typeof listener !=='function') {console.error('请传入正确的回调函数');return ;}const onceListener = (...args) =>{listener(...args);this.off(eventName,listener);};this.on(eventName,onceListener);}
}

自定义事件的时候用到,注意一些边界的检查

7、深拷贝

const deeoClone = function(source) {if(source === null || typeof source !=='object') {return source;}let res = Array.isArray(source) ? []:{};for(const key in source) {if(source.hansOwnProperty(key)) {res[key] = deepClone(source[key]);}}return res;
}
// 以上这个是深拷贝很基础的版本,但存在一些问题,例如循环引用,递归爆栈。以下这个为进阶版的。const deepClone1 = function (obj) {let cloneObj;if( obj && typeof obj !== 'object'){cloneObj = obj;}else if(obj && typeof obj ==='object'){cloneObj = Array.isArray(obj) ? []:{};for(let key in obj){if(obj.hasOwnProperty(key)){if(obj[key] && typeof obj[key] == 'object'){cloneObj[key] = deepClone1(obj[key]);}else{cloneObj[key] = obj[key];}}}}return cloneObj;
}

8、实现ES6的Class

用构造函数模拟,class只能用new创建,不可以直接调用,另外注意以下属性的描述符

const checkNew = function(instance,con) {if(!(instance instanceof con)){throw new TypeError(`Class constructor${con.name} connot be invoked without 'new'`);}
};
const defineProperties = function(target,obj) {for(const key in obj){Object.defineProperty(target,key,{value:obj[key],enumerable:false,configurable:true,writable:true,}); }
}
const createClass = function(con,proto,staticAttr){proto && defineProperties(con.prototype,proto);staticAttr && defineProperties(con,staticAttr);return con;
}
// 用法
function Person(name) {checkNew(this,Person);this.name = name;
}
var PersonClass = createClass(Person,{getName:function(){return this.name;}getAge:function(){}
})

9、实现ES6的继承

ES6内部使用寄生组合式继承,首先用Object.create继承原型,并传递第二个参数以将父类构造函数指向自身,同时设置数据属性描述符。然后用Object.setPrototypeOf继承静态属性和静态方法。

const inherit = function(subType,superType){// 对superType进行类型判断if(typeof superType !== 'function' && superType !== null){throw new TypeError("Super expression must either be null or a function");}subType.prototype = Object.create(superType && superType.prototype,{constructor:{value:subType,enumerable:false,configurable:true,writable:true}});// 继承静态方法superType && Object.setPrototypeOf(subType,superType);
}
// 用法
function superType(name) {this.name = name;
}
superType.staticFn = function(){console.log('这是staticFn');
}
superType.prototype.getName = function(){console.log('name:'+this.name);
}
function subType(name,age){superType.call('name:'+this.name);this.age = age;
}
inherit(subType,superType);
// 必须在继承之后再往subType中添加原型方法,否则会被覆盖掉
subType.prototype.getAge = function(){console.log('age:'+this.age);
}
let subTypeInstance = new subType('Twittytop',30);
subType.staticFn();
subTypeInstance.getName();
subTypeInstance.getAge();

10、使用reduce实现数组flat方法

const selfFlat = function (depth = 1){let arr = Array.prototype.slice.call(this);if(depth === 0) return arr;return arr.reduce((pre,cur) => {if(Array.isArray(cur)) {return [...pre,...selfFlat.call(cur,depth - 1)]} else {return [...pro,cur]}},[])
}

因为selfFlat是依赖this指向的,所以在reduce遍历时需要指定selfFlat的this指向,否则会默认指向window从而发生错误。
原理通过reduce遍历数组,遇到数组的某个元素扔是数组时,通过ES6的扩展运算符对其进行降维(ES5可以使用concat方法),而这个数组元素可能内部还嵌套数组,所以需要递归调用selfFlat。
同时原生的flat方法支持一个depth参数表示降维的深度,默认为1即给数组降一层维

11、CO(协成)实现

function co(gen) {return new Promise(function(resolve,reject) {if( typeof gen ==='function') gen =gen();if(!gen||typeof gen.next !=='function') return resolve(gen);onFulfilled();function onFulfilled(res) {let ret;try {ret = gen.next(res);} catch(e){return reject(e)}next(ret);}function onRejected(err) {let ret;try{ret = gen.throw(err);} catch(e){return reject(e)}next(ret);}function next(ret) {if(ret.done) return resolve(ret.value);let val = Promise.resolve(ret.value);return val.then(onFulfilled,onRejected);}})
}

使用方法:

co(function*() {let res1 = yield Promise.resolve(1);console.log(res1);let res2 = yield Promise.resolve(2);console.log(res2);let res3 = yield Promise.resolve(3);console.log(res3)return res1+res2+res3;
}).then(val =>{console.log('add:'+val);
},function(err){console.error(err.stack);
})

co接收一个生成器函数,当遇到yield时就暂停执行,交出控制权,当其他程序执行完毕后,将结果返回并从中断的地方继续执行,如此往复,一直到所有的任务都执行完毕,最后返回一个Promise并将生成器函数的返回值作为resolve值。

我们将*换成async,将yield换成await时,就和我们经常用到的async/await是一样的,所以说async/await是生成器函数的语法糖。
 

JavaScript手写面试题涵盖了很多不同的方面,从实现一些内置方法到处理异步编程。以下是一些常见的手写面试题:

  1. 实现instanceof运算符:可以通过检查对象的原型链来判断一个对象是否是某个构造函数的实例。

  2. 实现new操作符:可以通过创建一个新对象,并将构造函数的原型指向该对象来模拟new操作符的行为。

  3. 实现bind方法:bind方法可以创建一个新的函数,该函数的this值被绑定到指定的对象。

  4. 实现call方法:call方法可以调用一个函数并指定this值,以及传递任意数量的参数。

  5. 函数柯里化:柯里化是一种将多个参数的函数转换成一系列接受一个参数的函数的技术。

  6. 发布订阅模式:发布订阅是一种消息传递的模式,其中发送者(发布者)不会直接将消息发送给特定的接收者(订阅者),而是通过事件中心来管理消息的传递。

  7. 深拷贝:深拷贝是指创建一个完全独立的对象,其中包含原始对象的所有属性和嵌套对象的所有属性。

  8. 实现ES6的Class:ES6的Class用于创建基于类的对象,可以通过构造函数、原型和静态方法实现。

  9. 实现ES6的继承:ES6的继承可以通过extends关键字和super函数实现。

  10. 使用reduce实现数组flat方法:reduce方法可以将多维数组降维成一维数组。

  11. 实现CO(协程):协程是一种支持异步编程的技术,可以通过生成器函数和Promise的组合来实现。

以上是一些常见的JavaScript手写面试题的概述。希望这些信息对你有所帮助。

常见js面试手写题,“老大爷”看了都点赞加收藏。_js 手写面试题-CSDN博客 

相关文章:

js手撕代码

1、实现instanceof运算符 instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上&#xff0c;运算符左侧是实例对象&#xff0c;右侧是构造函数。 const isInstanceof function(left,right){let proto Object.getPrototypeOf(left);while(true…...

typecho反序列化

typecho反序列化 环境的搭建 漏洞复现前提 <?php class Typecho_Feed {const RSS1 RSS 1.0;const RSS2 RSS 2.0;const ATOM1 ATOM 1.0;const DATE_RFC822 r;const DATE_W3CDTF c;const EOL "\n";private $_type;private $_items;public function __const…...

php程序设计的基本原则

单一职责原则&#xff08;SRP&#xff09;&#xff1a;一个类应该只有一个原因引起变化&#xff0c;即一个类应该只负责一项职责。 class User {private $name;private $email;public function __construct($name, $email) {$this->name $name;$this->email $email;}p…...

python execute() 使用%s 拼接sql 避免sql注入攻击 好于.format

1 execute(参数一:sql 语句) # 锁定当前查询结果行 cursor.execute("SELECT high, low, vol FROM table_name WHERE symbol %s FOR UPDATE;"% (symbol,)) 2 .format() cursor.execute("SELECT high, low, vol FROM table_name WHERE symbol {} FOR UPDATE;…...

RPC项目解析(1)

分布式通信框架&#xff1a;让远程方法调用和调用进程内方法一样简单 RPC通信原理 rpc&#xff1a;远程过程调用&#xff08;远程能够调用其他模块的方法&#xff09; 在rpc中需要发送时候&#xff0c;对发送的信息进行序列化&#xff0c;在服务端对接收到的信息进行反序列化…...

点云从入门到精通技术详解100篇-基于 RGB 图像与点云融合的三维点云分割算法及成像系统

目录 前言 相机和激光雷达标定研究现状 点云分割算法研究现状...

JDK8新特性

Lembda表达式 lembda表达式是一个简洁、可传递的匿名函数,实现了把代码块赋值给一个变量的功能 是我认为jdk1.8中最让人眼前一亮的特性&#xff08;我没用过其他函数式的语言&#xff09; 在了解表达式之前&#xff0c;我们先看两个概念 函数式接口 含有且仅含有一个抽象方法&…...

X86_64函数调用汇编程序分(2)

X86_64函数调用汇编程序分&#xff08;2&#xff09; 1 X86_64寄存器使用标准2 leaveq和retq指令2.1 leaveq2.2 retq 3 执行leaveq和retq之后栈的结构3.1 执行leaveq之后栈的结构3.1.1 test_fun_b函数执行leaveq之前的栈结构示意图3.1.2 test_fun_b函数执行leaveq之后的栈结构示…...

组件传值之ref(解决父传子动态绑定问题)

在父组件往子组件传值&#xff0c;子组件中要显示父组件的信息&#xff0c;首先是在网上搜的watch 来监听组组件的props,但是父组件只传一次&#xff0c;后续再更改就没了&#xff0c;所以我用的$refs props:{params:{type:Object;defult():{return {} } } }watch:{params: {/…...

vscode-server

1know_host清除 2 删除服务器里的home/user/.vscode-server&#xff08;不是根root下的vscode-server&#xff09;&#xff0c;删除时用户名保持一致。 3 ssh配置文件 /etc/ssh/sshd_config[想改变,使用root&#xff0c;修改文件权限] 4 删除修改后&#xff0c;重启Windows下…...

ubuntu 20.04安装开发环境总结_安装python

Ubuntu 20.04 是一款主要面向开发人员的操作系统之一&#xff0c;与此同时&#xff0c;它还支持多种开发环境和工具的使用。但是因为对市面上各种软件的支持没有window那样友好&#xff0c;所以对ubuntu系统安装配置各种环境的问题做了个总结 安装 PyCharm&#xff1a; 可以从…...

尚硅谷_宋红康_IntelliJ IDEA 常用快捷键一览表

1-IDEA的日常快捷键 第1组&#xff1a;通用型 说明快捷键复制代码-copyctrl c粘贴-pastectrl v剪切-cutctrl x撤销-undoctrl z反撤销-redoctrl shift z保存-save allctrl s全选-select allctrl a 第2组&#xff1a;提高编写速度&#xff08;上&#xff09; 说明快捷…...

Java设计模式之建造者模式详解(Builder Pattern)

在日常的开发工作中&#xff0c;我们常常需要创建一些复杂的对象。这些对象可能包含许多不同的属性&#xff0c;并且这些属性的初始化过程可能相当复杂。在这种情况下&#xff0c;建造者模式是一种非常有用的设计模式&#xff0c;因为它允许我们分步骤地创建复杂的对象。 概念和…...

TCP的滑动窗口与拥塞控制

客户端每发送的一个包&#xff0c;服务器端都应该有个回复&#xff0c;如果服务器端超过一定的时间没有回复&#xff0c;客户端就会重新发送这个包&#xff0c;直到有回复。 为了保证顺序性&#xff0c;每一个包都有一个 ID。在建立连接的时候&#xff0c;会商定起始的 ID 是什…...

MySQL更新语句执行过程

执行流程 update t set name XXX where id 1; 加载id1的记录所在的整页数据到缓存池&#xff1b;旧值写入undolog便于回滚&#xff1b;更新内存数据&#xff1b;写redo log到RedoBuff&#xff1b;redo log顺序写入磁盘&#xff0c;准备提交事务&#xff08;prepare阶段&…...

Matlab图像处理-彩色图像基础

彩色的物理认识 人类能够感知的物体的颜色是由物体反射的光的性质决定的。如图8-2所示&#xff0c;可见光是由电磁波谱中较窄的波段组成。 如果物体反射的光在所有可见光波长范围内都是平衡的&#xff0c;那么从观察者的角度来看&#xff0c;它是白色的&#xff1b; 如果物体…...

MATLAB算法实战应用案例精讲-【数模应用】数据中台

目录 前言 几个高频面试题目 数据中台、数仓、大数据平台的区别 1)数据中台VS数据仓库...

el-form动态检验无法生效问题(已解决)

要对el-form里面的字段动态生成校验规则&#xff0c;测试了一系列的骚操作也无法生效&#xff0c;要么是require视图生效了&#xff0c;校验规则还是不生效;看了csdn里面好多方案&#xff0c;都是废话&#xff0c;废话&#xff0c;直接上硬货&#xff0c;最终总结如下&#xff…...

【python】代码学习过程问题总结

目录 1. 使用 conda 创建并进入虚拟环境 2. pycharm 选择 interpreter 的时候&#xff0c;在虚拟环境中找不到 python.exe 3.&#xff08;py & python&#xff09;ModuleNotFoundError: No module named XXX 4. AttributeError: module ‘tensorflow‘ has no attribu…...

Qt应用开发(基础篇)——菜单 QMenu

一、前言 QMenu类继承于QWidget&#xff0c;它提供了一个菜单样式的小部件&#xff0c;用于菜单栏、上下文菜单和一些弹出式菜单。 QMenu菜单的选项是可选的&#xff0c;它可以是一个下拉的菜单&#xff0c;也可以是独立的上下文菜单。下拉菜单通常作用于当用户单击相应的项目或…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言&#xff1a; 在Java编程中&#xff0c;类的生命周期是指类从被加载到内存中开始&#xff0c;到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期&#xff0c;让读者对此有深刻印象。 目录 ​…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...

API网关Kong的鉴权与限流:高并发场景下的核心实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中&#xff0c;API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关&#xff0c;Kong凭借其插件化架构…...