当前位置: 首页 > 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;也可以是独立的上下文菜单。下拉菜单通常作用于当用户单击相应的项目或…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

Mac flutter环境搭建

一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...

【Vue】scoped+组件通信+props校验

【scoped作用及原理】 【作用】 默认写在组件中style的样式会全局生效, 因此很容易造成多个组件之间的样式冲突问题 故而可以给组件加上scoped 属性&#xff0c; 令样式只作用于当前组件的标签 作用&#xff1a;防止不同vue组件样式污染 【原理】 给组件加上scoped 属性后…...

uni-app学习笔记二十七--设置底部菜单TabBar的样式

官方文档地址&#xff1a;uni.setTabBarItem(OBJECT) | uni-app官网 uni.setTabBarItem(OBJECT) 动态设置 tabBar 某一项的内容&#xff0c;通常写在项目的App.vue的onLaunch方法中&#xff0c;用于项目启动时立即执行 重要参数&#xff1a; indexnumber是tabBar 的哪一项&…...

统计按位或能得到最大值的子集数目

我们先来看题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;请你找出 nums 子集 按位或 可能得到的 最大值 &#xff0c;并返回按位或能得到最大值的 不同非空子集的数目 。 如果数组 a 可以由数组 b 删除一些元素&#xff08;或不删除&#xff09;得到&#xff0c;…...

Gerrit+repo管理git仓库,如果本地有新分支不能执行repo sync来同步远程所有修改,会报错

问题&#xff1a;创建一个本地分支TEST 来关联远程已有分支origin/TEST&#xff0c;直接执行repo sync可能会出现问题&#xff1a;比如&#xff0c;本地分支TES会错乱关联到origin/master&#xff0c;或者拉不下最新代码等问题。 // git checkout -b 新分支名 远程分支名字 git…...