Navigation
阅读进度0%
JS 手写合集:千分位、深拷贝、Promise、继承、路由、双向绑定等
December 27, 2025 (1mo ago)
JavaScript
手写源码
面试
详细的文章来源(https://www.yuque.com/cuggz/interview/pkg93q#kzrlR)
待更新..../2024/02/25
// 千分位 分割
{
function formatNumberWithCommas(number) {
// 将数字转换为字符串
const numberString = String(number);
// 检查是否有小数部分
const [integerPart, decimalPart] = numberString.split('.');
// 将整数部分每三位加一个逗号
const integerWithCommas = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
/**
* \B 零宽度断言; 匹配的是非单词边界的位置,即在一个数字字符的中间位置
* (?= ... ):表示一个正向零宽断言,它匹配满足条件的位置,但是不会消耗字符
* (\d{3}) 匹配三个字符
* + 表示匹配前面的模式(三个数字字符)一次或多次,即匹配连续的三个数字字符的组合。
* (?!\d):表示一个负向零宽断言,它匹配后面不是数字字符的位置,确保匹配的三个数字字符后面不再是数字字符。
*/
// 如果有小数部分,则拼接整数部分和小数部分,并返回
if (decimalPart) {
return `${integerWithCommas}.${decimalPart}`;
} else {
return integerWithCommas;
}
}
// 示例
console.log(formatNumberWithCommas(123456789)); // 输出:123,456,789
console.log(formatNumberWithCommas(1234.5678)); // 输出:1,234.5678
}
// instanceOf
function myInstanceOf(left, right) {
if (left === null || left === undefined) return false;
let proto = Object.getPrototypeOf(left); // 获取对象原型
prototype = right.prototype; // 获去 right 上的 prototype
while (true) {
if (!proto) return false;
if (proto === prototype) return false;
proto = Object.getPrototypeOf(proto); // 循环
}
}
// assign 实现,多种方式 我采取 es5 方式
function myAssign(target, ...sources) {
if (target === null) throw new TypeError('不能是空');
const res = Object(target);
sources.forEach(function (obj) {
if (obj !== null) {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
// 值就重新覆盖
res[key] = obj[key];
}
}
}
});
return res;
}
// New 原理
function myNew(constructor, ...args) {
// 创建一个新的空对象,并将构造函数的原型对象连接到该对象上
const obj = Object.create(constructor.prototype);
// 将构造函数的上下文绑定到新对象上
const result = constructor.apply(obj, args);
// 如果构造函数返回了一个对象,则返回该对象;否则返回新创建的对象
return result instanceof Object ? result : obj;
}
// 示例构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
// 使用自定义的 myNew 来调用构造函数
const person = myNew(Person, 'John', 30);
console.log(person); // 输出:Person { name: 'John', age: 30 }
// 手写 typeof
function getType(value) {
// 判断数据是 null 的情况
if (value === null) {
return value + '';
}
// 判断数据是引用类型的情况
if (typeof value === 'object') {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
} else {
// 判断数据是基本数据类型的情况和函数的情况
return typeof value;
}
}
// 防抖 截流(前者描述n秒之后执行,n之内再触发不执行;
// 后者描述 n 秒内同一个触发只能有一次 即使多次 也支持能一次生效)
// 某个时间之后再执行
function debounce(func, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
// 某个时间范围内不执行!
function throttle(func, interval) {
let lastTime = 0;
return function (...args) {
const now = Date.now();
if (now - lastTime >= interval) {
func.apply(this, args);
lastTime = now;
}
};
}
// 实现 call 方法
Function.prototype.myCall = function (context, ...args) {
if (typeof this !== 'function') {
throw new TypeError('The "this" value must be a function');
}
// 使用 Symbol 避免与调用对象的属性冲突
const tempKey = Symbol();
// 将函数作为对象的属性
context[tempKey] = this;
// 在对象上调用该函数,并传入参数
const result = context[tempKey](...args);
// 删除临时属性
delete context[tempKey];
// 返回函数执行的结果
return result;
};
// 实现 apply 方法
Function.prototype.myApply = function (context, argsArray) {
if (typeof this !== 'function') {
throw new TypeError('The "this" value must be a function');
}
// 使用 Symbol 避免与调用对象的属性冲突
const tempKey = Symbol();
// 将函数作为对象的属性
context[tempKey] = this;
// 在对象上调用该函数,并传入参数数组
const result = context[tempKey](...argsArray);
// 删除临时属性
delete context[tempKey];
// 返回函数执行的结果
return result;
};
// 实现 bind 方法
Function.prototype.myBind = function (context, ...args) {
if (typeof this !== 'function') {
throw new TypeError('The "this" value must be a function');
}
// 缓存当前函数
const self = this;
// 返回一个新的函数
return function (...newArgs) {
// 将原函数绑定到指定的上下文对象上,并传入参数
return self.myCall(context, ...args, ...newArgs);
};
};
// 示例
const person = {
name: 'John',
greet: function (greeting) {
return `${greeting}, ${this.name}!`;
},
};
const result = person.greet.myCall(person, 'Hello');
console.log(result); // 输出:Hello, John!
const result2 = person.greet.myApply(person, ['Hello']);
console.log(result2); // 输出:Hello, John!
const boundFunction = person.greet.myBind(person, 'Hello');
const result3 = boundFunction();
console.log(result3); // 输出:Hello, John!
// 函数柯里化 这个东西比较简单 其中使用了 递归
{
function curry(func) {
return function curried(...args) {
if (args.length >= func.length) {
return func.apply(this, args);
} else {
return function (...moreArgs) {
return curried.apply(this, args.concat(moreArgs));
};
}
};
}
// 示例
function sum(a, b, c) {
return a + b + c;
}
const curriedSum = curry(sum);
console.log(curriedSum(1)(2)(3)); // 输出:6
console.log(curriedSum(1, 2)(3)); // 输出:6
console.log(curriedSum(1, 2, 3)); // 输出:6
}
// 深拷贝实现 非常复杂!(来之 字节 大佬的文章)
{
// 先看一个比较简单的版本的 使用 WeakMap 来解决循环引用的问题
function deepClone(obj, cache = new WeakMap()) {
// 如果是基本类型或者 null,则直接返回
if (obj === null || typeof obj !== 'object') {
return obj;
}
// 如果已经拷贝过该对象,则直接返回缓存的拷贝对象
if (cache.has(obj)) {
return cache.get(obj);
}
// 创建一个新的对象或数组
const clone = Array.isArray(obj) ? [] : {};
// 将当前对象添加到缓存中
cache.set(obj, clone);
// 递归复制每一个属性
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
clone[key] = deepClone(obj[key], cache);
}
}
return clone;
}
// 示例
const obj = {
a: 1,
b: {
c: 2,
d: [3, 4],
},
};
// 添加循环引用
obj.e = obj;
const clonedObj = deepClone(obj);
console.log(clonedObj); // 输出与 obj 结构相同的对象,包含循环引用
// end 结束
// 可持续遍历的
const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
// 不可持续遍历的
const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const numberTag = '[object Number]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const errorTag = '[object Error]';
const regexpTag = '[object RegExp]';
const funcTag = '[object Function]';
const deepTag = [mapTag, setTag, arrayTag, objectTag, arrayTag];
// utils 提高for 速度
function forEach(array, iteratee) {
let index = -1;
const length = array.length;
while (++index < length) {
iteratee(array[index], index);
}
return array;
}
// utils 看看是否是引用类型
function isObject(target) {
const type = typeof target;
return target !== null && (type === 'object' || type === 'function');
}
// utils 看看类型
function getType(target) {
return Object.prototype.toString.call(target);
}
// utils 初始化被克隆对象 注意这样做的目的是防止 构造函数 丢失
function getInit(target) {
const Ctor = target.constructor;
return new Ctor();
}
// utils 克隆 symbol
function cloneSymbol(targe) {
return Object(Symbol.prototype.valueOf.call(targe));
}
// utils 克隆 正则
function cloneReg(targe) {
const reFlags = /\w*$/;
const result = new targe.constructor(targe.source, reFlags.exec(targe));
result.lastIndex = targe.lastIndex;
return result;
}
// utils 克隆 function 这个非常重要!! 面试官经常纠结这个问题
function cloneFunction(func) {
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
// 通过正则把 函数的body 和 参数匹配出来
const funcString = func.toString();
// 区分一下 是 ()=> {} 还是 普通 function
if (func.prototype) {
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if (body) {
if (param) {
const paramArr = param[0].split(',');
return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
} else {
return null;
}
} else {
// 直接 eval 生成
return eval(funcString);
}
}
// utils 克隆 不可便利
function cloneOtherType(targe, type) {
const Ctor = targe.constructor;
switch (type) {
case boolTag:
case numberTag:
case stringTag:
case errorTag:
case dateTag:
return new Ctor(targe);
case regexpTag:
return cloneReg(targe);
case symbolTag:
return cloneSymbol(targe);
case funcTag:
return cloneFunction(targe);
default:
return null;
}
}
// main 主函数
function clone(target, map = new Map()) {
// 原始数据类型 直接返回
if (!isObject(target)) return target;
// 如果是不可便利 类型请直接克隆 如果是 请初始化 一个 cloneTarget
const type = getType(target);
let cloneTarget;
if (deepTag.includes(type)) {
cloneTarget = getInit(target, type);
console.log('--', cloneTarget);
} else {
return cloneOtherType(target, type);
}
// 处理循环引用
if (map.get(target)) {
return target;
}
map.set(target, cloneTarget);
// 处理map 和 set
if (type === setTag) {
target.forEach((value) => {
cloneTarget.add(clone(value));
});
return cloneTarget;
}
if (type === mapTag) {
target.forEach((value, key) => {
cloneTarget.set(key, clone(value));
});
return cloneTarget;
}
// 处理对象和array
const keys = type === arrayTag ? undefined : Object.keys(target);
forEach(keys || target, (value, key) => {
if (keys) {
key = value;
}
cloneTarget[key] = clone(target[key], map);
});
return cloneTarget;
}
const map = new Map();
map.set('key', 'value');
map.set('ConardLi', 'code秘密花园');
const set = new Set();
set.add('ConardLi');
set.add('code秘密花园');
const target = {
field1: 1,
field2: undefined,
field3: {
child: 'child',
},
field4: [2, 4, 8],
empty: null,
map,
set,
bool: new Boolean(true),
num: new Number(2),
str: new String(2),
symbol: Object(Symbol(1)),
date: new Date(),
reg: /\d+/,
error: new Error(),
func1: () => {
console.log('code秘密花园');
},
func2: function (a, b) {
return a + b;
},
};
console.log('===>', clone(target));
}
// 手写Promise 非常复杂!我们实现了基础的promise
{
interface Callbacks {
onFulfilled: (value: any) => void;
onRejected: (reason: any) => void;
}
class MyPromise {
state: 'pending' | 'fulfilled' | 'rejected';
value: any;
callbacks: Callbacks[];
constructor(
executor: (
resolve: (value?: any) => void,
reject: (reason?: any) => void,
) => void,
) {
this.state = 'pending';
this.value = undefined;
this.callbacks = [];
const resolve = (value: any) => {
if (this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
// 将执行回调的逻辑放在微任务队列中
queueMicrotask(() => {
this.callbacks.forEach((callback) => callback.onFulfilled(value));
});
}
};
const reject = (reason: any) => {
if (this.state === 'pending') {
this.state = 'rejected';
this.value = reason;
queueMicrotask(() => {
this.callbacks.forEach((callback) => callback.onRejected(reason));
});
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(
onFulfilled?: (value: any) => any,
onRejected?: (reason: any) => any,
): MyPromise {
return new MyPromise((resolve, reject) => {
const onFulfilledCallback = (value: any) => {
try {
const result = onFulfilled ? onFulfilled(value) : value;
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
};
const onRejectedCallback = (reason: any) => {
try {
const result = onRejected ? onRejected(reason) : reason;
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
}
};
if (this.state === 'fulfilled') {
onFulfilledCallback(this.value);
} else if (this.state === 'rejected') {
onRejectedCallback(this.value);
} else {
this.callbacks.push({
onFulfilled: onFulfilledCallback,
onRejected: onRejectedCallback,
});
}
});
}
catch(onRejected: (reason: any) => any): MyPromise {
return this.then(undefined, onRejected);
}
static all(promises: MyPromise[]): MyPromise {
return new MyPromise((resolve, reject) => {
const results: any[] = [];
let counter = 0;
const checkCompletion = () => {
counter++;
if (counter === promises.length) {
resolve(results);
}
};
promises.forEach((promise, index) => {
promise
.then((value) => {
results[index] = value;
checkCompletion();
})
.catch((reason) => {
reject(reason);
});
});
});
}
static race(promises: MyPromise[]): MyPromise {
return new MyPromise((resolve, reject) => {
promises.forEach((promise) => {
promise.then(resolve, reject);
});
});
}
}
// 示例
const promise1 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 1 resolved');
}, 1000);
});
const promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
reject('Promise 2 rejected');
}, 500);
});
MyPromise.race([promise1, promise2])
.then((value) => {
console.log(value); // 输出:Promise 1 resolved
})
.catch((reason) => {
console.error(reason); // 输出:Promise 2 rejected
});
}
// 实现数组的乱序输出
{
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for (let i = 0; i < arr.length; i++) {
// 核心代码 两个位置互换
const randomIdex = Math.round(Math.random() * (arr.length - 1 - i)) + i;
[arr[i], arr[randomIdex]] = [arr[randomIdex], arr[i]];
}
// console.log(arr);
}
// 扁平化 https://bigfrontend.dev/problem/implement-Array-prototype.flat/discuss
// 上的一道题目
{
function _flat(arr, depth) {
if (!Array.isArray(arr) || depth <= 0) {
return arr;
}
return arr.reduce((prev, cur) => {
if (Array.isArray(cur)) {
return prev.concat(_flat(cur, depth - 1));
} else {
return prev.concat(cur);
}
}, []);
}
// 缩写
function flat(arr, depth = 1) {
return depth
? arr.reduce((acc, curr) => {
return [
...acc,
...(Array.isArray(curr) ? flat(curr, depth - 1) : [curr]),
];
}, [])
: arr;
}
}
// 深浅去重 (纯数字 比较好去,如果是object 就需要条件
{
// 纯数字
// let arr = [1, 1, 1, 1, 2, 3, 4, 4];
// let a = Array.from(new Set(arr));
// console.log(a);
// 统一Object 类型 那么就需要 cb
const arr = [
{ name: 'a', id: 1 },
{ name: 'b', id: 1 },
{ name: 'c', id: 2 },
];
// pat 作为条件存储 的是唯一匹配的条件 比如 id,如果多条件以, 分割
function uniqueArray(arr, pat) {
const map = {};
const res = [];
for (let i = 0; i < arr.length; i++) {
let valueString = '';
pat.split(',').forEach((item) => {
valueString += arr[i][item];
});
if (!map.hasOwnProperty(valueString)) {
map[valueString] = 1;
res.push(arr[i]);
}
}
return res;
}
// console.log(uniqueArray(arr, "id,name"));
}
// 实现大数整数 的加法 重点!
{
function sumBigNumber(a, b) {
let res = '';
let temp = 0;
a = a.split('');
b = b.split('');
while (a.length || b.length || temp) {
temp += ~~a.pop() + ~~b.pop(); // 其实这个 ~ 一是取反 ~ 是原封不动 ,实际上这行代码就是Number()的作用
res = (temp % 10) + res;
// 注意 true 的 转化是1 false 是 0
temp = temp > 9;
}
return res.replace(/^0+/, '');
}
// console.log(sumBigNumber("1891", "1239"));
}
// 转Tree
{
const source = [
{
id: 1,
pid: 0,
name: 'body',
},
{
id: 2,
pid: 1,
name: 'title',
},
{
id: 3,
pid: 2,
name: 'div',
},
];
function jsonToTree(data) {
// 初始化结果数组,并判断输入数据的格式
const result = [];
if (!Array.isArray(data)) {
return result;
}
// 使用map,将当前对象的id与当前对象对应存储起来
const map = {};
data.forEach((item) => {
map[item.id] = item;
});
//
data.forEach((item) => {
const parent = map[item.pid];
if (parent) {
(parent.children || (parent.children = [])).push(item);
} else {
result.push(item);
}
});
return result;
}
}
// ULR 解析
{
const url =
'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';
parseParam(url);
function parseParam(url) {
const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来
const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中
const paramsObj = {};
// 将 params 存到对象中
paramsArr.forEach((param) => {
if (/=/.test(param)) {
// 处理有 value 的参数
let [key, val] = param.split('='); // 分割 key 和 value
val = decodeURIComponent(val); // 解码
val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字
if (paramsObj.hasOwnProperty(key)) {
// 如果对象有 key,则添加一个值
paramsObj[key] = [].concat(paramsObj[key], val);
} else {
// 如果对象没有这个 key,创建 key 并设置值
paramsObj[key] = val;
}
} else {
// 处理没有 value 的参数
paramsObj[param] = true;
}
});
return paramsObj;
}
}
// 手机中间四位 *
{
// // split slice joni
// let tel = 18877776666;
// tel = "" + tel;
// var ary = tel.split("");
// ary.splice(3, 4, "****");
// var tel1 = ary.join("");
// console.log(tel1);
// // substring
// tel = "" + tel;
// var tel1 = tel.replace(tel.substring(3, 7), "****");
// console.log(tel1);
}
// 实现简易 发布订阅 , 核心 队列!
{
class EventCenter {
// 1. 定义事件容器,用来装事件数组
handlers = {};
// 2. 添加事件方法,参数:事件名 事件方法
addEventListener(type, handler) {
// 创建新数组容器
if (!this.handlers[type]) {
this.handlers[type] = [];
}
// 存入事件
this.handlers[type].push(handler);
}
// 3. 触发事件,参数:事件名 事件参数
dispatchEvent(type, params) {
// 若没有注册该事件则抛出错误
if (!this.handlers[type]) {
return new Error('该事件未注册');
}
// 触发事件
this.handlers[type].forEach((handler) => {
handler(...params);
});
}
// 4. 事件移除,参数:事件名 要删除事件,若无第二个参数则删除该事件的订阅和发布
removeEventListener(type, handler) {
if (!this.handlers[type]) {
return new Error('事件无效');
}
if (!handler) {
// 移除事件
delete this.handlers[type];
} else {
const index = this.handlers[type].findIndex((el) => el === handler);
if (index === -1) {
return new Error('无该绑定事件');
}
// 移除事件
this.handlers[type].splice(index, 1);
if (this.handlers[type].length === 0) {
delete this.handlers[type];
}
}
}
}
const center = new EventCenter();
center.addEventListener('getName', (value) => {
console.log('value===>', value);
});
const obj2 = {
name: 'xj',
getName: function () {
center.dispatchEvent('getName', '66666');
},
};
// obj2.getName();/
}
// 实现 观察者 (发布订阅是基于 它进行改进之后得来的)
{
// 定义一个目标对象
class Subject {
constructor() {
this.Observers = [];
}
add(observer) {
//添加
this.Observers.push(observer);
}
remove(observer) {
//移除
this.Observers.filter((item) => item === observer);
}
notify() {
//通知所有观察者
this.Observers.forEach((item) => {
item.update();
});
}
}
//定义观察者对象
class Observer {
constructor(name) {
this.name = name;
}
update() {
console.log(`my name is:${this.name}`);
}
}
// let sub = new Subject();
// let obs1 = new Observer("observer11");
// let obs2 = new Observer("observer22");
// sub.add(obs1);
// sub.add(obs2);
// sub.notify();
}
// 实现简易 es5 继承 介绍了 6 中OOP 实现继承 方式(https://juejin.cn/post/6844903854463516685#heading-2)
{
// 原型连接(缺点传参做不到,多子类型的时候 改一个 父类实现 会全部影响)
{
function parents() {
this.name = 'JoseyDong';
}
parents.prototype.getName = function () {
console.log(this.name);
};
function child() {}
//子类的原型对象 指向 父类的实例对象 这样 子类就有了 其父类的一些熟悉了
child.prototype = new parents();
// 创建一个子类的实例对象,如果它有父类的属性和方法,那么就证明继承实现了
const child1 = new child();
// child1.getName(); // => JoseyDong
}
// 构造函数 (缺点,方法都在构造函数中定义,每次创建实例都会创建一遍方法。)
{
function parents(name) {
this.name = name;
}
//call方法支持传递参数
function child(name) {
// call 改变this指向
parents.call(this, name);
}
const child1 = new child('I am child1');
const child2 = new child('I am child2');
// console.log(child1.name); // => I am child1
// console.log(child2.name); // => I am child2
}
// 组合
{
function student(name) {
this.name = name;
this.hobbies = ['sing', 'dance', 'rap'];
}
function greatStudent(name, age) {
student.call(this, name);
this.age = age;
}
greatStudent.prototype = new student(); // 注意这个时候 greatStudent 的 constructor 跑了你需要重置
greatStudent.prototype.constructor = greatStudent;
// let kunkun = new greatStudent("kunkun", "18");
// let fakekun = new greatStudent("fakekun", "28");
// console.log(kunkun.name, kunkun.age, kunkun.hobbies); // => kunkun 18 ["sing", "dance", "rap"]
// console.log(fakekun.name, fakekun.age, fakekun.hobbies); // => fakekunkun 28 ["sing", "dance", "rap"]
// kunkun.hobbies.push("basketball");
// console.log(kunkun.name, kunkun.age, kunkun.hobbies);
}
// 原型 这个方式是直接在原型上挂一个对象,但是忽略了 对象是引用的问题,会导致 preson 一改全都受影响
{
function createObj(o) {
function F() {}
F.prototype = o;
return new F();
}
const person = {
name: 'JoseyDong',
hobbies: ['sing', 'dance', 'rap'],
};
const person1 = createObj(person);
const person2 = createObj(person);
// console.log(person1.name, person1.hobbies); // => JoseyDong ["sing", "dance", "rap"]
// console.log(person2.name, person2.hobbies); // => JoseyDong ["sing", "dance", "rap"]
// person1.name = "xixi";
// person1.hobbies.push("basketball");
// console.log(person1.name, person1.hobbies); //xixi ["sing", "dance", "rap", "basketball"]
// console.log(person2.name, person2.hobbies); //JoseyDong ["sing", "dance", "rap", "basketball"]
}
// 寄生 这个方式属于新建立一个对象,这个就没有问题,缺点就是 每次创建对象都会创建一遍方法。
{
function createObj(o) {
const clone = Object.create(o);
clone.sayName = function () {
console.log('hi');
};
return clone;
}
const person = {
name: 'JoseyDong',
hobbies: ['sing', 'dance', 'rap'],
};
const anotherPerson = createObj(person);
// anotherPerson.sayName(); // => hi
}
// 寄生组合 最佳实践
{
function student(name) {
this.name = name;
this.hobbies = ['sing', 'dance', 'rap'];
}
function greatStudent(name, age) {
student.call(this, name);
this.age = age;
}
// 这样做的缺点是 父类构造函数会调用两次 new student() 和 new greatStudent
// greatStudent.prototype = new student();
// greatStudent.prototype.constructor = greatStudent;
// let kunkun = new greatStudent('kunkun','18');
//关键的三步 实现继承
// 使用F空函数当子类和父类的媒介 是为了防止修改子类的原型对象影响到父类的原型对象
const F = function () {};
F.prototype = student.prototype; // 这样做
greatStudent.prototype = new F();
const kunkun = new greatStudent('kunkun', '18');
// console.log(kunkun);
}
}
// 实现简单路由 主要还是 基于hash 的实现为原理 而实现的
{
class Route {
constructor() {
// 路由存储对象
this.routes = {};
// 当前hash
this.currentHash = '';
// 绑定this,避免监听时this指向改变
this.freshRoute = this.freshRoute.bind(this);
// 监听
window.addEventListener('load', this.freshRoute, false);
window.addEventListener('hashchange', this.freshRoute, false);
}
// 存储
storeRoute(path, cb) {
this.routes[path] = cb || function () {};
}
// 更新
freshRoute() {
this.currentHash = location.hash.slice(1) || '/';
this.routes[this.currentHash]();
}
}
}
// 实现简单 es5 和es6 双向数据绑定
{
const obj = {};
// 数据劫持 意思就是 设置对象属性的时候可以触发 get 和set 方法
Object.defineProperty(obj, 'text', {
configurable: true,
enumerable: true,
get() {
console.log('获取数据了');
},
set(newVal) {
console.log('数据更新了', newVal);
// input.value = newVal;
// span.innerHTML = newVal;
return newVal;
},
});
// proxy
class VM {
constructor(options, target) {
this.data = options.data || {}; // 监听的数据对象
this.target = target;
this.init(); // 初始化
}
// 初始化
init() {
this.observer();
}
// 监听数据变化方法
observer() {
const handler = {
get: (target, propkey) => {
console.log(`监听到${propkey}被取啦,值为:${target[propkey]}`);
return target[propkey];
},
set: (target, propkey, value) => {
if (target[propkey] !== value) {
console.log(`监听到${propkey}变化啦,值变为:${value}`);
}
return true;
},
};
this.data = new Proxy(this.data, handler);
}
}
// // 测试一下
// const vm = new VM({
// data: {
// name: "defaultName",
// test: "defaultTest",
// },
// });
// vm.data.name = "changeName"; // 监听到name变化啦,值变为:changeName
// vm.data.test = "changeTest"; // 监听到test变化啦,值变为:changeTest
// vm.data.name; // 监听到name被取啦,值为:changeName
// vm.data.test; // 监听到test被取啦,值为:changeTest
}
// 实现jsonp
{
// 动态的加载js文件
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");
// 设置一个全局的callback函数来接收回调结果
function handleRes(res) {
console.log(res);
}
// 接口返回的数据格式
}
// 小孩报数问题
/**
* 有30个小孩儿,编号从1-30,围成一圈依此报数,1、2、3 数到 3 的小孩儿退出这个圈,
* 然后下一个小孩 重新报数 1、2、3,问最后剩下的那个小孩儿的编号是多少?
*/
{
function childNum(num, count) {
const allplayer = [];
for (let i = 0; i < num; i++) {
allplayer[i] = i + 1;
}
let exitCount = 0; // 离开人数
let counter = 0; // 记录报数
let curIndex = 0; // 当前下标
while (exitCount < num - 1) {
if (allplayer[curIndex] !== 0) counter++;
if (counter == count) {
allplayer[curIndex] = 0;
counter = 0;
exitCount++;
}
curIndex++;
if (curIndex == num) {
curIndex = 0;
}
}
for (let i = 0; i < num; i++) {
if (allplayer[i] !== 0) {
return allplayer[i];
}
}
}
childNum(30, 3);
}
// 实现一个订阅
{
class EventEmitter {
subscriptions = new Map();
subscribe(eventName, callback) {
if (!this.subscriptions.has(eventName)) {
this.subscriptions.set(eventName, new Set());
}
const subscriptions = this.subscriptions.get(eventName);
const callbackObj = { callback: callback };
subscriptions.add(callbackObj);
return {
release: () => {
subscriptions.delete(callbackObj);
if (subscriptions.size === 0) {
delete this.subscriptions.eventName;
}
},
};
}
emit(eventName, ...args) {
const subscriptions = this.subscriptions.get(eventName);
if (subscriptions) {
subscriptions.forEach((cbObj) => {
cbObj.callback.apply(this, args);
});
}
}
}
}
// 找出文中出现最多的单词
{
function findMostWord(str) {
// 将字符串转换为小写并按照空格分割成单词数组
const words = str.toLowerCase().split(/\s+/);
// 创建一个对象用于统计单词出现的次数
const wordCounts = {};
// 统计单词出现的次数
for (const word of words) {
wordCounts[word] = (wordCounts[word] || 0) + 1;
}
// 找出出现次数最多的单词
let mostWord = '';
let maxCount = 0;
for (const word in wordCounts) {
if (wordCounts[word] > maxCount) {
mostWord = word;
maxCount = wordCounts[word];
}
}
// 返回出现次数最多的单词
return mostWord;
}
// 示例
const sentence = 'The quick brown fox jumps over the lazy dog';
console.log(findMostWord(sentence)); // 输出:'the'
}