Array.prototype.forEach()
Array.prototype.forEach = function(callback, thisArg) { if (this == null) { throw new TypeError('this is null or not defined'); } if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function'); } const O = Object(this); const len = O.length >>> 0; let k = 0; while (k < len) { if (k in O) { callback.call(thisArg, O[k], k, O); } k++; }}复制代码
实现简略路由
// 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]() }}复制代码
图片懒加载
能够给img标签对立自定义属性data-src='default.png'
,当检测到图片呈现在窗口之后再补充src属性,此时才会进行图片资源加载。
function lazyload() { const imgs = document.getElementsByTagName('img'); const len = imgs.length; // 视口的高度 const viewHeight = document.documentElement.clientHeight; // 滚动条高度 const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop; for (let i = 0; i < len; i++) { const offsetHeight = imgs[i].offsetTop; if (offsetHeight < viewHeight + scrollHeight) { const src = imgs[i].dataset.src; imgs[i].src = src; } }}// 能够应用节流优化一下window.addEventListener('scroll', lazyload);复制代码
解析 URL Params 为对象
let url = 'http://www.domain.com/?user=anonymous&id=123&id=456&city=%E5%8C%97%E4%BA%AC&enabled';parseParam(url)/* 后果{ user: 'anonymous', id: [ 123, 456 ], // 反复呈现的 key 要组装成数组,能被转成数字的就转成数字类型 city: '北京', // 中文需解码 enabled: true, // 未指定值得 key 约定为 true}*/复制代码
function parseParam(url) { const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 前面的字符串取出来 const paramsArr = paramsStr.split('&'); // 将字符串以 & 宰割后存到数组中 let 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;}复制代码
实现 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);}// 接口返回的数据格式handleRes({a: 1, b: 2});复制代码
instanceof
instanceof
运算符用于检测构造函数的prototype
属性是否呈现在某个实例对象的原型链上。
const myInstanceof = (left, right) => { // 根本数据类型都返回false if (typeof left !== 'object' || left === null) return false; let proto = Object.getPrototypeOf(left); while (true) { if (proto === null) return false; if (proto === right.prototype) return true; proto = Object.getPrototypeOf(proto); }}复制代码
实现prototype继承
所谓的原型链继承就是让新实例的原型等于父类的实例:
//父办法function SupperFunction(flag1){ this.flag1 = flag1;}//子办法function SubFunction(flag2){ this.flag2 = flag2;}//父实例var superInstance = new SupperFunction(true);//子继承父SubFunction.prototype = superInstance;//子实例var subInstance = new SubFunction(false);//子调用本人和父的属性subInstance.flag1; // truesubInstance.flag2; // false复制代码
手写 Promise.all
1) 外围思路
- 接管一个 Promise 实例的数组或具备 Iterator 接口的对象作为参数
- 这个办法返回一个新的 promise 对象,
- 遍历传入的参数,用Promise.resolve()将参数"包一层",使其变成一个promise对象
- 参数所有回调胜利才是胜利,返回值数组与参数程序统一
- 参数数组其中一个失败,则触发失败状态,第一个触发失败的 Promise 错误信息作为 Promise.all 的错误信息。
2)实现代码
一般来说,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) }) } })}// testlet 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) // [3, 1, 2]})复制代码
手写 new 操作符
在调用 new
的过程中会产生以上四件事件:
(1)首先创立了一个新的空对象
(2)设置原型,将对象的原型设置为函数的 prototype 对象。
(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象增加属性)
(4)判断函数的返回值类型,如果是值类型,返回创立的对象。如果是援用类型,就返回这个援用类型的对象。
function objectFactory() { let newObject = null; let constructor = Array.prototype.shift.call(arguments); let result = null; // 判断参数是否是一个函数 if (typeof constructor !== "function") { console.error("type error"); return; } // 新建一个空对象,对象的原型为构造函数的 prototype 对象 newObject = Object.create(constructor.prototype); // 将 this 指向新建对象,并执行函数 result = constructor.apply(newObject, arguments); // 判断返回对象 let flag = result && (typeof result === "object" || typeof result === "function"); // 判断返回后果 return flag ? result : newObject;}// 应用办法objectFactory(构造函数, 初始化参数);复制代码
实现数组的filter办法
Array.prototype._filter = function(fn) { if (typeof fn !== "function") { throw Error('参数必须是一个函数'); } const res = []; for (let i = 0, len = this.length; i < len; i++) { fn(this[i]) && res.push(this[i]); } return res;}复制代码
滚动加载
原理就是监听页面滚动事件,剖析clientHeight、scrollTop、scrollHeight三者的属性关系。
window.addEventListener('scroll', function() { const clientHeight = document.documentElement.clientHeight; const scrollTop = document.documentElement.scrollTop; const scrollHeight = document.documentElement.scrollHeight; if (clientHeight + scrollTop >= scrollHeight) { // 检测到滚动至页面底部,进行后续操作 // ... }}, false);复制代码
Function.prototype.apply()
第一个参数是绑定的this,默认为window
,第二个参数是数组或类数组
Function.prototype.apply = function(context = window, args) { if (typeof this !== 'function') { throw new TypeError('Type Error'); } const fn = Symbol('fn'); context[fn] = this; const res = context[fn](...args); delete context[fn]; return res;}复制代码
实现字符串翻转
在字符串的原型链上增加一个办法,实现字符串翻转:
String.prototype._reverse = function(a){ return a.split("").reverse().join("");}var obj = new String();var res = obj._reverse ('hello');console.log(res); // olleh复制代码
须要留神的是,必须通过实例化对象之后再去调用定义的办法,不然找不到该办法。
Promise
// 模仿实现Promise// Promise利用三大伎俩解决回调天堂:// 1. 回调函数提早绑定// 2. 返回值穿透// 3. 谬误冒泡// 定义三种状态const PENDING = 'PENDING'; // 进行中const FULFILLED = 'FULFILLED'; // 已胜利const REJECTED = 'REJECTED'; // 已失败class Promise { constructor(exector) { // 初始化状态 this.status = PENDING; // 将胜利、失败后果放在this上,便于then、catch拜访 this.value = undefined; this.reason = undefined; // 胜利态回调函数队列 this.onFulfilledCallbacks = []; // 失败态回调函数队列 this.onRejectedCallbacks = []; const resolve = value => { // 只有进行中状态能力更改状态 if (this.status === PENDING) { this.status = FULFILLED; this.value = value; // 胜利态函数顺次执行 this.onFulfilledCallbacks.forEach(fn => fn(this.value)); } } const reject = reason => { // 只有进行中状态能力更改状态 if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; // 失败态函数顺次执行 this.onRejectedCallbacks.forEach(fn => fn(this.reason)) } } try { // 立刻执行executor // 把外部的resolve和reject传入executor,用户可调用resolve和reject exector(resolve, reject); } catch(e) { // executor执行出错,将谬误内容reject抛出去 reject(e); } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function'? onRejected : reason => { throw new Error(reason instanceof Error ? reason.message : reason) } // 保留this const self = this; return new Promise((resolve, reject) => { if (self.status === PENDING) { self.onFulfilledCallbacks.push(() => { // try捕捉谬误 try { // 模仿微工作 setTimeout(() => { const result = onFulfilled(self.value); // 分两种状况: // 1. 回调函数返回值是Promise,执行then操作 // 2. 如果不是Promise,调用新Promise的resolve函数 result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } }); self.onRejectedCallbacks.push(() => { // 以下同理 try { setTimeout(() => { const result = onRejected(self.reason); // 不同点:此时是reject result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } }) } else if (self.status === FULFILLED) { try { setTimeout(() => { const result = onFulfilled(self.value); result instanceof Promise ? result.then(resolve, reject) : resolve(result); }); } catch(e) { reject(e); } } else if (self.status === REJECTED) { try { setTimeout(() => { const result = onRejected(self.reason); result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } } }); } catch(onRejected) { return this.then(null, onRejected); } static resolve(value) { if (value instanceof Promise) { // 如果是Promise实例,间接返回 return value; } else { // 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED return new Promise((resolve, reject) => resolve(value)); } } static reject(reason) { return new Promise((resolve, reject) => { reject(reason); }) } static all(promiseArr) { const len = promiseArr.length; const values = new Array(len); // 记录曾经胜利执行的promise个数 let count = 0; return new Promise((resolve, reject) => { for (let i = 0; i < len; i++) { // Promise.resolve()解决,确保每一个都是promise实例 Promise.resolve(promiseArr[i]).then( val => { values[i] = val; count++; // 如果全副执行完,返回promise的状态就能够扭转了 if (count === len) resolve(values); }, err => reject(err), ); } }) } static race(promiseArr) { return new Promise((resolve, reject) => { promiseArr.forEach(p => { Promise.resolve(p).then( val => resolve(val), err => reject(err), ) }) }) }}复制代码
实现Promise
我很早之前实现过一版,而且正文很多,然而竟然找不到了,这是在网络上找了一版带正文的,目测没有大问题,具体过程能够看这篇史上最易读懂的 Promise/A+ 齐全实现
var PromisePolyfill = (function () { // 和reject不同的是resolve须要尝试开展thenable对象 function tryToResolve (value) { if (this === value) { // 次要是避免上面这种状况 // let y = new Promise(res => setTimeout(res(y))) throw TypeError('Chaining cycle detected for promise!') } // 依据标准2.32以及2.33 对对象或者函数尝试开展 // 保障S6之前的 polyfill 也能和ES6的原生promise混用 if (value !== null && (typeof value === 'object' || typeof value === 'function')) { try { // 这里记录这次then的值同时要被try包裹 // 次要起因是 then 可能是一个getter, 也也就是说 // 1. value.then可能报错 // 2. value.then可能产生副作用(例如屡次执行可能后果不同) var then = value.then // 另一方面, 因为无奈保障 then 的确会像预期的那样只调用一个onFullfilled / onRejected // 所以减少了一个flag来避免resolveOrReject被屡次调用 var thenAlreadyCalledOrThrow = false if (typeof then === 'function') { // 是thenable 那么尝试开展 // 并且在该thenable状态扭转之前this对象的状态不变 then.bind(value)( // onFullfilled function (value2) { if (thenAlreadyCalledOrThrow) return thenAlreadyCalledOrThrow = true tryToResolve.bind(this, value2)() }.bind(this), // onRejected function (reason2) { if (thenAlreadyCalledOrThrow) return thenAlreadyCalledOrThrow = true resolveOrReject.bind(this, 'rejected', reason2)() }.bind(this) ) } else { // 领有then 然而then不是一个函数 所以也不是thenable resolveOrReject.bind(this, 'resolved', value)() } } catch (e) { if (thenAlreadyCalledOrThrow) return thenAlreadyCalledOrThrow = true resolveOrReject.bind(this, 'rejected', e)() } } else { // 根本类型 间接返回 resolveOrReject.bind(this, 'resolved', value)() } } function resolveOrReject (status, data) { if (this.status !== 'pending') return this.status = status this.data = data if (status === 'resolved') { for (var i = 0; i < this.resolveList.length; ++i) { this.resolveList[i]() } } else { for (i = 0; i < this.rejectList.length; ++i) { this.rejectList[i]() } } } function Promise (executor) { if (!(this instanceof Promise)) { throw Error('Promise can not be called without new !') } if (typeof executor !== 'function') { // 非标准 但与Chrome谷歌保持一致 throw TypeError('Promise resolver ' + executor + ' is not a function') } this.status = 'pending' this.resolveList = [] this.rejectList = [] try { executor(tryToResolve.bind(this), resolveOrReject.bind(this, 'rejected')) } catch (e) { resolveOrReject.bind(this, 'rejected', e)() } } Promise.prototype.then = function (onFullfilled, onRejected) { // 返回值穿透以及谬误穿透, 留神谬误穿透用的是throw而不是return,否则的话 // 这个then返回的promise状态将变成resolved即接下来的then中的onFullfilled // 会被调用, 然而咱们想要调用的是onRejected if (typeof onFullfilled !== 'function') { onFullfilled = function (data) { return data } } if (typeof onRejected !== 'function') { onRejected = function (reason) { throw reason } } var executor = function (resolve, reject) { setTimeout(function () { try { // 拿到对应的handle函数解决this.data // 并以此为根据解析这个新的Promise var value = this.status === 'resolved' ? onFullfilled(this.data) : onRejected(this.data) resolve(value) } catch (e) { reject(e) } }.bind(this)) } // then 承受两个函数返回一个新的Promise // then 本身的执行永远异步与onFullfilled/onRejected的执行 if (this.status !== 'pending') { return new Promise(executor.bind(this)) } else { // pending return new Promise(function (resolve, reject) { this.resolveList.push(executor.bind(this, resolve, reject)) this.rejectList.push(executor.bind(this, resolve, reject)) }.bind(this)) } } // for prmise A+ test Promise.deferred = Promise.defer = function () { var dfd = {} dfd.promise = new Promise(function (resolve, reject) { dfd.resolve = resolve dfd.reject = reject }) return dfd } // for prmise A+ test if (typeof module !== 'undefined') { module.exports = Promise } return Promise})()PromisePolyfill.all = function (promises) { return new Promise((resolve, reject) => { const result = [] let cnt = 0 for (let i = 0; i < promises.length; ++i) { promises[i].then(value => { cnt++ result[i] = value if (cnt === promises.length) resolve(result) }, reject) } })}PromisePolyfill.race = function (promises) { return new Promise((resolve, reject) => { for (let i = 0; i < promises.length; ++i) { promises[i].then(resolve, reject) } })}复制代码
手写 Promise.race
该办法的参数是 Promise 实例数组, 而后其 then 注册的回调办法是数组中的某一个 Promise 的状态变为 fulfilled 的时候就执行. 因为 Promise 的状态只能扭转一次, 那么咱们只须要把 Promise.race 中产生的 Promise 对象的 resolve 办法, 注入到数组中的每一个 Promise 实例中的回调函数中即可.
Promise.race = function (args) { return new Promise((resolve, reject) => { for (let i = 0, len = args.length; i < len; i++) { args[i].then(resolve, reject) } })}复制代码
函数柯里化的实现
函数柯里化指的是一种将应用多个参数的一个函数转换成一系列应用一个参数的函数的技术。
function curry(fn, args) { // 获取函数须要的参数长度 let length = fn.length; args = args || []; return function() { let subArgs = args.slice(0); // 拼接失去现有的所有参数 for (let i = 0; i < arguments.length; i++) { subArgs.push(arguments[i]); } // 判断参数的长度是否曾经满足函数所需参数的长度 if (subArgs.length >= length) { // 如果满足,执行函数 return fn.apply(this, subArgs); } else { // 如果不满足,递归返回科里化的函数,期待参数的传入 return curry.call(this, fn, subArgs); } };}// es6 实现function curry(fn, ...args) { return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);}复制代码
实现公布-订阅模式
class EventCenter{ // 1. 定义事件容器,用来装事件数组 let 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] } } }}复制代码
实现AJAX申请
AJAX是 Asynchronous JavaScript and XML 的缩写,指的是通过 JavaScript 的 异步通信,从服务器获取 XML 文档从中提取数据,再更新以后网页的对应局部,而不必刷新整个网页。
创立AJAX申请的步骤:
- 创立一个 XMLHttpRequest 对象。
- 在这个对象上应用 open 办法创立一个 HTTP 申请,open 办法所须要的参数是申请的办法、申请的地址、是否异步和用户的认证信息。
- 在发动申请前,能够为这个对象增加一些信息和监听函数。比如说能够通过 setRequestHeader 办法来为申请增加头信息。还能够为这个对象增加一个状态监听函数。一个 XMLHttpRequest 对象一共有 5 个状态,当它的状态变动时会触发onreadystatechange 事件,能够通过设置监听函数,来解决申请胜利后的后果。当对象的 readyState 变为 4 的时候,代表服务器返回的数据接管实现,这个时候能够通过判断申请的状态,如果状态是 2xx 或者 304 的话则代表返回失常。这个时候就能够通过 response 中的数据来对页面进行更新了。
- 当对象的属性和监听函数设置实现后,最初调用 sent 办法来向服务器发动申请,能够传入参数作为发送的数据体。
const SERVER_URL = "/server";let xhr = new XMLHttpRequest();// 创立 Http 申请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");// 发送 Http 申请xhr.send(null);复制代码
手写类型判断函数
function getType(value) { // 判断数据是 null 的状况 if (value === null) { return value + ""; } // 判断数据是援用类型的状况 if (typeof value === "object") { let valueClass = Object.prototype.toString.call(value), type = valueClass.split(" ")[1].split(""); type.pop(); return type.join("").toLowerCase(); } else { // 判断数据是根本数据类型的状况和函数的状况 return typeof value; }}复制代码