1、call 方法
/** * _call * * @param { context } context * @param { arguments } arguments */ Function.prototype._call = function(context) { // 如果没有传或传的值为空对象 context指向window
context = context || window let fn = Symbol(context) context[fn] = this //给context添加一个方法 指向this
// 处理参数 去除第一个参数this 其它传入fn函数
let args = [...arguments].slice(1) //[...xxx]把类数组变成数组,arguments为啥不是数组自行搜索 slice返回一个新数组
context[fn](...args) //执行fn
delete context[fn] //删除方法
} var obj = { name: 'Bob', age: '18', fn() { console.log(`My name is ${this.name}, my age is ${this.age}`) } } var dog = { name: 'Snoopy', age: 3 } obj.fn.call(dog,'daddata','ttt','yuyuyuuy') // My name is Snoby, my age is 3
obj.fn._call(dog,'daddata','ttt','yuyuyuuy') // My name is Snoby, my age is 3
2、 apply 方法
Function.prototype._apply = function(context) { // 如果没有传或传的值为空对象 context指向window
context = context || window let fn = Symbol(context) context[fn] = this let arg = [...arguments].slice(1) context[fn](arg) //执行fn
delete context[fn] //删除方法
}
3、bind方法
/** * _bind * * @param {*} context */ Function.prototype._bind = function (context) { //返回一个绑定this的函数,我们需要在此保存this
let self = this
// 可以支持柯里化传参,保存参数
let arg = [...arguments].slice(1) // 返回一个函数
return function () { //同样因为支持柯里化形式传参我们需要再次获取存储参数
let newArg = [...arguments] // 返回函数绑定this,传入两次保存的参数
//考虑返回函数有返回值做了return
return self.apply(context, arg.concat(newArg)) } }
4、promise方法
function MyPromise(exceptor) { let _this = this; _this.reason = ''; _this.value = ''; _this.resolveFnArr = []; _this.rejectFnArr = []; _this.status = 'pending'; function resolve(val) { if (_this.status === 'pending') { _this.value = val; _this.status = 'fulfiled'; _this.resolveFnArr.forEach(fn => fn()) } } function reject(reason) { if (_this.status === 'pending') { _this.reason = reason; _this.status = 'reject'; _this.rejectFnArr.forEach(fn => fn()) } } try { exceptor(resolve, reject); } catch (error) { reject(); } } MyPromise.prototype.then = function(resolve, reject) { let _this = this; if (_this.status === 'fulfiled') { resolve(_this.value); } if (_this.status === 'reject') { reject(_this.reason); } if (_this.status === 'pending') { _this.resolveFnArr.push(() => {resolve(_this.value)}) _this.rejectFnArr.push(() => {reject(_this.reason)}) } }
5、全面的promise写法
(function(window,undefined){ // resolve 和 reject 最终都会调用该函数
var final = function(status,value){ var _this = this, fn, status; if(_this._status !== 'PENDING') return; // 所以的执行都是异步调用,保证then是先执行的
setTimeout(function(){ _this._status = status; status = _this._status === 'FULFILLED' queue = _this[status ? '_resolves' : '_rejects']; while(fn = queue.shift()) { value = fn.call(_this, value) || value; } _this[status ? '_value' : '_reason'] = value; _this['_resolves'] = _this['_rejects'] = undefined; }); } //参数是一个函数,内部提供两个函数作为该函数的参数,分别是resolve 和 reject
var MyPromise = function(resolver){ if (!(typeof resolver === 'function' )) throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); //如果不是promise实例,就new一个
if(!(this instanceof MyPromise)) return new MyPromise(resolver); var _this = this; _this._value; _this._reason; _this._status = 'PENDING'; //存储状态
_this._resolves = []; _this._rejects = []; // var resolve = function(value) { //由於apply參數是數組
final.apply(_this,['FULFILLED'].concat([value])); } var reject = function(reason){ final.apply(_this,['REJECTED'].concat([reason])); } resolver(resolve,reject); } MyPromise.prototype.then = function(onFulfilled,onRejected){ var _this = this; // 每次返回一个promise,保证是可thenable的
return new MyPromise(function(resolve,reject){ function handle(value) { // 這一步很關鍵,只有這樣才可以將值傳遞給下一個resolve
var ret = typeof onFulfilled === 'function' && onFulfilled(value) || value; //判断是不是promise 对象
if (ret && typeof ret ['then'] == 'function') { ret.then(function(value) { resolve(value); }, function(reason) { reject(reason); }); } else { resolve(ret); } } function errback(reason){ reason = typeof onRejected === 'function' && onRejected(reason) || reason; reject(reason); } if(_this._status === 'PENDING'){ _this._resolves.push(handle); _this._rejects.push(errback); }else if(_this._status === FULFILLED){ // 状态改变后的then操作,立刻执行
callback(_this._value); }else if(_this._status === REJECTED){ errback(_this._reason); } }); } MyPromise.prototype.catch = function(onRejected){ return this.then(undefined, onRejected) } MyPromise.prototype.delay = function(ms,value){ return this.then(function(ori){ return MyPromise.delay(ms,value || ori); }) } MyPromise.delay = function(ms,value){ return new MyPromise(function(resolve,reject){ setTimeout(function(){ resolve(value); console.log('1'); },ms); }) } MyPromise.resolve = function(arg){ return new MyPromise(function(resolve,reject){ resolve(arg) }) } MyPromise.reject = function(arg){ return MyPromise(function(resolve,reject){ reject(arg) }) } MyPromise.all = function(promises){ if (!Array.isArray(promises)) { throw new TypeError('You must pass an array to all.'); } return MyPromise(function(resolve,reject){ var i = 0, result = [], len = promises.length, count = len //这里与race中的函数相比,多了一层嵌套,要传入index
function resolver(index) { return function(value) { resolveAll(index, value); }; } function rejecter(reason){ reject(reason); } function resolveAll(index,value){ result[index] = value; if( --count == 0){ resolve(result) } } for (; i < len; i++) { promises[i].then(resolver(i),rejecter); } }); } MyPromise.race = function(promises){ if (!Array.isArray(promises)) { throw new TypeError('You must pass an array to race.'); } return MyPromise(function(resolve,reject){ var i = 0, len = promises.length; function resolver(value) { resolve(value); } function rejecter(reason){ reject(reason); } for (; i < len; i++) { promises[i].then(resolver,rejecter); } }); } window.MyPromise = MyPromise; })(window);
6、filter
Array.prototype.filter = function(callback, thisArg) { if (this == undefined) { throw new TypeError('this is null or not undefined'); } if (typeof callback !== 'function') { throw new TypeError(callback + 'is not a function'); } const res = []; // 让O成为回调函数的对象传递(强制转换对象)
const O = Object(this); // >>>0 保证len为number,且为正整数
const len = O.length >>> 0; for (let i = 0; i < len; i++) { // 检查i是否在O的属性(会检查原型链)
if (i in O) { // 回调函数调用传参
if (callback.call(thisArg, O[i], i, O)) { res.push(O[i]); } } } return res; }
7、map方法
Array.prototype.map = function(callback, thisArg) { if (this == undefined) { throw new TypeError('this is null or not defined'); } if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } const res = []; // 同理
const O = Object(this); const len = O.length >>> 0; for (let i = 0; i < len; i++) { if (i in O) { // 调用回调函数并传入新数组
res[i] = callback.call(thisArg, O[i], i, this); } } return res; }
8、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++; } }
9、reduce方法
Array.prototype.reduce = function(callback, initialValue) { if (this == undefined) { throw new TypeError('this is null or not defined'); } if (typeof callback !== 'function') { throw new TypeError(callbackfn + ' is not a function'); } const O = Object(this); const len = this.length >>> 0; let accumulator = initialValue; let k = 0; // 如果第二个参数为undefined的情况下
// 则数组的第一个有效值作为累加器的初始值
if (accumulator === undefined) { while (k < len && !(k in O)) { k++; } // 如果超出数组界限还没有找到累加器的初始值,则TypeError
if (k >= len) { throw new TypeError('Reduce of empty array with no initial value'); } accumulator = O[k++]; } while (k < len) { if (k in O) { accumulator = callback.call(undefined, accumulator, O[k], k, O); } k++; } return accumulator; }
10、debounce(防抖)
const debounce = (fn, time) => { let timeout = null; return function() { clearTimeout(timeout) timeout = setTimeout(() => { fn.apply(this, arguments); }, time); } };
11、throttle(节流)
var throttle = function(func, delay) {
var prev = Date.now();
return function() {
var context = this;
var args = arguments;
var now = Date.now();
if (now - prev >= delay) {
func.apply(context, args);
prev = Date.now();
}
}
}
function handle() {
console.log(Math.random());
}
window.addEventListener('scroll', throttle(handle, 1000));
12、模拟new操作
function newOperator(ctor, ...args) { if (typeof ctor !== 'function') { throw new TypeError('Type Error'); } const obj = Object.create(ctor.prototype); const res = ctor.apply(obj, args); const isObject = typeof res === 'object' && res !== null; const isFunction = typeof res === 'function'; return isObject || isFunction ? res : obj; }
13、instanceof
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); } }
14、原型继承
function Parent() { this.name = 'parent'; } function Child() { Parent.call(this); this.type = 'children'; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child;
15、Object.assign
Object.defineProperty(Object, 'assign', { value: function(target, ...args) { if (target == null) { return new TypeError('Cannot convert undefined or null to object'); } // 目标对象需要统一是引用数据类型,若不是会自动转换
const to = Object(target); for (let i = 0; i < args.length; i++) { // 每一个源对象
const nextSource = args[i]; if (nextSource !== null) { // 使用for...in和hasOwnProperty双重判断,确保只拿到本身的属性、方法(不包含继承的)
for (const nextKey in nextSource) { if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }, // 不可枚举
enumerable: false, writable: true, configurable: true, })
16、深拷贝
const cloneDeep1 = (target, hash = new WeakMap()) => { // 对于传入参数处理
if (typeof target !== 'object' || target === null) { return target; } // 哈希表中存在直接返回
if (hash.has(target)) return hash.get(target); const cloneTarget = Array.isArray(target) ? [] : {}; hash.set(target, cloneTarget); // 针对Symbol属性
const symKeys = Object.getOwnPropertySymbols(target); if (symKeys.length) { symKeys.forEach(symKey => { if (typeof target[symKey] === 'object' && target[symKey] !== null) { cloneTarget[symKey] = cloneDeep1(target[symKey]); } else { cloneTarget[symKey] = target[symKey]; } }) } for (const i in target) { if (Object.prototype.hasOwnProperty.call(target, i)) { cloneTarget[i] =
typeof target[i] === 'object' && target[i] !== null
? cloneDeep1(target[i], hash) : target[i]; } } return cloneTarget; }
17、JSONP
const jsonp = ({ url, params, callbackName }) => { const generateUrl = () => { let dataSrc = ''; for (let key in params) { if (Object.prototype.hasOwnProperty.call(params, key)) { dataSrc += `${key}=${params[key]}&`; } } dataSrc += `callback=${callbackName}`; return `${url}?${dataSrc}`; } return new Promise((resolve, reject) => { const scriptEle = document.createElement('script'); scriptEle.src = generateUrl(); document.body.appendChild(scriptEle); window[callbackName] = data => { resolve(data); document.removeChild(scriptEle); } }) }
18、AJAX
const getJSON = function(url) { return new Promise((resolve, reject) => { const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp'); xhr.open('GET', url, false); xhr.setRequestHeader('Accept', '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(); }) }
19、图片懒加载
// 可以给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; } } }
20、滚动加载
// 原理就是监听页面滚动事件,分析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);
21、渲染几万条数据不卡住页面
// 渲染大数据时,合理使用createDocumentFragment和requestAnimationFrame,将操作切分为一小段一小段执行。
setTimeout(() => { // 插入十万条数据
const total = 100000; // 一次插入的数据
const once = 20; // 插入数据需要的次数
const loopCount = Math.ceil(total / once); let countOfRender = 0; const ul = document.querySelector('ul'); // 添加数据的方法
function add() { const fragment = document.createDocumentFragment(); for(let i = 0; i < once; i++) { const li = document.createElement('li'); li.innerText = Math.floor(Math.random() * total); fragment.appendChild(li); } ul.appendChild(fragment); countOfRender += 1; loop(); } function loop() { if(countOfRender < loopCount) { window.requestAnimationFrame(add); } } loop(); }, 0)
22、将VirtualDom转化为真实DOM结构
// vnode结构: // { // tag, // attrs, // children, // }
//Virtual DOM => DOM
function render(vnode, container) { container.appendChild(_render(vnode)); } function _render(vnode) { // 如果是数字类型转化为字符串
if (typeof vnode === 'number') { vnode = String(vnode); } // 字符串类型直接就是文本节点
if (typeof vnode === 'string') { return document.createTextNode(vnode); } // 普通DOM
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 => render(child, dom)); return dom; }