实现 filter 办法
Array.prototype.myFilter=function(callback, context=window){
let len = this.length
newArr = [],
i=0
for(; i < len; i++){if(callback.apply(context, [this[i], i , this])){newArr.push(this[i]);
}
}
return newArr;
}
实现一下 hash 路由
根底的 html
代码:
<html>
<style>
html, body {
margin: 0;
height: 100%;
}
ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
}
.box {
width: 100%;
height: 100%;
background-color: red;
}
</style>
<body>
<ul>
<li>
<a href="#red"> 红色 </a>
</li>
<li>
<a href="#green"> 绿色 </a>
</li>
<li>
<a href="#purple"> 紫色 </a>
</li>
</ul>
</body>
</html>
简略实现:
<script>
const box = document.getElementsByClassName('box')[0];
const hash = location.hash
window.onhashchange = function (e) {const color = hash.slice(1)
box.style.background = color
}
</script>
封装成一个 class:
<script>
const box = document.getElementsByClassName('box')[0];
const hash = location.hash
class HashRouter {constructor (hashStr, cb) {
this.hashStr = hashStr
this.cb = cb
this.watchHash()
this.watch = this.watchHash.bind(this)
window.addEventListener('hashchange', this.watch)
}
watchHash () {let hash = window.location.hash.slice(1)
this.hashStr = hash
this.cb(hash)
}
}
new HashRouter('red', (color) => {box.style.background = color})
</script>
二叉树档次遍历
// 二叉树档次遍历
class Node {constructor(element, parent) {
this.parent = parent // 父节点
this.element = element // 以后存储内容
this.left = null // 左子树
this.right = null // 右子树
}
}
class BST {constructor(compare) {
this.root = null // 树根
this.size = 0 // 树中的节点个数
this.compare = compare || this.compare
}
compare(a,b) {return a - b}
add(element) {if(this.root === null) {this.root = new Node(element, null)
this.size++
return
}
// 获取根节点 用以后增加的进行判断 放右边还是放左边
let currentNode = this.root
let compare
let parent = null
while (currentNode) {compare = this.compare(element, currentNode.element)
parent = currentNode // 先将父亲保存起来
// currentNode 要不停的变动
if(compare > 0) {currentNode = currentNode.right} else if(compare < 0) {currentNode = currentNode.left} else {currentNode.element = element // 相等时 先笼罩后续解决}
}
let newNode = new Node(element, parent)
if(compare > 0) {parent.right = newNode} else if(compare < 0) {parent.left = newNode}
this.size++
}
// 档次遍历 队列
levelOrderTraversal(visitor) {if(this.root == null) {return}
let stack = [this.root]
let index = 0 // 指针 指向 0
let currentNode
while (currentNode = stack[index++]) {
// 反转二叉树
let tmp = currentNode.left
currentNode.left = currentNode.right
currentNode.right = tmp
visitor.visit(currentNode.element)
if(currentNode.left) {stack.push(currentNode.left)
}
if(currentNode.right) {stack.push(currentNode.right)
}
}
}
}
// 测试
var bst = new BST((a,b)=>a.age-b.age) // 模仿 sort 办法
// ![](http://img-repo.poetries.top/images/20210522203619.png)
// ![](http://img-repo.poetries.top/images/20210522211809.png)
bst.add({age: 10})
bst.add({age: 8})
bst.add({age:19})
bst.add({age:6})
bst.add({age: 15})
bst.add({age: 22})
bst.add({age: 20})
// 应用访问者模式
class Visitor {constructor() {this.visit = function (elem) {elem.age = elem.age*2}
}
}
// ![](http://img-repo.poetries.top/images/20210523095515.png)
console.log(bst.levelOrderTraversal(new Visitor()))
二分查找
function search(arr, target, start, end) {
let targetIndex = -1;
let mid = Math.floor((start + end) / 2);
if (arr[mid] === target) {
targetIndex = mid;
return targetIndex;
}
if (start >= end) {return targetIndex;}
if (arr[mid] < target) {return search(arr, target, mid + 1, end);
} else {return search(arr, target, start, mid - 1);
}
}
// const dataArr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// const position = search(dataArr, 6, 0, dataArr.length - 1);
// if (position !== -1) {// console.log(` 指标元素在数组中的地位:${position}`);
// } else {// console.log("指标元素不在数组中");
// }
实现一个 padStart()或 padEnd()的 polyfil
String.prototype.padStart
和 String.prototype.padEnd
是 ES8
中新增的办法,容许将空字符串或其余字符串增加到原始字符串的结尾或结尾。咱们先看下应用语法:
String.padStart(targetLength,[padString])
用法:
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
// 1. 若是输出的指标长度小于字符串本来的长度则返回字符串自身
'xxx'.padStart(2, 's') // 'xxx'
// 2. 第二个参数的默认值为 " ",长度是为 1 的
// 3. 而此参数可能是个不确定长度的字符串,若是要填充的内容达到了指标长度,则将不要的局部截取
'xxx'.padStart(5, 'sss') // ssxxx
// 4. 可用来解决日期、金额格式化问题
'12'.padStart(10, 'YYYY-MM-DD') // "YYYY-MM-12"
'09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12"
polyfill 实现:
String.prototype.myPadStart = function (targetLen, padString = " ") {if (!targetLen) {throw new Error('请输出须要填充到的长度');
}
let originStr = String(this); // 获取到调用的字符串, 因为 this 本来是 String{},所以须要用 String 转为字符串
let originLen = originStr.length; // 调用的字符串本来的长度
if (originLen >= targetLen) return originStr; // 若是 本来 > 指标 则返回本来字符串
let diffNum = targetLen - originLen; // 10 - 6 // 差值
for (let i = 0; i < diffNum; i++) { // 要增加几个成员
for (let j = 0; j < padString.length; j++) { // 输出的 padString 的长度可能不为 1
if (originStr.length === targetLen) break; // 判断每一次增加之后是否到了指标长度
originStr = `${padString[j]}${originStr}`;
}
if (originStr.length === targetLen) break;
}
return originStr;
}
console.log('xxx'.myPadStart(16))
console.log('xxx'.padStart(16))
还是比较简单的,而 padEnd
的实现和它一样,只须要把第二层 for
循环里的 ${padString[j]}${orignStr}
换下地位就能够了。
转化为驼峰命名
var s1 = "get-element-by-id"
// 转化为 getElementById
var f = function(s) {return s.replace(/-\w/g, function(x) {return x.slice(1).toUpperCase();})
}
参考 前端进阶面试题具体解答
实现一个队列
基于链表构造实现队列
const LinkedList = require('./ 实现一个链表构造')
// 用链表默认应用数组来模仿队列,性能更佳
class Queue {constructor() {this.ll = new LinkedList()
}
// 向队列中增加
offer(elem) {this.ll.add(elem)
}
// 查看第一个
peek() {return this.ll.get(0)
}
// 队列只能从头部删除
remove() {return this.ll.remove(0)
}
}
var queue = new Queue()
queue.offer(1)
queue.offer(2)
queue.offer(3)
var removeVal = queue.remove(3)
console.log(queue.ll,'queue.ll')
console.log(removeVal,'queue.remove')
console.log(queue.peek(),'queue.peek')
字符串最长的不反复子串
题目形容
给定一个字符串 s,请你找出其中不含有反复字符的 最长子串 的长度。示例 1:
输出: s = "abcabcbb"
输入: 3
解释: 因为无反复字符的最长子串是 "abc",所以其长度为 3。示例 2:
输出: s = "bbbbb"
输入: 1
解释: 因为无反复字符的最长子串是 "b",所以其长度为 1。示例 3:
输出: s = "pwwkew"
输入: 3
解释: 因为无反复字符的最长子串是 "wke",所以其长度为 3。请留神,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。示例 4:
输出: s = ""
输入: 0
答案
const lengthOfLongestSubstring = function (s) {if (s.length === 0) {return 0;}
let left = 0;
let right = 1;
let max = 0;
while (right <= s.length) {let lr = s.slice(left, right);
const index = lr.indexOf(s[right]);
if (index > -1) {left = index + left + 1;} else {lr = s.slice(left, right + 1);
max = Math.max(max, lr.length);
}
right++;
}
return max;
};
手写深度比拟 isEqual
思路:深度比拟两个对象,就是要深度比拟对象的每一个元素。=> 递归
-
递归退出条件:
- 被比拟的是两个值类型变量,间接用“===”判断
- 被比拟的两个变量之一为
null
,直接判断另一个元素是否也为null
-
提前结束递推:
- 两个变量
keys
数量不同 - 传入的两个参数是同一个变量
- 两个变量
- 递推工作:深度比拟每一个
key
function isEqual(obj1, obj2){
// 其中一个为值类型或 null
if(!isObject(obj1) || !isObject(obj2)){return obj1 === obj2;}
// 判断是否两个参数是同一个变量
if(obj1 === obj2){return true;}
// 判断 keys 数是否相等
const obj1Keys = Object.keys(obj1);
const obj2Keys = Object.keys(obj2);
if(obj1Keys.length !== obj2Keys.length){return false;}
// 深度比拟每一个 key
for(let key in obj1){if(!isEqual(obj1[key], obj2[key])){return false;}
}
return true;
}
查找字符串中呈现最多的字符和个数
例: abbcccddddd -> 字符最多的是 d,呈现了 5 次
let str = "abcabcabcbbccccc";
let num = 0;
let char = '';
// 使其依照肯定的秩序排列
str = str.split('').sort().join('');
// "aaabbbbbcccccccc"
// 定义正则表达式
let re = /(\w)\1+/g;
str.replace(re,($0,$1) => {if(num < $0.length){
num = $0.length;
char = $1;
}
});
console.log(` 字符最多的是 ${char},呈现了 ${num}次 `);
判断是否是电话号码
function isPhone(tel) {var regx = /^1[34578]\d{9}$/;
return regx.test(tel);
}
实现一个 add 办法实现两个大数相加
// 题目
let a = "9007199254740991";
let b = "1234567899999999999";
function add(a ,b){//...}
实现代码如下:
function add(a ,b){
// 取两个数字的最大长度
let maxLength = Math.max(a.length, b.length);
// 用 0 去补齐长度
a = a.padStart(maxLength , 0);//"0009007199254740991"
b = b.padStart(maxLength , 0);//"1234567899999999999"
// 定义加法过程中须要用到的变量
let t = 0;
let f = 0; //"进位"
let sum = "";
for(let i=maxLength-1 ; i>=0 ; i--){t = parseInt(a[i]) + parseInt(b[i]) + f;
f = Math.floor(t/10);
sum = t%10 + sum;
}
if(f!==0){sum = '' + f + sum;}
return sum;
}
实现一个 sleep 函数,比方 sleep(1000) 意味着期待 1000 毫秒
// 应用 promise 来实现 sleep
const sleep = (time) => {return new Promise(resolve => setTimeout(resolve, time))
}
sleep(1000).then(() => {// 这里写你的骚操作})
实现 getValue/setValue 函数来获取 path 对应的值
// 示例
var object = {a: [{ b: { c: 3} }] }; // path: 'a[0].b.c'
var array = [{a: { b: [1] } }]; // path: '[0].a.b[0]'
function getValue(target, valuePath, defaultValue) {}
console.log(getValue(object, "a[0].b.c", 0)); // 输入 3
console.log(getValue(array, "[0].a.b[0]", 12)); // 输入 1
console.log(getValue(array, "[0].a.b[0].c", 12)); // 输入 12
实现
/**
* 测试属性是否匹配
*/
export function testPropTypes(value, type, dev) {const sEnums = ['number', 'string', 'boolean', 'undefined', 'function']; // NaN
const oEnums = ['Null', 'Object', 'Array', 'Date', 'RegExp', 'Error'];
const nEnums = ['[object Number]',
'[object String]',
'[object Boolean]',
'[object Undefined]',
'[object Function]',
'[object Null]',
'[object Object]',
'[object Array]',
'[object Date]',
'[object RegExp]',
'[object Error]',
];
const reg = new RegExp('\\[object (.*?)\\]');
// 齐全匹配模式,type 应该传递相似格局[object Window] [object HTMLDocument] ...
if (reg.test(type)) {
// 排除 nEnums 的 12 种
if (~nEnums.indexOf(type)) {if (dev === true) {console.warn(value, 'The parameter type belongs to one of 12 types:number string boolean undefined Null Object Array Date RegExp function Error NaN');
}
}
if (Object.prototype.toString.call(value) === type) {return true;}
return false;
}
}
const syncVarIterator = {getter: function (obj, key, defaultValue) {
// 后果变量
const defaultResult = defaultValue === undefined ? undefined : defaultValue;
if (testPropTypes(obj, 'Object') === false && testPropTypes(obj, 'Array') === false) {return defaultResult;}
// 后果变量,临时指向 obj 持有的援用,后续将可能被一直的批改
let result = obj;
// 失去晓得值
try {
// 解析属性档次序列
const keyArr = key.split('.');
// 迭代 obj 对象属性
for (let i = 0; i < keyArr.length; i++) {
// 如果第 i 层属性存在对应的值则迭代该属性值
if (result[keyArr[i]] !== undefined) {result = result[keyArr[i]];
// 如果不存在则返回未定义
} else {return defaultResult;}
}
} catch (e) {return defaultResult;}
// 返回获取的后果
return result;
},
setter: function (obj, key, val) {
// 如果不存在 obj 则返回未定义
if (testPropTypes(obj, 'Object') === false) {return false;}
// 后果变量,临时指向 obj 持有的援用,后续将可能被一直的批改
let result = obj;
try {
// 解析属性档次序列
const keyArr = key.split('.');
let i = 0;
// 迭代 obj 对象属性
for (; i < keyArr.length - 1; i++) {
// 如果第 i 层属性对应的值不存在,则定义为对象
if (result[keyArr[i]] === undefined) {result[keyArr[i]] = {};}
// 如果第 i 层属性对应的值不是对象(Object)的一个实例,则抛出谬误
if (!(result[keyArr[i]] instanceof Object)) {throw new Error('obj.' + keyArr.splice(0, i + 1).join('.') + 'is not Object');
}
// 迭代该层属性值
result = result[keyArr[i]];
}
// 设置属性值
result[keyArr[i]] = val;
return true;
} catch (e) {return false;}
},
};
应用 promise 来实现
创立 enhancedObject
函数
const enhancedObject = (target) =>
new Proxy(target, {get(target, property) {if (property in target) {return target[property];
} else {return searchFor(property, target); // 理论应用时要对 value 值进行复位
}
},
});
let value = null;
function searchFor(property, target) {for (const key of Object.keys(target)) {if (typeof target[key] === "object") {searchFor(property, target[key]);
} else if (typeof target[property] !== "undefined") {value = target[property];
break;
}
}
return value;
}
应用 enhancedObject
函数
const data = enhancedObject({
user: {
name: "test",
settings: {theme: "dark",},
},
});
console.log(data.user.settings.theme); // dark
console.log(data.theme); // dark
以上代码运行后,控制台会输入以下代码:
dark
dark
通过观察以上的输入后果可知,应用
enhancedObject
函数解决过的对象,咱们就能够不便地拜访一般对象外部的深层属性。
实现一个拖拽
<style>
html, body {
margin: 0;
height: 100%;
}
#box {
width: 100px;
height: 100px;
background-color: red;
position: absolute;
top: 100px;
left: 100px;
}
</style>
<div id="box"></div>
window.onload = function () {var box = document.getElementById('box');
box.onmousedown = function (ev) {
var oEvent = ev || window.event; // 兼容火狐, 火狐下没有 window.event
var distanceX = oEvent.clientX - box.offsetLeft; // 鼠标到可视区右边的间隔 - box 到页面右边的间隔
var distanceY = oEvent.clientY - box.offsetTop;
document.onmousemove = function (ev) {
var oEvent = ev || window.event;
var left = oEvent.clientX - distanceX;
var top = oEvent.clientY - distanceY;
if (left <= 0) {left = 0;} else if (left >= document.documentElement.clientWidth - box.offsetWidth) {left = document.documentElement.clientWidth - box.offsetWidth;}
if (top <= 0) {top = 0;} else if (top >= document.documentElement.clientHeight - box.offsetHeight) {top = document.documentElement.clientHeight - box.offsetHeight;}
box.style.left = left + 'px';
box.style.top = top + 'px';
}
box.onmouseup = function () {
document.onmousemove = null;
box.onmouseup = null;
}
}
}
版本号排序的办法
题目形容: 有一组版本号如下 ['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);
实现千位分隔符
// 保留三位小数
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 : '');
}
树形构造转成列表(解决菜单)
[
{
id: 1,
text: '节点 1',
parentId: 0,
children: [
{
id:2,
text: '节点 1_1',
parentId:1
}
]
}
]
转成
[
{
id: 1,
text: '节点 1',
parentId: 0 // 这里用 0 示意为顶级节点
},
{
id: 2,
text: '节点 1_1',
parentId: 1 // 通过这个字段来确定子父级
}
...
]
实现代码如下:
function treeToList(data) {let res = [];
const dfs = (tree) => {tree.forEach((item) => {if (item.children) {dfs(item.children);
delete item.children;
}
res.push(item);
});
};
dfs(data);
return res;
}
请实现一个 add 函数,满足以下性能
add(1); // 1
add(1)(2); // 3
add(1)(2)(3);// 6
add(1)(2, 3); // 6
add(1, 2)(3); // 6
add(1, 2, 3); // 6
function add(...args) {
// 在外部申明一个函数,利用闭包的个性保留并收集所有的参数值
let fn = function(...newArgs) {return add.apply(null, args.concat(newArgs))
}
// 利用 toString 隐式转换的个性,当最初执行时隐式转换,并计算最终的值返回
fn.toString = function() {return args.reduce((total,curr)=> total + curr)
}
return fn
}
考点:
- 应用闭包,同时要对 JavaScript 的作用域链(原型链)有深刻的了解
- 重写函数的
toSting()
办法
// 测试,调用 toString 办法触发求值
add(1).toString(); // 1
add(1)(2).toString(); // 3
add(1)(2)(3).toString();// 6
add(1)(2, 3).toString(); // 6
add(1, 2)(3).toString(); // 6
add(1, 2, 3).toString(); // 6
字符串查找
请应用最根本的遍从来实现判断字符串 a 是否被蕴含在字符串 b 中,并返回第一次呈现的地位(找不到返回 -1)。
a='34';b='1234567'; // 返回 2
a='35';b='1234567'; // 返回 -1
a='355';b='12354355'; // 返回 5
isContain(a,b);
function isContain(a, b) {for (let i in b) {if (a[0] === b[i]) {
let tmp = true;
for (let j in a) {if (a[j] !== b[~~i + ~~j]) {tmp = false;}
}
if (tmp) {return i;}
}
}
return -1;
}