封装异步的 fetch,应用 async await 形式来应用
(async () => {
class HttpRequestUtil {async get(url) {const res = await fetch(url);
const data = await res.json();
return data;
}
async post(url, data) {
const res = await fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
const result = await res.json();
return result;
}
async put(url, data) {
const res = await fetch(url, {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
data: JSON.stringify(data)
});
const result = await res.json();
return result;
}
async delete(url, data) {
const res = await fetch(url, {
method: 'DELETE',
headers: {'Content-Type': 'application/json'},
data: JSON.stringify(data)
});
const result = await res.json();
return result;
}
}
const httpRequestUtil = new HttpRequestUtil();
const res = await httpRequestUtil.get('http://golderbrother.cn/');
console.log(res);
})();
版本号排序的办法
题目形容: 有一组版本号如下 ['0.1.1', '2.3.3', '0.302.1', '4.2', '4.3.5', '4.3.4.5']
。当初须要对其进行排序,排序的后果为 ['4.3.5','4.3.4.5','2.3.3','0.302.1','0.1.1']
arr.sort((a, b) => {
let i = 0;
const arr1 = a.split(".");
const arr2 = b.split(".");
while (true) {const s1 = arr1[i];
const s2 = arr2[i];
i++;
if (s1 === undefined || s2 === undefined) {return arr2.length - arr1.length;}
if (s1 === s2) continue;
return s2 - s1;
}
});
console.log(arr);
图片懒加载
能够给 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);
函数珂里化
指的是将一个承受多个参数的函数 变为 承受一个参数返回一个函数的固定模式,这样便于再次调用,例如 f(1)(2)
经典面试题:实现add(1)(2)(3)(4)=10;
、add(1)(1,2,3)(2)=9;
function add() {const _args = [...arguments];
function fn() {_args.push(...arguments);
return fn;
}
fn.toString = function() {return _args.reduce((sum, cur) => sum + cur);
}
return fn;
}
JSONP
script 标签不遵循同源协定,能够用来进行 跨域申请,长处就是兼容性好但仅限于 GET 申请
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);
}
})
}
实现数组的乱序输入
次要的实现思路就是:
- 取出数组的第一个元素,随机产生一个索引值,将该第一个元素和这个索引对应的元素进行替换。
- 第二次取出数据数组第二个元素,随机产生一个除了索引为 1 的之外的索引值,并将第二个元素与该索引值对应的元素进行替换
- 依照下面的法则执行,直到遍历实现
var arr = [1,2,3,4,5,6,7,8,9,10];
for (var i = 0; i < arr.length; i++) {const randomIndex = Math.round(Math.random() * (arr.length - 1 - i)) + i;
[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]];
}
console.log(arr)
还有一办法就是倒序遍历:
var arr = [1,2,3,4,5,6,7,8,9,10];
let length = arr.length,
randomIndex,
temp;
while (length) {randomIndex = Math.floor(Math.random() * length--);
temp = arr[length];
arr[length] = arr[randomIndex];
arr[randomIndex] = temp;
}
console.log(arr)
参考 前端进阶面试题具体解答
手写 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 isPhone(tel) {var regx = /^1[34578]\d{9}$/;
return regx.test(tel);
}
实现数组的 flat 办法
function _flat(arr, depth) {if(!Array.isArray(arr) || depth <= 0) {return arr;}
return arr.reduce((prev, cur) => {if (Array.isArray(cur)) {return prev.concat(_flat(cur, depth - 1))
} else {return prev.concat(cur);
}
}, []);
}
小孩报数问题
有 30 个小孩儿,编号从 1 -30,围成一圈依此报数,1、2、3 数到 3 的小孩儿退出这个圈,而后下一个小孩 从新报数 1、2、3,问最初剩下的那个小孩儿的编号是多少?
function childNum(num, count){let allplayer = [];
for(let i = 0; i < num; i++){allplayer[i] = i + 1;
}
let exitCount = 0; // 来到人数
let counter = 0; // 记录报数
let curIndex = 0; // 以后下标
while(exitCount < num - 1){if(allplayer[curIndex] !== 0) counter++;
if(counter == count){allplayer[curIndex] = 0;
counter = 0;
exitCount++;
}
curIndex++;
if(curIndex == num){curIndex = 0};
}
for(i = 0; i < num; i++){if(allplayer[i] !== 0){return allplayer[i]
}
}
}
childNum(30, 3)
实现 instanceOf
// 模仿 instanceof
function instance_of(L, R) {
//L 示意左表达式,R 示意右表达式
var O = R.prototype; // 取 R 的显示原型
L = L.__proto__; // 取 L 的隐式原型
while (true) {if (L === null) return false;
if (O === L)
// 这里重点:当 O 严格等于 L 时,返回 true
return true;
L = L.__proto__;
}
}
替换 a,b 的值,不能用长期变量
奇妙的利用两个数的和、差:
a = a + b
b = a - b
a = a - b
实现 apply 办法
apply 原理与 call 很类似,不多赘述
// 模仿 apply
Function.prototype.myapply = function(context, arr) {var context = Object(context) || window;
context.fn = this;
var result;
if (!arr) {result = context.fn();
} else {var args = [];
for (var i = 0, len = arr.length; i < len; i++) {args.push("arr[" + i + "]");
}
result = eval("context.fn(" + args + ")");
}
delete context.fn;
return result;
};
字符串解析问题
var a = {
b: 123,
c: '456',
e: '789',
}
var str=`a{a.b}aa{a.c}aa {a.d}aaaa`;
// => 'a123aa456aa {a.d}aaaa'
实现函数使得将 str 字符串中的 {}
内的变量替换,如果属性不存在放弃原样(比方{a.d}
)
相似于模版字符串,但有一点出入,实际上原理大差不差
const fn1 = (str, obj) => {
let res = '';
// 标记位,标记后面是否有{
let flag = false;
let start;
for (let i = 0; i < str.length; i++) {if (str[i] === '{') {
flag = true;
start = i + 1;
continue;
}
if (!flag) res += str[i];
else {if (str[i] === '}') {
flag = false;
res += match(str.slice(start, i), obj);
}
}
}
return res;
}
// 对象匹配操作
const match = (str, obj) => {const keys = str.split('.').slice(1);
let index = 0;
let o = obj;
while (index < keys.length) {const key = keys[index];
if (!o[key]) {return `{${str}}`;
} else {o = o[key];
}
index++;
}
return o;
}
实现 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);
手写 Promise
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
function MyPromise(fn) {
// 保留初始化状态
var self = this;
// 初始化状态
this.state = PENDING;
// 用于保留 resolve 或者 rejected 传入的值
this.value = null;
// 用于保留 resolve 的回调函数
this.resolvedCallbacks = [];
// 用于保留 reject 的回调函数
this.rejectedCallbacks = [];
// 状态转变为 resolved 办法
function resolve(value) {
// 判断传入元素是否为 Promise 值,如果是,则状态扭转必须期待前一个状态扭转后再进行扭转
if (value instanceof MyPromise) {return value.then(resolve, reject);
}
// 保障代码的执行程序为本轮事件循环的开端
setTimeout(() => {
// 只有状态为 pending 时能力转变,if (self.state === PENDING) {
// 批改状态
self.state = RESOLVED;
// 设置传入的值
self.value = value;
// 执行回调函数
self.resolvedCallbacks.forEach(callback => {callback(value);
});
}
}, 0);
}
// 状态转变为 rejected 办法
function reject(value) {
// 保障代码的执行程序为本轮事件循环的开端
setTimeout(() => {
// 只有状态为 pending 时能力转变
if (self.state === PENDING) {
// 批改状态
self.state = REJECTED;
// 设置传入的值
self.value = value;
// 执行回调函数
self.rejectedCallbacks.forEach(callback => {callback(value);
});
}
}, 0);
}
// 将两个办法传入函数执行
try {fn(resolve, reject);
} catch (e) {
// 遇到谬误时,捕捉谬误,执行 reject 函数
reject(e);
}
}
MyPromise.prototype.then = function(onResolved, onRejected) {
// 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
onResolved =
typeof onResolved === "function"
? onResolved
: function(value) {return value;};
onRejected =
typeof onRejected === "function"
? onRejected
: function(error) {throw error;};
// 如果是期待状态,则将函数退出对应列表中
if (this.state === PENDING) {this.resolvedCallbacks.push(onResolved);
this.rejectedCallbacks.push(onRejected);
}
// 如果状态曾经凝固,则间接执行对应状态的函数
if (this.state === RESOLVED) {onResolved(this.value);
}
if (this.state === REJECTED) {onRejected(this.value);
}
};
实现类的继承
类的继承在几年前是重点内容,有 n 种继承形式各有优劣,es6 遍及后越来越不重要,那么多种写法有点『回字有四样写法』的意思,如果还想深刻了解的去看红宝书即可,咱们目前只实现一种最现实的继承形式。
function Parent(name) {this.parent = name}
Parent.prototype.say = function() {console.log(`${this.parent}: 你打篮球的样子像 kunkun`)
}
function Child(name, parent) {
// 将父类的构造函数绑定在子类上
Parent.call(this, parent)
this.child = name
}
/** 1. 这一步不必 Child.prototype =Parent.prototype 的起因是怕共享内存,批改父类原型对象就会影响子类 2. 不必 Child.prototype = new Parent()的起因是会调用 2 次父类的构造方法(另一次是 call),会存在一份多余的父类实例属性 3. Object.create 是创立了父类原型的正本,与父类原型齐全隔离 */
Child.prototype = Object.create(Parent.prototype);
Child.prototype.say = function() {console.log(`${this.parent}好,我是练习时长两年半的 ${this.child}`);
}
// 留神记得把子类的结构指向子类自身
Child.prototype.constructor = Child;
var parent = new Parent('father');
parent.say() // father: 你打篮球的样子像 kunkun
var child = new Child('cxk', 'father');
child.say() // father 好,我是练习时长两年半的 cxk
throttle(节流)
高频工夫触发, 但 n 秒内只会执行一次, 所以节流会浓缩函数的执行频率。
const throttle = (fn, time) => {
let flag = true;
return function() {if (!flag) return;
flag = false;
setTimeout(() => {fn.apply(this, arguments);
flag = true;
}, time);
}
}
节流常利用于鼠标一直点击触发、监听滚动事件。
实现千位分隔符
// 保留三位小数
parseToMoney(1234.56); // return '1,234.56'
parseToMoney(123456789); // return '123,456,789'
parseToMoney(1087654.321); // return '1,087,654.321'
function parseToMoney(num) {num = parseFloat(num.toFixed(3));
let [integer, decimal] = String.prototype.split.call(num, '.');
integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,');
return integer + '.' + (decimal ? decimal : '');
}
正则表达式(使用了正则的前向申明和反前向申明):
function parseToMoney(str){
// 仅仅对地位进行匹配
let re = /(?=(?!\b)(\d{3})+$)/g;
return str.replace(re,',');
}
验证是否是身份证
function isCardNo(number) {var regx = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
return regx.test(number);
}