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菜单的选项是可选的,它可以是一个下拉的菜单,也可以是独立的上下文菜单。下拉菜单通常作用于当用户单击相应的项目或…...

MySQL-DDL语句
MySQL-DDL语句 数据库操作语句增删数据库查看数据库列表创建数据库进入(使用)数据库/查看当前所在的数据库查看数据库的建库语句查看数据库的编码集和校验集删除数据库修改数据库的编码集查看数据库支持的编码集和校验集 数据库备份备份单个数据库恢复数…...

总结987
考研倒计时102天 时间记录: 6:20起床 7:00~7:40早读,13年tex2 7:50~8:20实验室 8:30~8:34列日计划 8:40~11:18进步本回顾,记录 11:20~12:20计算机网络网课 2:10~3:05计网20道选择题 3:07~4:42政治1000题25道选择题纠错 …...

【服务器 | 测试】如何在centos 7上面安装jmeter
安装之前需要几个环境,以下是列出的几个环境 CentOS 7.7 64位JDK 1.8JMeter 5.2 1. 下载jmeter安装包 JMeter是开源的工具,安装 JMeter 要先安装好 JDK 的环境,安装JDK在前面的文章已经讲到 JMeter最新版下载地址:Apache JMeter…...

20.04部署cartographer
部署cartographer sudo apt-get update sudo apt-get install -y python3-wstool python3-rosdep ninja-build stow下载cartographer新建了一个ws mkdir carto_ws cd carto_ws wstool init src wstool merge -t src https://raw.githubusercontent.com/cartographer-project/…...

djangoMTV初探
1.restful请求方式 一个视图对应多个操作(增删改查) 老的方式 views.py from django.shortcuts import render from django.http import HttpResponse,request,QueryDict, JsonResponse from myapp.models import User from django.views.generi…...

Minecraft--基于云服务器搭建自己的服务器--简易搭建
阿丹: 上一个项目结束了。但是看着自己的服务器想着能不能做点啥子吧。想到了之前和兄弟们玩的麦块。好久没和兄弟们一起玩耍了。怀念之前一起连一个wifi玩我的世界的时候是真快乐。于是尝试自己动手搭建一个我的世界服务器,邀请兄弟们重温一下快乐。 提…...

【数据结构与算法】十大经典排序算法
文章目录 前言一、常见十大排序算法总结1、名词解释2、时间复杂度 二、排序算法与C语言实现1、冒泡排序2、选择排序3、插入排序4、希尔排序5、归并排序6、快速排序7、堆排序8、计数排序9、桶排序10、基数排序 总结 前言 排序算法是《数据结构与算法》中最基本的算法之一。 排序…...

Android 12.0 SystemUI下拉状态栏定制化之隐藏下拉通知栏布局功能实现(一)
1.前言 在12.0的系统定制化开发中,由于从12.0开始SystemUI下拉状态栏和11.0的变化比较大,所以可以说需要从新分析相关的SystemUI的 布局,然后做分析来实现不同的功能,今天就开始实现关于隐藏SystemUI下拉状态栏中的通知栏布局系列一 如图: 2.SystemUI下拉状态栏定制化之…...

665. 非递减数列-先改后验法
665. 非递减数列 给你一个长度为 n 的整数数组 nums ,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列。 我们是这样定义一个非递减数列的: 对于数组中任意的 i (0 < i < n-2),总满足 nums[i] < …...

调教 文心一言 生成 AI绘画 提示词(Midjourney)
文章目录 第一步第二步第三步第四步第五步第六步第七步第八步 文心一言支持连续对话 我瞎玩的非专业哈哈 第一步 你好,今天我们要用扩散模型创建图像。我会给你提供一些信息。行吗? 第二步 这是Midjourney的工作原理:Midjourney是另一个基于ai的工具,能…...