javascript原生代码实现及代码总结
JsonP等实现
注意:JSONP可能会引起csrf攻击
function jsonp({url = '', params = {}, cb="cb"}) {
if (!url) return
const data2Url = data => {
return Object.keys(data).reduce((acc, cur) => {
acc.push(`${cur}=${data[cur]}`)
return acc
}, []).join('&')
}
return new Promise((resolve,reject) => {
const cbFn = `jsonp_${Date.now()}` // 定义函数名
const obody = document.body
const script = document.createElement('script')
params[cb] = cbFn // 将函数名放在回掉函数中
window[cbFn] = function(res) {
res ? resolve(res) : reject('error');
obody.removeChild(script)
window[cbFn] = null
}
script.src = `${url}?${data2Url(params)}`
obody.appendChild(script)
})
}
截流&防抖
var COUNT = 0;
function testFn() {console.log('testFN 被调用了 ' + ++COUNT + '次')} //被调用的函数
var throttle = function (fn, delay, atleast) { //调用的函数、自定义时间、多长时间至少会执行一次
var timer = null;
var previous = null;
return function () {
var now = +new Date();
if ( !previous ) previous = now;
if ( atleast && now - previous > atleast ) {
fn();
// 重置上一次开始时间为本次结束时间
previous = now;
clearTimeout(timer);
} else {
clearTimeout(timer);
timer = setTimeout(function() {
fn();
previous = null;
}, delay);
}
}
};
window.onscroll = throttle(testFn, 500, 1000);
深拷贝
- 方法一:
JSON.parse(JSON.stringify(obj))
性能最快- 具有循环引用的对象时,报错
- 当值为函数、undefined、或symbol时,无法拷贝
- 方法二:
递归
// 深度优先
// map是为了解决循环引用问题
function deepCloneDFS(obj, map = new Map()){
if (obj instanceof RegExp) return new RegExp(obj)
if (obj instanceof Date) return new Date(obj)
if (typeof obj !== "object" || obj === null) return obj;
let newObj=Array.isArray(obj)?[]:{}
if (map.get(obj)) {
return obj;
}
map.set(obj, newObj);
for(var attr in obj){
if (Object.prototype.hasOwnProperty.call(obj, attr)) {
newObj[attr]=deepCloneDFS(obj[attr], map)
}
}
return newObj
}
// 广度优先
function getEmpty(o){
if(Object.prototype.toString.call(o) === '[object Object]'){
return {};
}
if(Object.prototype.toString.call(o) === '[object Array]'){
return [];
}
return o;
}
function deepCloneBFS(origin) {
let queue = [];
let map = new Map(); // 记录出现过的对象,用于处理环
let target = getEmpty(origin);
if(target !== origin){
queue.push([origin, target]);
map.set(origin, target);
}
while(queue.length){
let [ori, tar] = queue.shift();
for(let key in ori){
// 处理环状
if(map.get(ori[key])){
tar[key] = map.get(ori[key]);
continue;
}
tar[key] = getEmpty(ori[key]);
if(tar[key] !== ori[key]){
queue.push([ori[key], tar[key]]);
map.set(ori[key], tar[key]);
}
}
}
return target;
}
柯里化函数 currying
主要作用
- 提前绑定好函数里面的某些参数,达到参数复用的效果,提高了适用性.
- 固定易变因素
- 延迟计算
function currying(fn, ...args) {
return args.length < fn.length ? (...args2) => currying(fn, ...args, ...args2) : fn(...args)
}
计算两个数组的交集
需要注意的是当两个数组是[1,1]和[1]的时候,他们的交集是[1]
方法一
const intersect = (nums1, nums2) => {
const map = {}
const res = []
for (let n of nums1) {
if (map[n]) {
map[n]++
} else {
map[n] = 1
}
}
for (let n of nums2) {
if (map[n] > 0) {
res.push(n)
map[n]--
}
}
return res
}
方法二
const intersect = (nums1, nums2) => {
return nums1.filter(item => {
let idx = nums2.indexOf(item)
if (idx !== -1) {
nums2.splice(idx, 1)
return item
}
})
}
实现DUFF装置
DUFF是红宝书中提到的一种可以很大限度降低循环次数的方式
var values = [];
// 假设有 50w 条数据
for (var i = 0; i < 500000; i++) {
values.push(i);
}
var doSomething = (num) => console.log(num);
var count = Math.ceil(values.length / 8); // 计算循环次数
var start = values.length % 8; // 余数
var j = 0;
do {
switch (start) {
case 0: doSomething(j++);
case 7: doSomething(j++);
case 6: doSomething(j++);
case 5: doSomething(j++);
case 4: doSomething(j++);
case 3: doSomething(j++);
case 2: doSomething(j++);
case 1: doSomething(j++);
}
start = 0;
} while (--count > 0);
监听动态脚步创建
function loadscript(url,callback){
var script=document.createElement('script');
script.type='text/javascript';
if(script.readyState){ //ie
script.onreadystatechange=function(){
if(script.readyState == 'loaded' || script.readyState == 'complete'){
script.onreadystatechange = null;
callback()
}
};
}else{ //其他浏览器
script.onload=function(){
callback()
};
};
script.src=url;
document.getElementsByTagName('head')[0].appendChild(script)
}
new方法的实现
原理:
- 我们创建了一个空对象obj
- 我们将这个空对象的__proto__成员指向了Base函数对象prototype成员对象
- 我们将Base函数对象的this指针替换成obj,然后再调用Base函数,于是我们就给obj对象赋值了一个id成员变量,这个成员变量的值是”base”
- 如果有返回且返回为对象,则返回,如果没有,则默认返回原对象
function New(fun) {
var obj = {};
if (fun.prototype !== null) {
obj.__proto__ = fun.prototype;
//obj = Object.create(fun.prototype)
}
var ret = fun.call(obj);
if((typeof ret === "object" || typeof ret === "function") && ret !== null){
return ret
}else {
return obj
}
}
instanceof的实现方式
用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链
function _instanceof(left, right) {
const prototype = right.protytype
let left = Object.getPrototypeOf(left)
while(true) {
if(left === null) {
return false
}
if (left === prototype) {
return true
}
left = left._proto_
}
}
call/apply 的实现原理是什么?
call/apply
主要的功能就是更改this
指向并立即执行;它们的区别但就是传递的参数不同func.call(thisArg, arg1, arg2, ...)
,func.apply(thisArg, [argsArray])
Function.prototype.call = (content = window) => {
const fn = Symbol(); // 防止污染属性
content[fn] = this
const args = [...arguments].slice(1)
const result = content[fn](...args)
delete content[fn]
return result
}
apply
和call
的实现方式基本类似,仅仅是在处理参数上多做下处理就好了
实现LazyMan 类
LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food');
// Hi I am Tony
// 等待了5秒...
// I am eating lunch
// I am eating dinner
// 等待了10秒...
// I am eating junk food
实现方式:
class LazyManClass {
constructor(name) {
this.tasks = []
this.name = name
console.log(`Hi I am ${this.name}`)
setTimeout(() => {
this.next()
})
}
eat(food) {
const fn = () => {
console.log(`I am eating ${food}`)
this.next()
}
this.tasks.push(fn.bind(this))
return this
}
sleep(time) {
const fn = () => {
console.log(`等待了${time}秒...`);
setTimeout(_ => {
this.next()
}, time * 1000)
}
this.tasks.push(fn.bind(this))
return this
}
sleepFirst(time) {
const fn = () => {
console.log(`等待了${time}秒...`);
setTimeout(_ => {
this.next()
}, time * 1000)
}
this.tasks.unshift(fn.bind(this))
return this
}
next() {
const fn = this.tasks.shift()
fn && fn()
return this
}
}
const lazyMan = name => {
return new LazyManClass(name)
}
实现一个 EventEmitter 方法
类似Vue的event方法
class EventEmitter{
constructor() {
this.events = {}
}
on(name, fn) {
(this.events[name] || (this.events[name] = [])).push(fn)
return this
}
off(name, fn) {
if(!this.events[name]) return this
if (!fn) {
this.events[name] = null
return this
}
this.events[name] = this.events[name].filter(item => item !== fn)
return this
}
emit(name, ...args) {
const fn = this.events[name]
if(!fn) return this
fn.forEach(item => {
item.apply(this, args)
})
return this
}
once(name, fn) {
const once = function() {
fn.apply(this, arguments)
this.off(name, once)
}
this.events[name].push(once)
}
}
'rgb(255, 255, 255)' -> '#FFFFFF'
function rgb2hex(rgb) {
var rgb = rgb.match(/\d+/g);
var hex = (n) => {
return ("0" + Number(n).toString(16)).slice(-2);
// return Number(n).toString(16).padStart(2, '0')
}
return rgb.reduce((acc, cur) => acc + hex, '#').toUpperCase()
}
如何判断一个空对象
//1.
JSON.stringify(data) == "{}"
//2.for…in循环判断
//3.
Object.getOwnPropertyNames(data)
//4.
Object.kes(data)
Object.create 兼容实现
Object._create = (o) => {
let Fn = function() {}; // 临时的构造函数
Fn.prototype = o;
return new Fn;
}
简单版的Promise
class PromiseA {
constructor (init) {
this.PromiseStatus = 'pending'
var resolve = (val) => {
if (this.resolveCallback) {
this.PromiseStatus = 'fulfilled'
this.resolveCallback(val)
}
}
if (init) {
setTimeout(() => { // 为了支持同步任务
init(resolve, reject)
})
}
}
then (onFulfill, onReject) {
this.resolveCallback = onFulfill
this.rejectCallback = onReject
return this
}
}
模拟实现一个 Promise.finally
不管 Promise 对象最后状态如何,都会执行的操作
Promise.prototype._finally = function (callback) {
let P = this.constructor;
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => { throw reason })
);
};
实现 Promise.race()
有一个promise率先改变状态;则改变Promise状态并执行then方法
Promise.prototype._race = promises => new Promise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, reject)
})
})
实现 Promise.all()
所有promise同步执行完才会执行then方法; 并且只要有一个状态是rejected
,则最后结果就是rejected
Promise.prototype._all = function (promises) {
return new Promise((resolve, reject) => {
let index = 0;
let result = [];
if (promises.length === 0) {
resolve(result);
} else {
setTimeout(() => {
for (let i = 0; i < promises.length; i++) {
//promises[i] 可能是普通值
Promise.resolve(promises[i]).then((data) => {
result[i] = data;
if (++index === promises.length) {
resolve(result);
}
}, (err) => {
reject(err);
return;
});
}
})
}
});
}
实现 Promise.allSettled()
if (!Promise.allSettled) {
const rejectHandler = reason => ({status: "rejected", reason})
const resolveHandler = value => ({status: "fulfilled", value})
Promise.allSettled = promises =>
Promise.all(
promises.map((promise) =>
Promise.resolve(promise)
.then(resolveHandler, rejectHandler)
)
);
}
实现一个可以控制最大并发数的函数
const sendRequest = function(urls, max = 3, callback) {
let finished = 0
const total = urls.length
const handler = () => {
if (urls.length) {
const url = urls.shift()
fetch(url).then(() => {
finished++
handler()
}).catch(err => {
throw Error(err)
})
}
if (finished >= total) {
callback()
}
}
for(let i = 0; i < max; i++) {
handler()
}
}
实现简易的Symbol
获取页面 FPS
1s是否达到 60 帧,16.5ms左右渲染一次,即可间接地反映浏览器的渲染帧率,所以通过显示当前网页的 FPS 值,判断页面是否卡顿参考
var lastTime = performance.now();
var frame = 0;
var lastFameTime = performance.now();
var loop = function(time) {
var now = performance.now();
var fs = (now - lastFameTime);
lastFameTime = now;
var fps = Math.round(1000/fs);
console.log(fps)
frame++;
if (now >= 1000 + lastTime) {
var fps = Math.round( 1000 / (( now - lastTime ) / frame));
console.log(fps,'平均值')
frame = 0;
lastTime = now;
};
window.requestAnimFrame(loop);
}
loop()
一个极简的模板引擎
描述:
render("我是{{name}},年龄{{age}}", {
name: "lucifer",
age: 17
});
// 结果: 我是姓名,年龄18
实现:
function render(tpl, data) {
return tpl.replace(/\{\{(.+?)\}\}/g, function($1, $2) {
// $1 分组为 类似 {{name}}
// $2 分组为 类似 name
// 加上面的小括号就是为了方便拿到key而已
return data[$2];
});
}
camelCased (驼峰式) 命名与 kebab-case(短横线命名)互转
const hyphenate = function (str) {
const hyphenateRE = /\B([A-Z])/g
return str.replace(hyphenateRE, '_$1').toLowerCase()
}
console.log(hyphenate('aCDef')) // a_c_def
const camelize = function (str) {
const camelizeRE = /_(\w)/g
return str.replace(camelizeRE, function (_, $1) { return $1 ? $1.toUpperCase() : '' })
}
console.log(camelize('a_c_def')) // ACDef
实现JSON.stringify函数
function JSONStringify(obj) {
if (
obj === undefined ||
obj === null ||
typeof obj === "string" ||
typeof obj === "boolean" ||
typeof obj === "number"
) {
return obj;
}
if (typeof obj === "function") {
return "";
}
if (Array.isArray(obj)) {
let result = [];
for (let i = 0; i < obj.length; i++) {
result.push(JSONStringify(obj[i]));
}
return "[" + result.join(",") + "]";
} else {
let result = [];
for (let key in obj) {
result.push(`"${key}":${JSONStringify(obj[key])}`);
}
return "{" + result.join(",") + "}";
}
}
实现repeat方法
String.prototype.repeatString = function (num) {
return Array(num + 1).join(this);
}
AOP面向切面编程
AOP就是让一个函数在另一个函数之前或者之后执行; 在 js 中的妙用.参考
- 防止 window.onload 被二次覆盖.
- 无侵入的统计代码.
- 分离表单请求和校验.
- 给 ajax 请求动态添加参数.
- 职责链模式.
- 组合代替继承.
Function.prototype.before = function(func) {
const _self = this
return function() {
if(func.apply(this, arguments) === false){
return false
}
return _self.apply(this, arguments)
}
}
Function.prototype.after = function(func) {
const _self = this
return function() {
const result = _self.apply(this, arguments)
if(result === false){
return false
}
func.apply(this, arguments)
return result
}
}
compose组合函数实现
以把处理数据的函数向管道一样连起来,上一个函数的结果作为下一个函数的参数,得到最终的结果:
// reduce
const compose = (...funs) => {
return (...arg) => {
return funs.reduce((acc, curFn) => {
return typeof acc === 'function' ? curFn(acc(...arg)) : curFn(acc)
})
}
}
// loadsh
var flow = function (fns) {
var len = fns.length
var index = len
while (index--) {
if (typeof fns[index] !== 'function') {
throw new TypeError('Expected a function')
}
}
return function (...args) {
var index = 0
var reslut = len ? fns[index].apply(this, args) : args[0]
while (++index < len) {
reslut = fns[index].call(this, reslut)
}
return reslut
}
}
来源
实现requestIdleCallback方法var requestIdleCallback =
function (cb) {
if (global.requestIdleCallback) return global.requestIdleCallback(cb)
var start = Date.now();
return setTimeout(function () {
cb({
didTimeout: false,
timeRemaining: function () {
return Math.max(0, 50 - (Date.now() - start));
}
});
}, 1);
}
var cancelIdleCallback =
function (id) {
if (global.cancelIdleCallback) return global.cancelIdleCallback(id)
return clearTimeout(id);
}
exports.requestIdleCallback = requestIdleCallback
exports.cancelIdleCallback = cancelIdleCallback
实现 setTimeout 函数
let setTimeout = (fn, timeout, ...args) => {
// 初始当前时间
const start = +new Date()
let timer, now
const loop = () => {
timer = window.requestAnimationFrame(loop)
// 再次运行时获取当前时间
now = +new Date()
// 当前运行时间 - 初始当前时间 >= 等待时间 ===>> 跳出
if (now - start >= timeout) {
fn.apply(this, args)
window.cancelAnimationFrame(timer)
}
}
window.requestAnimationFrame(loop)
}
function test(){
console.log("test")
}
let timerID = setTimeout(test, 1000);
小红包免费领
小礼物走一走
部分内容来源于网络,如有侵权,请留言或联系994917123@qq.com;访问量:waiting;访客数:waiting