手写代码集合
- 1、节流和防抖
- 节流
- 防抖
- 2、改变this指向的函数
- bind
- apply
- call
- 3、promise
- 概念
- 状态
- 实现一个promise
- promise.all
- promise.race
- 4、深拷贝
- 5、发布订阅模式
- 6、new
- 7、Object.create
- 8、Ajax
- 9、JSONP
- 10、路由
- 11、函数柯里化
- 12、instanceof
- 13、数组
- 数组扁平化
- 数组去重
- map
- reduce
- forEach
- 14、sleep
- 15、用 setTimeout 实现 setInterval
- 16、图片加载
- 懒加载
- 滚动加载
1、节流和防抖
节流
在 s 秒内即使被触发多次,也只能执行一次。
function throttle(fn, delay){
let flag = true;
return function (...args){
if(!flag){
return;
}
flag = false;
setTimeout(() => {
fn.apply(this, args);
flag = true;
}, delay)
}
}
防抖
s 秒内函数只会被执行一次,如果 s 秒内多次被触发,则以最后一次触发为标准重新计算延迟时间。
function debounce(fn, delay){
let timer;
return function(...args){
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay)
}
}
2、改变this指向的函数
bind
Function.prototype.bind = function (context){
context = context || window;
context.fn = this;
let args = [... arguments].slice(1);
return function func() {
// 判断是否被当做构造函数使用
if(this instanceof func) {
return new context.fn(...args, ...arguments);
}
return context.fn.apply(context,args.concat(...arguments));
}
}
apply
Function.prototype.call = function (context){
context = context || window;
context.fn = this;
let result;
let args = [...arguments].splice(1);
if(arguments[1]){
result = context.fn(...args);
} else {
result = context.fn();
}
delete context.fn;
return result;
}
call
Function.prototype.apply = function (context){
context = context || window;
context.fn = this;
let args = [...arguments].splice(1);
let result = context.fn(...args);
delete context.fn;
return result;
}
3、promise
概念
promise 对象是一个代理对象,被代理的值在 promise 对象创建时可能是未知的,可以为异步操作的成功和失败分别绑定相应的处理方法。 让异步方法可以像同步方法那样返回值,但并不是立即返回最终执行结果,而是一个能代表未来出现的结果的promise对象。解决了异步回调地狱问题。
状态
1、pending:进行中
2、fulfilled:已成功
3、rejected:失败
只有异步操作才能决定当前的状态,其他操作无法改变。
一旦状态确定,就不会再改变,这时候称为定型 resolved,状态变化情况:pending ——> fulfilled,pending ——> rejected。
实现一个promise
class Promise{
constructor(executor) {
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
this.resolveCallback = [];
this.rejectCallback = [];
function resolve(value) {
if (this.status === 'pending') {
this.status = 'fulfilled';
this.value = value;
this.resolveCallback.forEach(fn => {
fn();
})
}
}
function reject(reason) {
if (this.status === 'pending') {
this.status = 'rejected';
this.reason = reason;
this.rejectCallback.forEach(fn => {
fn();
})
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
then(onFulfilled, onRejected){
return new Promise((resolve, reject) => {
if (this.status === 'fulfilled') {
setTimeout(() => {
const x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject);
}, 0)
}
if (this.status === 'rejected') {
setTimeout(() => {
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject);
}, 0)
}
if (this.status === 'pending') {
this.resolveCallback.push(() => {
setTimeout(() => {
const x = onFulfilled(this.value)
onFulfilled(promise2, x, resolve, reject);
}, 0)
})
this.resolveCallback.push(() => {
setTimeout(() => {
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject);
}, 0)
})
}
})
}
}
const resolvePromise = (promise, x, resolve, reject) => {
if(promise === x){
throw TypeError('循环')
}
if(typeof x === 'function' || (typeof x === 'object' && x !== null)){
try{
const then = x.then;
if(typeof then === 'function'){
then.call(x, y => {
resolvePromise(promise, y, resolve, reject);
}, r => {
reject(r);
})
}
} catch (e){
reject(e);
}
} else {
resolve(x);
}
}
promise.all
解决并发问题,多个异步并发获取最终的结果,如果有一个失败则失败。
Promise.all = function (iterators){
let count = 0,
res = [];
return new Promise((resolve, reject) => {
for(let i=0; i<iterators.length; i++){
Promise.resolve(iterators[i]).then((data) => {
res[i] = data;
if(++count === iterators.length){
resolve(res);
}
}).catch(e => {
reject(e);
})
}
})
}
promise.race
Promise.race = function (iterators){
return new Promise((resolve, reject) => {
for(let p in iterators){
Promise.resolve(p).then((res) => {
resolve(res);
}).catch(e => {
reject(e);
})
}
})
}
4、深拷贝
function deepClone(obj) {
// 过滤一些特殊情况
if(obj === null) {
return null;
}
if(typeof obj !== "object") {
return obj;
}
if(obj instanceof RegExp) { // 正则
return new RegExp(obj);
}
if(obj instanceof Date) { // 日期
return new Date(obj);
}
var newObj = obj instanceof Array ? []:{};
for(let key in obj) {
if(obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key]);
}
}
return newObj;
}
5、发布订阅模式
class Publisher{
constructor() {
this.watcher = {};
}
on(event, fn){
if(!this.watcher[event]){
this.watcher[event] = [];
}
this.watcher[event].push(fn);
}
emit(event, ...args){
if(!this.watcher[event]){
return;
}
this.watcher[event].forEach(fn => {
fn.apply(fn, args);
});
}
off(event, fn){
if(!this.watcher[event]){
return;
}
let index = this.watcher[event].indexOf(fn);
this.watcher[event].splice(index, 1);
}
once(event, fn){
function callback(){
fn();
this.off(event, fn);
}
this.on(event, callback);
}
}
6、new
1、创建类实例:新建一个空对象,这个对象原型指向构造函数的 prototype,执行构造函数后返回这个对象。
2、初始化实例:构造函数被传入参数并调用,this 指向该实例的 obj。
3、如果是对象则返回结果,否则返回原始值。
function createNew(con, ...args) {
let obj = {};
obj.__proto__ = con.prototype;
// 将构造函数中的this指向这个对象,并传递参数
let result = con.apply(obj, args);
return result instanceof Object ? result : obj
}
7、Object.create
1、创建一个临时性的构造函数。
2、将传入的对象作为这个构造函数的原型。
3、最后返回这个临时类型的一个新实例。
function create(obj) {
function F() {};
F.prototype = obj;
return new F();
}
8、Ajax
1、创建Ajax对象。
2、向服务器发送请求,使用xmlHttpRequest对象的open和send方法。
3、监听状态变化,执行相应回调函数。
4、发送请求。
// 原生Ajax
function ajax(url){
let xhr = new XMLHttpRequest();
xhr.open('get', url);
xhr.onreadystatechange = function (){
if(xhr.status === 200 && xhr.readyState === 4){
let result = xhr.responseText;
let obj = JSON.parse(result);
}
}
xhr.send();
}
// 封装为promise
var myAjax = function (url, data){
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.open('get', url);
xhr.onreadystatechange = function (){
if(xhr.status === 200 && xhr.readyState === 4){
resolve(JSON.parse(xhr.responseText));
} else if (xhr.readyState === 4 && xhr.status !== 200){
reject('error');
}
}
xhr.send(data);
})
}
9、JSONP
JSONP 是 JSON 的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
JSONP 由回调函数和数据组成。回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的 JSON 数据。
function jsonp(url, data, callback) {
// 处理json对象,拼接url
data.callback = callback;
let params = [];
for (let item in data) {
params.push(item + '=' + data[item]);
}
// 创建script元素
let script = document.createElement('script');
script.src = url + '?' + params.join('&');
document.body.appendChild(script);
// 返回promise
return new Promise((resolve, reject) => {
window[callback] = function (data){
try {
resolve(data);
} catch (e) {
reject(e)
} finally {
// 移除script元素
script.parentNode.removeChild(script)
console.log(script)
}
}
})
}
10、路由
function Route(){
this.route = new Map;
this.currentUrl = '';
}
Route.prototype.registerRoute = function (url ,cb){
this.route.set(url || '', cd || function (){});
}
Route.prototype.refreshRoute = function (){
this.currentUrl = location.hash.slice(1) || '/';
this.route.get(this.currentUrl || '')();
}
Route.prototype.init = function (){
window.addEventListener('load', this.refreshRoute.bind(this), false);
window.addEventListener('hashchange', this.refreshRoute.bind(this), false);
}
window.Route = new Route();
window.Route.init();
11、函数柯里化
柯里化:将接受多个参数的函数变换成接收一个单一参数(最初函数的第一个参数)的函数,并返回一个接收剩余参数的新函数。
作用:参数复用、提前返回和延迟执行。
function curry(fn) {
var length = fn.length;
var args = args || [];
return function() {
var newArgs = args.concat(Array.prototype.slice.call(arguments))
// 目前收集到的参数小于最初的length,则继续收集参数
if(newArgs.length < length) {
return curry.call(this, fn, newArgs);
} else {
return fn.apply(this, newArgs);
}
}
}
12、instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
function instanceOf (left, right) {
if (typeof left !== 'object' || left === null) {
return false;
}
let proto = Object.getPrototypeOf(left);
while (proto) {
if (right.prototype === proto) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
13、数组
数组扁平化
// for循环
function flatten(arr) {
var array = [];
for(var i = 0; i < arr.length; i ++) {
if (Array.isArray(arr[i])) {
array = array.concat(flatten(arr[i]));
} else {
array.push(arr[i]);
}
}
return array;
}
// reduce
function flatten(arr) {
return arr.reduce(function(array, item){
return array.concat(Array.isArray(item) ? flatten(item) : item);
}, [])
}
数组去重
var arr = [1,1,1,2,2,2,3,3,4,5];
var newArr = [];
// forEach
arr.forEach(item => {
if(newArr.indexOf(item) === -1){
newArr.push(item);
}
})
// set
newArr = [...new Set(arr)];
map
Array.prototype.map = function (callback, context){
let arr = [];
for(var k=0; k<this.length; k++){
arr.push(callback.call(context, this[k], k, this));
}
return arr;
}
reduce
Array.prototype.reduce = function (callback, value){
for(var i=0; i<this.length; i++){
value = callback(this[i], value);
}
return value;
}
forEach
Array.prototype.forEach= function (callback){
for (var i = 0; i < this.length; i++) {
callback(this[i])
}
}
14、sleep
function sleep(time){
return new Promise((resolve) => {
setTimeout(resolve, time)
});
}
15、用 setTimeout 实现 setInterval
// 递归执行
function Interval(fn, time){
function interval(){
setTimeout(interval, time);
fn();
}
setTimeout(interval, time);
}
// 规定次数
function Interval(fn, time, num){
function interval(){
if(typeof num === 'undefined' || num --> 0){
setTimeout(interval, time);
try{
fn();
} catch (e){
num = 0;
throw e.toString();
}
}
}
setTimeout(interval, time);
}
16、图片加载
懒加载
function lazy(){
var imgs = document.getElementsByTagName('img');
// 视口高度
var viewHeight = document.documentElement.clientHeight;
// 滚动条高度
var scrollHeight = document.documentElement.scrollTop || document.body.scrollTop;
for(var i=0; i<imgs.length; i++){
var offsetHeight = imgs[i].offsetTop;
if(offsetHeight < viewHeight + scrollHeight){
imgs[i].src = imgs[i].dataset.src;
}
}
}
滚动加载
window.addEventListener('scroll', function (){
var clientHeight = document.documentElement.clientHeight;
var scrollTop = document.documentElement.scrollTop;
var scrollHeight = document.documentElement.scrollHeight;
if(clientHeight + scrollTop >= scrollHeight){
// 此时页面已滚动到底部
}
}, false)
参考文章:https://zhuanlan.zhihu.com/p/119027881、、https://www.jianshu.com/p/98df7dc36cb8、https://zhuanlan.zhihu.com/p/258068663