手写题目整理
new
function myNew(fn,...args){
let obj=Object.create(fn.prototype) //这里已经创建了里面的函数
fn.call(obj,...args) //初始化
return obj
}
用法如下:
// // function Person(name, age) {
// // this.name = name;
// // this.age = age;
// // }
// // Person.prototype.say = function() {
// // console.log(this.age);
// // };
// // let p1 = myNew(Person, "lihua", 18);
// // console.log(p1.name);
// // console.log(p1);
// // p1.say();
call,apply,bind
Function.prototype.myCall = function(context, ...args) {
const fn = this;
context = context || window;
context.fn=fn
const result = context.fn(...args);
delete context.fn;
return result;
};
function greet(message) {
console.log(`${message}, ${this.name}!`);
}
const person = { name: "Alice" };
greet.myCall(person, "Hello");
Function.prototype.myApply = function(context, args) {
const fn = this;
context = context || window;
context.fn=fn
const result = context.fn(...args);
delete context.fn;
return result;
};
function greet(message) {
console.log(`${message}, ${this.name}!`);
}
const person = { name: "Alice" };
greet.myApply(person, ["Hello","world"]);
Function.prototype.myBind=function(){
const args=[...arguments]
const mythis=args.shift()
const self=this
return function(){
return self.apply(mythis,[...args,...arguments])
}
}
function greet(message) {
console.log(`${message}, ${this.name}!`);
}
const person = { name: "Alice" };
greet.myBind(person,"Hello")();
instanceof
function myInstanceof(left, right) {
let proto = Object.getPrototypeOf(left),
prototype = right.prototype;
while (true) {
if (!proto) return false;
if (proto === prototype) return true;
proto = Object.getPrototypeOf(proto);
}
}
Object.is
Object.is = function (x, y) {
if (x === y) {
// 当前情况下,只有一种情况是特殊的,即 +0 -0
// 如果 x !== 0,则返回true
// 如果 x === 0,则需要判断+0和-0,则可以直接使用 1/+0 === Infinity 和 1/-0 === -Infinity来进行判断
return x !== 0 || 1 / x === 1 / y
}
// x !== y 的情况下,只需要判断是否为NaN,如果x!==x,则说明x是NaN,同理y也一样
// x和y同时为NaN时,返回true
return x !== x && y !== y
}
大数相加
function add(a ,b){
//取两个数字的最大长度
let maxLength = Math.max(a.length, b.length)
//用0去补齐长度
a = a.padStart(maxLength , 0)
b = b.padStart(maxLength , 0)
//定义加法过程中需要用到的变量
let t = 0
let f = 0
let sum = ""
for(let i=maxLength-1
t = parseInt(a[i]) + parseInt(b[i]) + f
f = Math.floor(t/10)
sum = t%10 + sum
}
if(f!==0){
sum = '' + f + sum
}
return sum
}
//调用
let a = "9007199254740991"
let b = "1234567899999999999"
let sum = add(a,b)
深拷贝
function deepClone(obj){
if(typeof obj!=='object' || obj===null){
return obj
}
let result
if(obj instanceof Array){
result=[]
}else{
result={}
}
for(let key in obj){
if(obj.hasOwnProperty(key)){
result[key]=deepClone(obj[key])
}
}
}
JSON.parse(JSON.stringify(obj))
深度比较
function isObject(obj){
return typeof obj === 'object' && obj !== null
}
function isEqual(obj1,obj2){
if(!isObject(obj) || !isObject(obj2)){
return obj1 ===obj2
}
if(obj1 === obj2){
return true
}
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if(obj1Keys.length !== obj2Keys.length){
return false
}
for(let key in obj1){
const res =isEqual(obj1[key],obj2[key])
if(!res){
return false
}
}
return true
}
函数柯里化
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn(...args);
} else {
return function (...moreArgs) {
return curried(...args, ...moreArgs);
};
}
};
}
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
const result1 = curriedAdd(1)(2)(3);
const result2 = curriedAdd(1, 2)(3);
const result3 = curriedAdd(1)(2, 3);
节流防抖
function throttle(func, delay) {
let lastTime = 0;
return function(...args) {
const currentTime = Date.now();
if (currentTime - lastTime >= delay) {
func.apply(this, args);
lastTime = currentTime;
}
};
}
function doSomething() {
console.log('Doing something...');
}
const throttledFunction = throttle(doSomething, 1000);
document.addEventListener('scroll', throttledFunction);
function debounce(func, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
function doSomething() {
console.log('Doing something...');
}
const debouncedFunction = debounce(doSomething, 1000);
window.addEventListener('scroll', debouncedFunction);
settimeout 模拟实现 setinterval
function simulateInterval(func, delay) {
let timer=null
function interval() {
func();
setTimeout(interval, delay);
}
timer=setTimeout(interval, delay);
return {
cancel:()=>clearTimeout(timer)
}
}
let a=simulateInterval(function() {
console.log("This will run every 1000ms");
}, 1000);
a.cancel()
原生 XMLHttpRequest
const SERVER_URL = "/server";
let xhr = new XMLHttpRequest();
xhr.open("GET", SERVER_URL, true);
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
if (this.status === 200) {
handle(this.response);
} else {
console.error(this.statusText);
}
};
xhr.onerror = function() {
console.error(this.statusText);
};
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
xhr.send(null);
Ajax
const getJSON = function (url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url, false);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4) return;
if (xhr.status === 200 || xhr.status === 304) {
resolve(xhr.responseText);
} else {
reject(new Error(xhr.responseText));
}
};
xhr.send();
});
};
订阅发布模式
function Publisher() {
this.subscribers = [];
}
Publisher.prototype.subscribe = function (subscriber) {
this.subscribers.push(subscriber);
};
Publisher.prototype.unsubscribe = function (subscriber) {
const index = this.subscribers.indexOf(subscriber);
if (index !== -1) {
this.subscribers.splice(index, 1);
}
};
Publisher.prototype.notify = function (message) {
this.subscribers.forEach(function (subscriber) {
subscriber.update(message);
});
};
function Subscriber(name) {
this.name = name;
}
Subscriber.prototype.update = function (message) {
console.log(`${this.name} 收到消息:${message}`);
};
const newsPublisher = new Publisher();
const subscriber1 = new Subscriber('订阅者1');
const subscriber2 = new Subscriber('订阅者2');
newsPublisher.subscribe(subscriber1);
newsPublisher.subscribe(subscriber2);
newsPublisher.notify('新闻更新:重要通知!');
jsonp
function addScript(src) {
const script = document.createElement('script');
script.src = src;
script.type = "text/javascript";
document.body.appendChild(script);
}
addScript("http://xxx.xxx.com/xxx.js?callback=handleRes");
function handleRes(res) {
console.log(res);
}
是否有循环引用
const isCycleObject = (obj, parent) => {
// parent 参数用于追踪当前对象的祖先对象,初始值为 [obj],用于存储对象的引用链
const parentArr = parent || [obj]
// 遍历对象的属性
for (let i in obj) {
if (typeof obj[i] === 'object') {
let flag = false
// 检查当前属性值是否已经在祖先链中存在
parentArr.forEach((pObj) => {
if (pObj === obj[i]) {
flag = true
}
})
// 如果发现当前属性值在祖先链中存在,返回 true 表示存在循环引用
if (flag) return true
// 递归调用 isCycleObject 检查当前属性值的子属性,传递更新的祖先链
flag = isCycleObject(obj[i], [...parentArr, obj[i]])
// 如果在子属性中发现循环引用,返回 true
if (flag) return true
}
}
// 如果没有发现循环引用,返回 false
return false
}
//调用
// 创建一个对象 person1
const person1 = {
name: "Alice"
}
// 创建另一个对象 person2 并将 person1 作为属性引用
const person2 = {
name: "Bob",
friend: person1
}
// 将 person2 作为 person1 的 friend 属性引用
person1.friend = person2
// 现在,person1 和 person2 彼此引用,形成了循环引用
console.log(isCycleObject(person1))
继承手写
function Parent(name) {
this.name = name
this.say = () => {
console.log(111)
}
}
Parent.prototype.play = () => {
console.log(222)
}
function Children(name) {
Parent.call(this)
this.name = name
}
Children.prototype = Object.create(Parent.prototype)
Children.prototype.constructor = Children
// let child = new Children("111")
// // console.log(child.name)
// // child.say()
// // child.play()
数组
数组扁平化
function flatter(arr) {
if (!arr.length) return;
return arr.reduce(
(pre, cur) => {
Array.isArray(cur) ? [...pre, ...flatter(cur)] : [...pre, cur]
},[]
);
}
arr.flat(Infinity)
对象 flatten
const obj = {
a: {
b: 1,
c: 2,
d: {e: 5}
},
b: [1, 3, {a: 2, b: 3}],
c: 3
}
flatten(obj) 结果返回如下
// {
// 'a.b': 1,
// 'a.c': 2,
// 'a.d.e': 5,
// 'b[0]': 1,
// 'b[1]': 3,
// 'b[2].a': 2,
// 'b[2].b': 3
// c: 3
// }
function isObject(val) {
return typeof val === "object" && val !== null;
}
function flatten(obj) {
if (!isObject(obj)) {
return;
}
let res = {};
const dfs = (cur, prefix) => {
if (isObject(cur)) {
if (Array.isArray(cur)) {
cur.forEach((item, index) => {
dfs(item, `${prefix}[${index}]`);
});
} else {
for (let k in cur) {
dfs(cur[k], `${prefix}${prefix ? "." : ""}${k}`);
}
}
} else {
res[prefix] = cur;
}
};
dfs(obj, "");
return res;
}
flatten();
数组去重
const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8]
let res = Array.from(new Set(array))
列表转成树形结构
function listToTree(data) {
let temp = {}
let treeData = []
for (let i = 0
temp[data[i].id] = data[i]
}
for (let i in temp) {
if (+temp[i].parentId != 0) {
if (!temp[temp[i].parentId].children) {
temp[temp[i].parentId].children = []
}
temp[temp[i].parentId].children.push(temp[i])
} else {
treeData.push(temp[i])
}
}
return treeData
}
[
{
id: 1,
text: '节点1',
parentId: 0 //这里用0表示为顶级节点
},
{
id: 2,
text: '节点1_1',
parentId: 1 //通过这个字段来确定子父级
}
...
]
转成
[
{
id: 1,
text: '节点1',
parentId: 0,
children: [
{
id:2,
text: '节点1_1',
parentId:1
}
]
}
]
树形结构转成列表
function treeToList(data) {
let res = []
const dfs = (tree) => {
tree.forEach((item) => {
if (item.children) {
dfs(item.children)
delete item.children
}
res.push(item)
})
}
dfs(data)
return res
}
[
{
id: 1,
text: '节点1',
parentId: 0,
children: [
{
id:2,
text: '节点1_1',
parentId:1
}
]
}
]
转成
[
{
id: 1,
text: '节点1',
parentId: 0 //这里用0表示为顶级节点
},
{
id: 2,
text: '节点1_1',
parentId: 1 //通过这个字段来确定子父级
}
...
]
排序
冒泡排序
function bubbleSort(arr) {
// 缓存数组长度
const len = arr.length
// 外层循环用于控制从头到尾的比较+交换到底有多少轮
for (let i = 0
// 内层循环用于完成每一轮遍历过程中的重复比较+交换
for (let j = 0
// 若相邻元素前面的数比后面的大
if (arr[j] > arr[j + 1]) {
// 交换两者
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
}
}
}
// 返回数组
return arr
}
// console.log(bubbleSort([3, 6, 2, 4, 1]))
选择排序
function selectSort(arr) {
// 缓存数组长度
const len = arr.length
// 定义 minIndex,缓存当前区间最小值的索引,注意是索引
let minIndex
// i 是当前排序区间的起点
for (let i = 0
// 初始化 minIndex 为当前区间第一个元素
minIndex = i
// i、j分别定义当前区间的上下界,i是左边界,j是右边界
for (let j = i
// 若 j 处的数据项比当前最小值还要小,则更新最小值索引为 j
if (arr[j] < arr[minIndex]) {
minIndex = j
}
}
// 如果 minIndex 对应元素不是目前的头部元素,则交换两者
if (minIndex !== i) {
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]
}
}
return arr
}
// console.log(quickSort([3, 6, 2, 4, 1]))
插入排序
function insertSort(arr) {
for (let i = 1
let j = i
let target = arr[j]
while (j > 0 && arr[j - 1] > target) {
arr[j] = arr[j - 1]
j--
}
arr[j] = target
}
return arr
}
// console.log(insertSort([3, 6, 2, 4, 1]))
快排
function quickSort(arr) {
if (arr.length < 2) {
return arr
}
const cur = arr[arr.length - 1]
const left = arr.filter((v, i) => v <= cur && i !== arr.length - 1)
const right = arr.filter((v) => v > cur)
return [...quickSort(left), cur, ...quickSort(right)]
}
// console.log(quickSort([3, 6, 2, 4, 1]))
归并排序
function merge(left, right) {
let res = []
let i = 0
let j = 0
while (i < left.length && j < right.length) {
if (left[i] < right[j]) {
res.push(left[i])
i++
} else {
res.push(right[j])
j++
}
}
if (i < left.length) {
res.push(...left.slice(i))
} else {
res.push(...right.slice(j))
}
return res
}
function mergeSort(arr) {
if (arr.length < 2) {
return arr
}
const mid = Math.floor(arr.length / 2)
const left = mergeSort(arr.slice(0, mid))
const right = mergeSort(arr.slice(mid))
return merge(left, right)
}
// console.log(mergeSort([3, 6, 2, 4, 1]))
Promise
手写Promise
class myPromise{
static PENDING="待定"; static RESOLVED="解决"; static REJECTED="拒绝"
constructor(fn){
this.state=myPromise.PENDING
this.result=null
try{
fn(this.resolve.bind(this),this.reject.bind(this))
}catch(error){
this.state=myPromise.REJECTED
this.result=error
}
}
resolve(result){
if(this.state===myPromise.PENDING){
this.result=result
this.state=myPromise.RESOLVED
}
}
reject(result){
if(this.state===myPromise.PENDING){
this.result=result
this.state=myPromise.REJECTED
}
}
then(fn_success,fn_fail){
return new myPromise((resolve,reject)=>{
setTimeout(()=>{
fn_success= fn_success instanceof Function ? fn_success:()=>{}
fn_fail= fn_fail instanceof Function ? fn_fail:()=>{}
if(this.state===myPromise.RESOLVED){
fn_success(this.result)
}
if(this.state===myPromise.REJECTED){
fn_fail(this.result)
}
resolve(this.result)
},0)
})
}
}
console.log("第一步")
let p=new Promise((resolve,reject)=>{
console.log("第二步")
resolve()
}).then((res)=>{
console.log("第四步")
})
console.log("第三步")
console.log("第一步111")
let p1=new myPromise((resolve,reject)=>{
console.log("第二步111")
resolve()
}).then(res=>{
console.log("第四步111")
},err=>{
console.log(err)
}).then(()=>{
console.log("链式调用")
})
console.log("第三步111")
Promise.all
function promiseAll(promises) {
return new Promise(function(resolve, reject) {
if(!Array.isArray(promises)){
throw new TypeError(`argument must be a array`)
}
var resolvedCounter = 0;
var promiseNum = promises.length;
var resolvedResult = [];
for (let i = 0; i < promiseNum; i++) {
Promise.resolve(promises[i]).then(value=>{
resolvedCounter++;
resolvedResult[i] = value;
if (resolvedCounter == promiseNum) {
return resolve(resolvedResult)
}
},error=>{
return reject(error)
})
}
})
}
let p1 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(1)
}, 1000)
})
let p2 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(2)
}, 2000)
})
let p3 = new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(3)
}, 3000)
})
promiseAll([p3, p1, p2]).then(res => {
console.log(res)
})
Promise.race
Promise.race = function (args) {
return new Promise((resolve, reject) => {
for (let i = 0, len = args.length; i < len; i++) {
args[i].then(resolve, reject)
}
})
}
实现有并行限制的 Promise 调度器
class Scheduler {
constructor(limit) {
this.queue = [];
this.maxCount = limit;
this.runCounts = 0;
}
add(time, order) {
const promiseCreator = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(order);
resolve();
}, time);
});
};
this.queue.push(promiseCreator);
}
taskStart() {
for (let i = 0; i < this.maxCount; i++) {
this.request();
}
}
request(){
if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {
return;
}
this.runCounts++;
this.queue.shift()().then(() => {
this.runCounts--;
this.request();
});
}
}
const scheduler = new Scheduler(2);
const addTask = (time, order) => {
scheduler.add(time, order);
};
addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
scheduler.taskStart();
v-model 自定义组件
<custom-input v-model="searchText"> //这个是自定义组件
// <custom-input> 组件
<input v-bind:value="message" v-on:input="onmessage"></aa-input>
props:{message:"",}
methods:{
onmessage(e){
$emit('input',e.target.value)
}
}
dom
将虚拟 Dom 转化为真实 Dom
function _render(vnode) {
if (typeof vnode === "number") {
vnode = String(vnode);
}
if (typeof vnode === "string") {
return document.createTextNode(vnode);
}
const dom = document.createElement(vnode.tag);
if (vnode.attrs) {
Object.keys(vnode.attrs).forEach((key) => {
const value = vnode.attrs[key];
dom.setAttribute(key, value);
});
}
vnode.children.forEach((child) => dom.appendChild(_render(child)));
return dom;
}
调用
{
tag: 'DIV',
attrs:{
id:'app'
},
children: [
{
tag: 'SPAN',
children: [
{ tag: 'A', children: [] }
]
},
{
tag: 'SPAN',
children: [
{ tag: 'A', children: [] },
{ tag: 'A', children: [] }
]
}
]
}
把上诉虚拟Dom转化成下方真实Dom
<div id="app">
<span>
<a></a>
</span>
<span>
<a></a>
<a></a>
</span>
</div>