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执行过程:
- 创建一个新对象;
- 新对象的[[prototype]] 特性指向构造函数的prototype属性;
- 构造函数内部的this指向新对象;
- 执行构造函数;
- 如果构造函数返回非空对象,则返回该对象;否则返回新对象;
代码如下:
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手写面试题涵盖了很多不同的方面,从实现一些内置方法到处理异步编程。以下是一些常见的手写面试题:
-
实现instanceof运算符:可以通过检查对象的原型链来判断一个对象是否是某个构造函数的实例。
-
实现new操作符:可以通过创建一个新对象,并将构造函数的原型指向该对象来模拟new操作符的行为。
-
实现bind方法:bind方法可以创建一个新的函数,该函数的this值被绑定到指定的对象。
-
实现call方法:call方法可以调用一个函数并指定this值,以及传递任意数量的参数。
-
函数柯里化:柯里化是一种将多个参数的函数转换成一系列接受一个参数的函数的技术。
-
发布订阅模式:发布订阅是一种消息传递的模式,其中发送者(发布者)不会直接将消息发送给特定的接收者(订阅者),而是通过事件中心来管理消息的传递。
-
深拷贝:深拷贝是指创建一个完全独立的对象,其中包含原始对象的所有属性和嵌套对象的所有属性。
-
实现ES6的Class:ES6的Class用于创建基于类的对象,可以通过构造函数、原型和静态方法实现。
-
实现ES6的继承:ES6的继承可以通过extends关键字和super函数实现。
-
使用reduce实现数组flat方法:reduce方法可以将多维数组降维成一维数组。
-
实现CO(协程):协程是一种支持异步编程的技术,可以通过生成器函数和Promise的组合来实现。
以上是一些常见的JavaScript手写面试题的概述。希望这些信息对你有所帮助。
常见js面试手写题,“老大爷”看了都点赞加收藏。_js 手写面试题-CSDN博客
相关文章:
js手撕代码
1、实现instanceof运算符 instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上,运算符左侧是实例对象,右侧是构造函数。 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程序设计的基本原则
单一职责原则(SRP):一个类应该只有一个原因引起变化,即一个类应该只负责一项职责。 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)
分布式通信框架:让远程方法调用和调用进程内方法一样简单 RPC通信原理 rpc:远程过程调用(远程能够调用其他模块的方法) 在rpc中需要发送时候,对发送的信息进行序列化,在服务端对接收到的信息进行反序列化…...
点云从入门到精通技术详解100篇-基于 RGB 图像与点云融合的三维点云分割算法及成像系统
目录 前言 相机和激光雷达标定研究现状 点云分割算法研究现状...

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

X86_64函数调用汇编程序分(2)
X86_64函数调用汇编程序分(2) 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(解决父传子动态绑定问题)
在父组件往子组件传值,子组件中要显示父组件的信息,首先是在网上搜的watch 来监听组组件的props,但是父组件只传一次,后续再更改就没了,所以我用的$refs props:{params:{type:Object;defult():{return {} } } }watch:{params: {/…...

vscode-server
1know_host清除 2 删除服务器里的home/user/.vscode-server(不是根root下的vscode-server),删除时用户名保持一致。 3 ssh配置文件 /etc/ssh/sshd_config[想改变,使用root,修改文件权限] 4 删除修改后,重启Windows下…...
ubuntu 20.04安装开发环境总结_安装python
Ubuntu 20.04 是一款主要面向开发人员的操作系统之一,与此同时,它还支持多种开发环境和工具的使用。但是因为对市面上各种软件的支持没有window那样友好,所以对ubuntu系统安装配置各种环境的问题做了个总结 安装 PyCharm: 可以从…...
尚硅谷_宋红康_IntelliJ IDEA 常用快捷键一览表
1-IDEA的日常快捷键 第1组:通用型 说明快捷键复制代码-copyctrl c粘贴-pastectrl v剪切-cutctrl x撤销-undoctrl z反撤销-redoctrl shift z保存-save allctrl s全选-select allctrl a 第2组:提高编写速度(上) 说明快捷…...
Java设计模式之建造者模式详解(Builder Pattern)
在日常的开发工作中,我们常常需要创建一些复杂的对象。这些对象可能包含许多不同的属性,并且这些属性的初始化过程可能相当复杂。在这种情况下,建造者模式是一种非常有用的设计模式,因为它允许我们分步骤地创建复杂的对象。 概念和…...

TCP的滑动窗口与拥塞控制
客户端每发送的一个包,服务器端都应该有个回复,如果服务器端超过一定的时间没有回复,客户端就会重新发送这个包,直到有回复。 为了保证顺序性,每一个包都有一个 ID。在建立连接的时候,会商定起始的 ID 是什…...
MySQL更新语句执行过程
执行流程 update t set name XXX where id 1; 加载id1的记录所在的整页数据到缓存池;旧值写入undolog便于回滚;更新内存数据;写redo log到RedoBuff;redo log顺序写入磁盘,准备提交事务(prepare阶段&…...

Matlab图像处理-彩色图像基础
彩色的物理认识 人类能够感知的物体的颜色是由物体反射的光的性质决定的。如图8-2所示,可见光是由电磁波谱中较窄的波段组成。 如果物体反射的光在所有可见光波长范围内都是平衡的,那么从观察者的角度来看,它是白色的; 如果物体…...
MATLAB算法实战应用案例精讲-【数模应用】数据中台
目录 前言 几个高频面试题目 数据中台、数仓、大数据平台的区别 1)数据中台VS数据仓库...
el-form动态检验无法生效问题(已解决)
要对el-form里面的字段动态生成校验规则,测试了一系列的骚操作也无法生效,要么是require视图生效了,校验规则还是不生效;看了csdn里面好多方案,都是废话,废话,直接上硬货,最终总结如下ÿ…...

【python】代码学习过程问题总结
目录 1. 使用 conda 创建并进入虚拟环境 2. pycharm 选择 interpreter 的时候,在虚拟环境中找不到 python.exe 3.(py & python)ModuleNotFoundError: No module named XXX 4. AttributeError: module ‘tensorflow‘ has no attribu…...

Qt应用开发(基础篇)——菜单 QMenu
一、前言 QMenu类继承于QWidget,它提供了一个菜单样式的小部件,用于菜单栏、上下文菜单和一些弹出式菜单。 QMenu菜单的选项是可选的,它可以是一个下拉的菜单,也可以是独立的上下文菜单。下拉菜单通常作用于当用户单击相应的项目或…...

【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...

elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...

C++实现分布式网络通信框架RPC(2)——rpc发布端
有了上篇文章的项目的基本知识的了解,现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...

热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...