注意: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))性能最快 递归// 深度优先
// 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;
}
主要作用
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是红宝书中提到的一种可以很大限度降低循环次数的方式
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)
}
原理:
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
}
}
用于检测构造函数的 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主要的功能就是更改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('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)
}
类似Vue的eventopen in new window方法
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)
}
}
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 = (o) => {
let Fn = function() {}; // 临时的构造函数
Fn.prototype = o;
return new Fn;
}
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 对象最后状态如何,都会执行的操作
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率先改变状态;则改变Promise状态并执行then方法
Promise.prototype._race = promises => new Promise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, reject)
})
})
所有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;
});
}
})
}
});
}
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()
}
}
1s是否达到 60 帧,16.5ms左右渲染一次,即可间接地反映浏览器的渲染帧率,所以通过显示当前网页的 FPS 值,判断页面是否卡顿参考open in new window
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];
});
}
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
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(",") + "}";
}
}
String.prototype.repeatString = function (num) {
return Array(num + 1).join(this);
}
AOP就是让一个函数在另一个函数之前或者之后执行; 在 js 中的妙用.参考open in new window
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
}
}
以把处理数据的函数向管道一样连起来,上一个函数的结果作为下一个函数的参数,得到最终的结果:
// 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
}
}
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
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);
注意: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))性能最快 递归// 深度优先
// 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;
}
主要作用
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是红宝书中提到的一种可以很大限度降低循环次数的方式
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)
}
原理:
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
}
}
用于检测构造函数的 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主要的功能就是更改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('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)
}
类似Vue的eventopen in new window方法
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)
}
}
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 = (o) => {
let Fn = function() {}; // 临时的构造函数
Fn.prototype = o;
return new Fn;
}
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 对象最后状态如何,都会执行的操作
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率先改变状态;则改变Promise状态并执行then方法
Promise.prototype._race = promises => new Promise((resolve, reject) => {
promises.forEach(promise => {
promise.then(resolve, reject)
})
})
所有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;
});
}
})
}
});
}
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()
}
}
1s是否达到 60 帧,16.5ms左右渲染一次,即可间接地反映浏览器的渲染帧率,所以通过显示当前网页的 FPS 值,判断页面是否卡顿参考open in new window
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];
});
}
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
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(",") + "}";
}
}
String.prototype.repeatString = function (num) {
return Array(num + 1).join(this);
}
AOP就是让一个函数在另一个函数之前或者之后执行; 在 js 中的妙用.参考open in new window
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
}
}
以把处理数据的函数向管道一样连起来,上一个函数的结果作为下一个函数的参数,得到最终的结果:
// 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
}
}
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
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);