function myNew(fn, ...args) {
let obj = {};
obj.__proto__ = fn.prototype;
let res = fn.call(obj, ...args);
if (res && (typeof res === "object" || typeof res === "function")) {
return res;
}
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);
p1.say();
function Instanceof(left, right) {
let leftVal = left.__proto__;
let rightVal = right.prototype;
while(leftVal !== null) {
if (leftVal === rightVal){
return true;
}
leftVal = leftVal.__proto__; // 继续查找原型链
}
return false;
}
// 测试代码
console.log(Instanceof([], Array));
console.log(Instanceof([], Object));
function deepClone(objData) {
let result = Array.isArray(objData) ? [] : {};
for (let i in objData) {
if (objData.hasOwnProperty(i)) {
result[i] = typeof objData[i] === "object" ? deepClone(objData[i]) : objData[i];
}
}
return result;
}
// 测试代码
var obj1 = {
a: 1,
b: {
a: 2,
c: {
name: 'jack',
age: 12
}
}
};
var obj2 = deepClone(obj1);
console.log(obj1);
Array.prototype.myReduce = function (fn, initvalue) {
if(Object.prototype.toString.call(fn) !== "[object Function]" ) {
throw new Error('第一个参数必须是函数');
}
// 第二个参数是可选的
let pre = null;
if (arguments.length > 1) {
pre = initvalue
} else {
pre = this[0];
}
// 迭代 fn 方法:
for (let i = 0; i < this.length; i++) {
pre = fn(pre, this[i], i, this);
}
return pre;
}
// 测试代码
let arr = [1, 2, 3];
let result = arr.myReduce((pre, cur) => {
return pre + cur;
}, 0)
console.log(result);
Array.prototype.myFilter = function(fn) {
if(Object.prototype.toString.call(fn) !== "[object Function]") {
throw new Error('第一个参数必须是函数');
}
let list = [];
for(let i =0; i < this.length; i++) {
let result = fn(this[i], i, this);
if (result) {
list.push(this[i]);
}
}
return list;
}
// 测试代码
console.log([2, 4, 6].myFilter(item => item < 3));
Array.prototype.myMap = function(fn) {
if(Object.prototype.toString.call(fn) !== "[object Function]") {
throw new Error('第一个参数必须是函数');
}
let list = [];
for(let i = 0; i< this.length; i++) {
let result = fn(this[i], i, this);
list.push(result);
}
return list;
}
// 测试代码
console.log([1, 2, 3, 5].myMap(item => item * 10));
Array.prototype.myEvery = function(fn) {
if(Object.prototype.toString.call(fn) !== "[object Function]") {
throw new Error('第一个参数必须是函数');
}
for(let i = 0; i< this.length; i++) {
if(!fn(this[i], i, this)) {
return false;
}
}
return true;
}
// 测试代码
console.log([1, 2, 3, 5].myEvery(item => item > 1));
// 1. reduce方法实现:
function flat01(arr) {
if(!arr.length) return;
return arr.reduce((pre, cur) => {
return Array.isArray(cur) ? [...pre, ...flat01(cur)] : [...pre, cur]
}, [])
}
// 2. 递归实现:
function flat02(arr) {
if(!arr.length) return;
let result = [];
for(let i of arr) {
if(!Array.isArray(i)) {
result.push(i);
} else {
result = [...result, ...flat02(i)];
}
};
return result;
}
// 3. 常规数组实现
function flat03(arr) {
if(!arr.length) return;
while(arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
};
return arr;
}
// 测试代码
console.log(flat01([1, 2, [1, [2, 3, [4, 5, [6]]]]]));
console.log(flat02([1, 2, [1, [2, 3, [4, 5, [6]]]]]));
console.log(flat03([1, 2, [1, [2, 3, [4, 5, [6]]]]]));
// 函数柯里化 —— 将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术
function curry(fn) {
if(fn.length <= 1) return fn; // 确定传入的函数的参数的个数
const result = (...args) => {
if(fn.length === args.length) {
return fn(...args);
} else {
return (...argsNew) => { // 递归取参数,然后在合并参数,一直到取到的参数数量和 fn 参数数量相等
return result(...args, ...argsNew);
}
}
}
return result;
}
// 测试代码
let add = (a, b, c, d) => a + b+ c + d;
const curriedAdd = curry(add);
console.log(add(1, 2, 3, 5));
console.log(curriedAdd(1)(2)(3)(5));
Function.prototype.myCall = function (context, ...args) {
if (!context || context === null) {
context = window;
}
let sym = Symbol();
context[sym] = this; // 此时调用myCall方法的是context的sym属性(方法)
const result = context[sym](...args); // 执行函数并返回结果 相当于把自身作为传入的context的方法进行调用了
delete context[sym]; //删除该方法,不然会对传入对象造成污染(添加该方法)
return result;
}
// 测试代码
function greet() {
var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
console.log(reply);
}
var obj = {
animal: 'cats',
sleepDuration: '12 and 16 hours'
};
greet.myCall(obj);
Function.prototype.myApply = function (context, args = []) {
if (!context || context === null) {
context = window;
}
// 创造唯一的key值 作为我们构造的context内部方法名
let sym = Symbol();
context[sym] = this;
// 执行函数并返回结果
let result = context[sym](...args);
delete context[sym];
return result;
};
// 测试代码
function greet() {
var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
console.log(reply);
}
var obj = {
animal: 'cats',
sleepDuration: '12 and 16 hours'
};
greet.myApply(obj);
Function.prototype.myBind = function(context, ...args) {
const self = this;
args = args ? args : [];
return function() {
let newArgs = [...arguments];
return self.apply(context, args.concat(newArgs));
}
}
// 测试代码
function greet() {
var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
console.log(reply);
}
var obj = {
animal: 'cats',
sleepDuration: '12 and 16 hours'
};
greet.myBind(obj)();
// Object.create()会将参数对象作为一个新创建的空对象的原型, 并返回这个空对象
// 1. 简单版
function myCreate(obj) {
function CreateObj() {};
CreateObj.prototype = obj;
return new CreateObj();
}
// 2. 官方版
if (typeof Object.create !== "function") {
Object.create = function (proto, propertiesObject) {
if (typeof proto !== 'object' && typeof proto !== 'function') {
throw new TypeError('Object prototype may only be an Object: ' + proto);
} else if (proto === null) {
throw new Error("This browser's implementation of Object.create is a shim and doesn't support 'null' as the first argument.");
}
if (typeof propertiesObject !== 'undefined') throw new Error("This browser's implementation of Object.create is a shim and doesn't support a second argument.");
function F() {}
F.prototype = proto;
return new F();
};
}
// 测试代码
let a = {
name: 'jack',
age: 12
};
let b = myCreate(a);
console.log(b); // {}
console.log(b.name) // "jack"
// 防抖函数 —— 连续触发在最后一次执行方法,场景:输入框匹配
function debounce(fn, time) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer);
timer = null;
}
timer = setTimeout(() => {
fn();
}, time);
}
}
// 节流函数 —— 在一定时间内只触发一次,场景:长列表滚动节流
function throttle(fn, time) {
let isRuning = false;
return function() {
if(isRuning) {
return;
}
isRuning = true;
setTimeout(() => {
fn();
isRuning = false;
}, time)
}
}
// 测试代码
let fn = () => {
console.log('fffffff')
}
setInterval(debounce(fn, 500), 1000) // 第一次在1500ms后触发,之后每1000ms触发一次
setInterval(throttle(fn, 1000), 1500) // 开启定时器任务测试
function ajax(options) {
let url = options.url
const method = options.method.toLocaleLowerCase() || 'get';
const async = options.async != false; // 默认是异步的: true
const data = options.data; // 携带参数
const xhr = new XMLHttpRequest(); // 初始化 XMLHttpRequest 实例
if (options.timeout && options.timeout > 0) { // 请求超时时间
xhr.timeout = options.timeout
}
return new Promise((resolve, reject) => {
xhr.ontimeout = () => reject && reject('请求超时'); // 当请求超时时,会触发 ontimeout 方法; 此时抛出错误
xhr.onreadystatechange = () => {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
resolve && resolve(xhr.responseText);
} else {
reject && reject();
}
}
}
xhr.onerror = err => reject && reject(err); // 捕获错误信息
// 处理参数信息:
let paramArr = []
let encodeData
if (data instanceof Object) {
for (let key in data) {
paramArr.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
}
encodeData = paramArr.join('&')
}
// get 请求的处理
if (method === 'get') {
// 检测 url 中是否已存在 ? 及其位置
const index = url.indexOf('?')
if (index === -1) url += '?'
else if (index !== url.length - 1) url += '&'
// 拼接 url
url += encodeData
}
xhr.open(method, url, async)
if (method === 'get') {
xhr.send(null);
} else {
// post 方式需要设置请求头
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8')
xhr.send(encodeData)
}
})
}
// 使用方法:
ajax({
url: 'your request url',
method: 'get',
async: true,
timeout: 1000,
data: {
test: 1,
aaa: 2
}
}).then(
res => console.log('请求成功: ' + res),
err => console.log('请求失败: ' + err)
)
const jsonp = function (url, data) {
return new Promise((resolve, reject) => {
// 初始化url,构造完整的 url:
let dataString = url.indexOf('?') === -1 ? '?' : '';
let callbackName = `jsonpCB_${Date.now()}`;
url += `${dataString}callback=${callbackName}`;
if (data) {
// 有请求参数,依次添加到url
for (let k in data) {
url += `${k}=${data[k]}`;
}
}
// 创建 script 标签:
let jsNode = document.createElement('script');
jsNode.src = url;
// 触发callback,触发后删除js标签和绑定在window上的callback
window[callbackName] = result => {
delete window[callbackName]
document.body.removeChild(jsNode)
if (result) {
resolve(result)
} else {
reject('没有返回数据')
}
}
// js加载异常的情况
jsNode.addEventListener('error', () => {
delete window[callbackName]
document.body.removeChild(jsNode)
reject('JavaScript资源加载失败')
}, false)
// 添加js节点到document上时,开始请求
document.body.appendChild(jsNode)
})
}
// 代码实现:
jsonp('http://192.168.0.103:8081/jsonp', {
a: 1,
b: 'heiheihei'
})
.then(result => {
console.log(result)
})
.catch(err => {
console.error(err)
})
class EventEmitter {
constructor() {
this.events = {};
}
// 事件监听/订阅
on(type, callBack) {
let callbacks = this.events[type] || [];
callbacks.push(callBack);
this.events[type] = callbacks;
}
// 取消订阅
off(type, callBack) {
if (!this.events[type]) return;
this.events[type] = this.events[type].filter(item => {
return item !== callBack;
})
}
// 触发事件
emit(type, ...rest) {
if (this.events[type] && this.events[type].length > 0) {
for (let fn of this.events[type]) {
fn(...rest);
}
} else {
console.log('没有取到事件')
}
}
// 触发一次
once(type, callback) {
let fn = function (...args) {
callback(...args);
this.off(type, fn); // 触发一次之后取消订阅
};
this.on(type, wrapFun);
}
}
// 测试代码
const llzevent = new EventEmitter();
const handle = (...rest) => {
console.log(rest);
};
llzevent.on("click", handle);
console.log(llzevent)
llzevent.emit('click', 1, 2, 4);
const EventUtils = {
// 添加事件(视能力分别使用dom0||dom2||IE三种方式方式来绑定事件)
addEvent: function(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else if (element.attachEvent) {
element.attachEvent("on" + type, handler);
} else {
element["on" + type] = handler;
}
},
// 移除事件
removeEvent: function(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else if (element.detachEvent) {
element.detachEvent("on" + type, handler);
} else {
element["on" + type] = null;
}
},
// 获取事件目标
getTarget: function(event) {
return event.target || event.srcElement;
},
// 获取 event 对象的引用,取到事件的所有信息,确保随时能使用 event
getEvent: function(event) {
return event || window.event;
},
// 阻止事件(主要是事件冒泡,因为 IE 不支持事件捕获)
stopPropagation: function(event) {
if (event.stopPropagation) {
event.stopPropagation();
} else {
event.cancelBubble = true;
}
},
// 取消事件的默认行为
preventDefault: function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
}
};
<ul class="container">
<li class="item">1231<span style="color: red">123131</span></li>
<li class="item">8888</li>
<li class="item">第三行</li>
<li class="item"><div>第四行</div></li>
</ul>
<script>
const container = document.querySelector(".container");
delegate(container, ".item", "click", (e, item) => {
console.log(item.textContent);
});
function delegate(container, itemSelector, event, handler) {
container.addEventListener(event, (e) => {
let target = e.target;
while (!target.matches(itemSelector) && target !== container) {
target = target.parentNode;
}
if (target !== container) {
handler(e, target);
}
});
}
</script>
// 简单版本
class Scheduler {
constructor(limit) {
this.limit = limit
this.number = 0
this.queue = []
}
addTask(timeout, str) {
this.queue.push([timeout, str])
}
start() {
if (this.number < this.limit&&this.queue.length) {
var [timeout, str] = this.queue.shift()
this.number++
setTimeout(() => {
console.log(str)
this.number--
this.start()
}, timeout * 1000);
this.start()
}
}
}
// 带并发限制的异步调度器 Scheduler,同时要可以限制并发数量
class Scheduler {
constructor(maxCount) {
this.maxCount = maxCount;
this.queue = [];
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();
<div class="box"></div>
<style>
.box {
position: absolute;
width: 100px;
background-color: red;
height: 100px;
left: 0;
top: 0;
}
</style>
<script>
let lastPosition = [];
const boxEle = document.querySelector(".box");
let isDraging = false;
boxEle.addEventListener("mousedown", (e) => {
isDraging = true;
lastPosition[0] = e.clientX;
lastPosition[1] = e.clientY;
});
document.addEventListener("mousemove", (e) => {
if (!isDraging) return;
const deltaX = e.clientX - lastPosition[0];
const deltaY = e.clientY - lastPosition[1];
const curLeft = parseInt(boxEle.style.left || 0);
const curTop = parseInt(boxEle.style.top || 0);
boxEle.style.cssText = `left: ${curLeft + deltaX}px;top: ${curTop + deltaY}px`;
lastPosition[0] = e.clientX;
lastPosition[1] = e.clientY;
});
document.addEventListener("mouseup", () => {
isDraging = false;
});
</script>
class MyPromise {
constructor(func) {
this.status = 'pending'
this.result = undefined;
this.error = undefined;
this.resoledCallbacks = [];
this.rejectedCallbacks = [];
const resolve = (result) => {
this.status = 'resolved'
this.result = result;
setTimeout(() => {
this.resoledCallbacks.forEach((callback) => callback())
})
};
const reject = (error) => {
this.status = 'rejected'
this.error = error;
setTimeout(() => {
this.rejectedCallbacks.forEach((callback) => callback())
})
};
try {
func && func(resolve, reject);
} catch (err) {
this.status = 'rejected';
this.error = err;
}
}
then(resolveCallback, rejectedCallback) {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
if (this.status === 'resolved') {
this.onResolve(resolveCallback, resolve, reject);
} else if (this.status === 'rejected') {
this.onReject(rejectedCallback, resolve, reject);
} else {
this.resoledCallbacks.push(() => this.onResolve(resolveCallback, resolve, reject));
this.rejectedCallbacks.push(() => this.onReject(rejectedCallback, resolve, reject));
}
})
})
}
catch(callback) {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
if (this.status === 'resolved') {
resolve(this.result);
} else if (this.status === 'rejected') {
this.onReject(callback, resolve, reject);
} else {
this.resoledCallbacks.push(() => resolve(this.result));
this.rejectedCallbacks.push(() => this.onReject(callback, resolve, reject));
}
})
})
}
finally(callback) {
return new MyPromise((resolve, reject) => {
if (this.status === 'pending') {
this.resoledCallbacks.push(() => this.onFinally(callback, resolve, reject));
this.rejectedCallbacks.push(() => this.onFinally(callback, resolve, reject));
} else {
this.onFinally(callback, resolve, reject);
}
})
}
onResolve(callback, resolve, reject) {
if (!callback) return resolve();
try {
const result = callback(this.result);
if (result instanceof MyPromise) {
result.then((res) => {
resolve(res);
}).catch((err) => {
reject(err);
})
} else if (result instanceof Object && result.then instanceof Function) {
result.then(res => {
resolve(res);
})
} else {
resolve(result);
}
} catch (err) {
reject(err)
}
}
onReject(callback, resolve, reject) {
if (!callback) return reject(this.error);
try {
const res = callback(this.error);
resolve(res)
} catch (err) {
reject(err);
}
}
onFinally(callback, resolve, reject) {
try {
callback && callback();
if (this.status === 'resolved') resolve(this.result);
if (this.status === 'rejected') reject(this.error);
} catch (err) {
reject(err)
}
}
}
function resolve(val) {
if (val instanceof Promise) return val;
if (val && val.then instanceof Function) {
return new Promise(val.then);
};
return new Promise((resolve) => resolve(val));
}
function reject(val) {
return new Promise((resolve, reject) => reject(val))
}
function all(promiseArr) {
return new Promise((resolve, reject) => {
if(!Array.isArray(promiseArr)) {
reject(new Error('传参错误'))
}
let res = []; // 消息搜集
let counter = 0;
for(let i = 0; i< promiseArr.length; i++) {
Promise.resolve(promiseArr[i]).then((data) => {
counter ++;
res.push(data);
if(counter === promiseArr.length) {
resolve(res);
}
}).catch(error => {
reject(new Error('失败'));
})
}
})
}
function race(promiseArr) {
return new Promise((resolve, reject) => {
if(!Array.isArray(promiseArr)){
return reject(new Error('传入的参数必须是数组'));
}
for(let i = 0;i< promiseArr.length; i++) {
Promise.resolve(promiseArr[i]).then((data) => {
resolve(data);
}).catch(error => {
reject(new Error('失败'));
})
}
})
}
function any(promises) {
const errors = []
let count = 0;
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then((result) => {
resolve(result)
}).catch((err) => {
errors[i] = err;
count++;
if (count === promises.length) {
reject(errors)
}
})
}
})
}
module.exports = {
resolve,
reject,
all,
race,
any
}
// es5版本
function Father(name) {
this.name = name;
}
Father.prototype.printName = function() {
console.log(this.name);
}
function Son(age) {
this.age = age;
Father.call(this);
}
Son.prototype = Object.create(Father.prototype); // 把 Father.prototype 作为一个新创建的空对象的原型,并返回这个空对象
Son.prototype.constructor = Son;
// es6版本
class Father {
constructor(name) {
this.name = name;
}
printName() {
console.log(this.name);
}
}
class Son extends Father{
constructor(name, age) {
super(name);
this.age = age;
}
printAll() {
console.log(this.name, this.age);
}
}
// setTimeout版本
const sleep = (fn, wait = 1000) => {
return function() {
setTimeout(() => {
fn.apply(this, arguments);
}, wait)
}
}
// async / await版本
const sleep = async function(fn, wait, ...args) {
const innerFunc = () => {
setTimeout(() => {
fn(...args);
}, wait)
}
await innerFunc();
}
// promise 版本
const sleep = (wait = 1000) => {
return new Promise((resolve) => {
setTimeout(resolve, wait)
})
}
function bubbleSort(arr) {
let length = arr.length;
for(let i =0; i< length; i++) {
for(let j = i; j< length; j++) {
if(arr[j] > arr[j+1]) {
[arr[j], arr[j+1]] = [arr[j+1], arr[j]];
}
}
}
return arr;
}
function selectSort(arr) {
let length = arr.length;
let minIndex; // 用来缓存最小的一个值得 index
for(let i = 0; i< length; i++) {
minIndex = i;
for(let j = i; j< length; j++) {
if(arr[j] < arr[minIndex]) {
minIndex = j;
}
}
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
}
return arr;
}
function quickSort(arr) {
if(arr.length < 2) {
return arr;
}
let cur = arr[arr.length -1]; // 选取一个值作为分界点的值,也可以取arr[0]
let leftarr = arr.filter((item, index) => item <= cur && index !== arr.length - 1);
let rightArr = arr.filter((item, index) => item >= cur && index !== arr.length - 1);
return [...quickSort(leftarr), cur, ...quickSort(rightArr)];
}
function searchVal(arr, val, left = 0, right = arr.length - 1) {
let midIndex = parseInt((left + right) / 2, 10);
if (val > arr[midIndex]) {
return searchVal(arr, val, midIndex + 1, right);
} else if (val < arr[midIndex]) {
return searchVal(arr, val, left, midIndex - 1);
} else {
return midIndex;
}
}
// 测试代码
let arr = [1, 4, 5, 66, 7, 8, 34, 4344, 67];
console.log(searchVal(arr, 4));
// 实现一个:lazyMan('Tony').eat('lunch').sleep(10).eat('dinner');
// Hi I am Tony
// I am eating lunch
// 等待了10秒...
// I am eating diner
class LazyManClass {
constructor(name) {
this.name = name;
this.queue = [];
console.log(`HI i am ${this.name}`);
setTimeout(() => {
this.next();
}, 0)
}
eat(food) {
const fn = () => {
console.log(`I am eating ${food}`);
this.next();
}
this.queue.push(fn);
return this;
}
sleep(time) {
const fn = () => {
setTimeout(() => {
console.log(`等待了${time}秒...`);
this.next();
}, time * 1000);
}
this.queue.push(fn);
return this;
}
next() {
// 刷出队列中的第一个任务,并执行:
const fn = this.queue.shift();
fn && fn();
}
}
// 代码测试:
function lazyMan(name) {
return new LazyManClass(name);
}
lazyMan('Tony').sleep(5).eat('lunch');
// LRU算法:获取数据和写入数据,写入数据时如果密钥存在则变更数据值,不存在则插入该组数据值,缓存到上线==上限时应该在写入新数据之前删除最开始写入的数据
class LRUCache {
constructor(size) {
this.size = size; // 容量
this.secretKey = new Map(); // 建立 key 值的字典
}
// 取值
get(key) {
if(this.secretKey.has(key)) {
let templateValue = this.secretKey.get(key);
// 删除了之后重新set 进去
this.secretKey.delete(key);
this.secretKey.set(key, templateValue);
return templateValue;
} else {
return -1;
}
}
// 更新
put(key, value) {
if(this.secretKey.has(key)) {
this.secretKey.delete(key);
this.secretKey.set(key, value);
} else if(this.secretKey.size < this.size) {
this.secretKey.set(key, value);
} else {
this.secretKey.set(key, value);
this.secretKey.delete(this.secretKey.keys().next().value);
}
}
}
掘金链接: