写在前头,笔者大概工作三年吧,从毕业后一直在上家公司待了三年,直到今年8.19我正式交接离职了(裸辞),因为我相信我值得更好的
休息了三个星期后,我开始了我的求职之路,在面试了四家公司后我荣幸的收到了三家公司的offer,期间也碰到了一些手写代码的问题,现在我就将面试中问到的,以及我面试准备到的一起贴出来,希望每位同学都能够day day up!
call
// call
Function.prototype.call = function(context,...arg) {
// console.log(arg)
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context == undefined ? window : context;
let type = typeof context;
if(!/^(object|fucntion)$/.test(type)){
if(/^(symbol|bigint)$/.test(type)){
context = Object(context)
}else{
context = new context.constructor(context)
}
}
let key = Symbol('key'),
result;
context[key] = this;
result = context[key](...arg)
delete context[key]
return result
}
function fn(){
console.log(this,arguments)
}
console.log("----------call----------")
fn.call(1,1,2,3)
fn.call(1)
console.log("----------call----------")
apply
// apply
Function.prototype.apply = function(context,arg) {
// console.log(arg)
if (typeof this !== 'function') {
throw new TypeError('Error')
}
context = context == undefined ? window : context;
let type = typeof context;
if(!/^(object|fucntion)$/.test(type)){
if(/^(symbol|bigint)$/.test(type)){
context = Object(context)
}else{
context = new context.constructor(context)
}
}
let key = Symbol('key'),
result;
context[key] = this;
// 处理参数和 call 有区别
if (arg) {
result = context[key](...arg)
} else {
result = context[key]()
}
delete context[key]
return result
}
function fn(){
console.log(this,arguments)
}
console.log("----------apply----------")
fn.apply(1,[1,2,3])
fn.apply(1,[])
fn.apply(1)
console.log("----------apply----------")
bind
// bind
Function.prototype.bind = function(context,...arg) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
let _this = this;
context = context == undefined ? window : context;
let type = typeof context;
if (!/^(object|function)$/.test(type)) {
if (/^(symbol|bigint)$/.test(type)) {
context = Object(context);
} else {
context = new context.constructor(context);
}
}
return function anonymous(...arg1){
_this.call(context,...arg,...arg1)
}
};
function fn(){
console.log(this, arguments)
}
let fn1 = fn.bind(1,2,3,4);
console.log("----------bind----------")
fn1(5,6,7)
console.log("----------bind----------")
new
// new
// var person = new Person("a","b")
function myNew(fn,...arg) {
// let obj = {};
// obj.__proto__ = fn.prototype
let obj = Object.create(fn.prototype)
let result = fn.apply(obj,arg)
return result instanceof Object ? result : obj;
}
Object.create
Object.create = function(obj) {
function F(){}
F.prototype = obj
return new F()
}
instanceOf
function instance_of(L,R){
L = L.__proto__; // 隐式原型
R = R.prototype; // 显式原型
while(true){
if(L === null){
return false
}else if(L === R){
return true
}
L = L.__proto__
}
}
console.log(instance_of([],Array))
console.log(instance_of([],Object))
页面用了多少标签
var tags = [...document.getElementsByTagName("*")].map(el=>el.tagName);
console.log(new Set(tags))
获取绝对位置
function getPos(el){
var pos = {
x:0,
y:0
}
while(el){
pos.x += el.offsetLeft;
pos.y += el.offsetTop;
el = el.offsetParent
}
return pos
}
url参数获取
// http://localhost:1995/order?a=1&b=2&c=3
const search = window.location.search.substring(1);
const func1 = (search)=>{
let obj = {}
search.split("&").forEach(item=>{
const arg = item.split("=");
obj[arg[0]] = arg[1]
})
return obj
}
const func2 = (search)=>{
let obj = new URLSearchParams(`?${search}`)
return obj
// obj.get('a')
}
//URLSearchParams 兼容性不好需要polyfill
class URLSearchParams {
constructor(searchName) {
this._initName(searchName)
this._init()
}
_initName(searchName) {
if (typeof searchName === "string") {
if (searchName, indexOf('?') === 0) {
this._searchName = searchName.substring(1);
} else {
this._searchName = searchName;
}
return
}
if (Array.isArray(searchName)) {
this._searchName = searchName.map(item => {
return `${item[0]}=${item[1]}`
}).join('&')
return
}
if (typeof searchName === "object") {
this._searchName = Object.keys(searchName).map(item => {
return `${item}=${searchName[item]}`
}).join('&')
}
}
_init() {
this._result = {}
this._searchName.split("&").forEach(item => {
const arg = item.split("=");
this._result[arg[0]] = arg[1]
})
}
get(key) {
return this._result[key]
}
append(key, value) {
this._result[key] = value
}
delete(key) {
delete this._result[key]
}
has(key) {
if (key in this._result) {
return true
}
return false
}
keys() {
return Object.keys(this._result)
}
values() {
return Object.keys(this._result).map(item => {
return this._result[item]
})
}
toString() {
return Object.keys(this._result).map(item => {
return `${item}=${this._result[item]}`
}).join('&')
}
}
懒加载
// 实现懒加载
// <ul>
// <li><img src="./imgs/default.png" data="./imgs/1.png" alt=""></li>
// <li><img src="./imgs/default.png" data="./imgs/2.png" alt=""></li>
// <li><img src="./imgs/default.png" data="./imgs/3.png" alt=""></li>
// <li><img src="./imgs/default.png" data="./imgs/4.png" alt=""></li>
// <li><img src="./imgs/default.png" data="./imgs/5.png" alt=""></li>
// <li><img src="./imgs/default.png" data="./imgs/6.png" alt=""></li>
// <li><img src="./imgs/default.png" data="./imgs/7.png" alt=""></li>
// <li><img src="./imgs/default.png" data="./imgs/8.png" alt=""></li>
// <li><img src="./imgs/default.png" data="./imgs/9.png" alt=""></li>
// <li><img src="./imgs/default.png" data="./imgs/10.png" alt=""></li>
// </ul>
let imgs = document.querySelectorAll('img')
// 可视区高度
let clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
/*function lazyLoad () {
// 滚动卷去的高度
let scrollTop = window.scrollY || window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
for (let i = 0; i < imgs.length; i ++) {
// 图片在可视区冒出的高度
let x = clientHeight + scrollTop - imgs[i].offsetTop
// 图片在可视区内
if (x > 0 && x < clientHeight+imgs[i].height) {
imgs[i].src = imgs[i].getAttribute('data')
}
}
}*/
function lazyLoad () {
imgs.forEach(item=>{
if(item.getBoundingClientRect().top < clientHeight){
imgs[i].src = imgs[i].getAttribute('data')
}
})
}
window.addEventListener('scroll', lazyLoad);
深拷贝
/*JSON.parse(JSON.stringify())
这种方式存在一定的问题:
正则 => 空对象
BigInt => 报错
日期 => 转换为字符串就转换不回来了
Symbol/undefined/function => 没了*/
// 递归拷贝
function deepCopy(source){
if(typeof source !== "object" || source === null){
return source
}
let target;
if(source instanceof Array){
target = []
}else{
target = {}
}
for(let attr in source){
target[attr] = deepCopy(source[attr])
}
return target
}
function cloneDeep(source) {
// 验证类型
if (source === null) return null;
if (typeof source !== "object") return source;
if (source instanceof RegExp) return new RegExp(source);
if (source instanceof Date) return new Date(source);
// 对于对象和数组我们再进行循环克隆
let target = new source.constructor();
Object.keys(source).forEach(key => {
target[key] = cloneDeep(source[key]);
});
return target;
}
let source = {
a: 1,
b: '1',
c: true,
d: null,
e: undefined,
f: [1,2,3],
h: {a:1},
i: function (){},
j: /\d+/,
k: new Date(),
l: Symbol('key'),
m: BigInt(10)
}
console.log("----------clone----------")
let result1 = deepCopy(source)
console.log(result1)
let result2 = cloneDeep(source)
console.log(result2)
delete source['m']
let result3 = JSON.parse(JSON.stringify(source))
console.log(result3)
console.log("----------clone----------")
函数节流
function throttle(func, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (!timeout) {
console.log(this,11)
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
函数防抖
function debounce(func, wait) {
console.log(this,11)
let timeout;
return function () {
console.log(this,22)
let context = this;
let args = arguments;
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args)
}, wait);
}
}
sleep延迟函数
function sleep(time){
return new Promise((resolve, reject)=>{
setTimeout(resolve, time, 'success')
})
}
实现一个自定义事件
class MyEvent {
constructor() {
this.handle = {};
}
addEvent(eventName, fn) {
if (!(eventName in this.handle)) {
this.handle[eventName] = [];
}
this.handle[eventName].push(fn);
}
trigger(eventName) {
if (!this.handle[eventName]) {
return;
}
this.handle[eventName].forEach(cb => {
cb();
})
}
removeEvent(eventName, fn) {
if(!(eventName in this.handle)) {
return;
}
var stack = this.handle[eventName]
for (let i = 0, l = stack.length; i < l; i++) {
if (stack[i] === fn) {
stack.splice(i, 1);
// return this.removeEvent(eventName, fn);
break;
}
}
}
}
几种排序算法的实现
// 冒泡排序
// N-1 N-2 N-3 N-4 ... 1 N*(N-1)/2 N^2/2-N/2 O(N^2)
Array.prototype.bubble = function bubble(){
let _this = this;
let len = _this.length;
// 控制循环多少轮
for(let i = 0; i < len-1; i++){
// 控制每一轮循环多少次
for(let j = 0; j < len-1-i; j++){
if(_this[j] > _this[j+1]){
let temp = _this[j];
_this[j] = _this[j+1];
_this[j+1] = temp;
}
}
}
return _this;
}
// 冒泡优化
Array.prototype.bubble1 = function bubble1(){
let _this = this;
let len = _this.length;
let flag = false;
// 控制循环多少轮
for(let i = 0; i < len-1; i++){
// 控制每一轮循环多少次
for(let j = 0; j < len-1-i; j++){
if(_this[j] > _this[j+1]){
let temp = _this[j];
_this[j] = _this[j+1];
_this[j+1] = temp;
flag = true;
}
}
if(!flag) break;
flag = false; // 每一轮false
}
return _this;
}
// 选择
Array.prototype.select = function select() {
for (let j = 0; j < this.length - 1; j++) {
let min = j,
temp = null;
// 找到比当前项还小的这一项索引
for (let i = min + 1; i < this.length; i++) {
if (this[i] < this[min]) {
min = i;
}
}
// 让最小的项和当前首位交换位置
swap(this,min,j);
}
return this;
};
let ary = [12, 8, 24, 16, 1];
ary.select();
console.log(ary);
// 插入 O(N^2)
Array.prototype.insert = function insert(){
// 取一张牌放手里
let _this = this;
let len = _this.length;
let handle = [_this[0]];
// 开始抓牌
for(let i = 1; i < len; i++){
// A每一次新抓的牌
let A = _this[i];
for(let j = handle.length; j >= 0; j--){
// B手里的这张牌
let B = handle[j];
if( A > B ){
// 新抓的牌A比B要大,则放在B的后面
handle.splice(j + 1, 0, A)
break; // 没必要和手里的牌继续比较了
}
// 都比到最开始,A都没有比任何的牌大,则A是最小的,插入到开始
if( j === 0 ){
handle.unshift(A)
}
}
}
return handle
}
// 快速 O(n*log2(n)) 类似二分法
Array.prototype.quick = function quick(){
let _this = this;
// 如果处理的数组只有一项或者空的,则无需处理了
if( _this.length <= 1) {
return _this
}
// 获取中间项,并且把中间项在数组中删除
let middleIndex = Math.floor(_this.length / 2);
let middleValue = _this.splice(middleIndex, 1)[0];
let len = _this.length;
let leftArr = [];
let rightArr = [];
for(let i = 0; i < len; i++ ){
_this[i] < middleValue ? leftArr.push(_this[i]) : rightArr.push(_this[i])
}
return quick.call(leftArr).concat(middleValue, quick.call(rightArr))
}
var arr = [7,3,9,5,1,4]
console.log(arr.quick())
实现一个栈结构
// 栈
class Stack {
container = [];
// 进栈
enter(element) {
this.container.unshift(element);
}
// 出栈
leave() {
return this.container.shift();
}
// 栈的长度
size() {
return this.container.length;
}
// 获取栈中的结果
value() {
return this.container;
}
}
Number.prototype.decimal2binary = function decimal2binary() {
let sk = new Stack,
decimalNum = this.valueOf();
if (decimalNum === 0) return '0';
while (decimalNum > 0) {
sk.enter(decimalNum % 2);
decimalNum = Math.floor(decimalNum / 2);
}
return sk.value().join('');
};
console.log((10).decimal2binary());
console.log((10).toString(2));
实现一个队列结构
// 队列
class Queue {
container = [];
// 进入
enter(element) {
this.container.push(element);
}
// 离开
leave() {
return this.container.shift();
}
// 队列的长度
size() {
return this.container.length;
}
// 获取队列中的结果
value() {
return this.container;
}
}
function game(n, m) {
let qe = new Queue;
for (let i = 0; i < n; i++) {
qe.enter(i + 1);
}
while (qe.size() > 1) {
for (let i = 0; i < m - 1; i++) {
qe.enter(qe.leave());
}
qe.leave();
}
return qe.value().toString();
}
console.log(game(8, 5));
扁平化数组
let arr = [
[1, 2, 2],
[3, 4, 5, 5],
[6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10
];
/*方案1:使用 Array.prototype.flat 处理*/
arr = arr.flat(Infinity);
/*方案2:把数组直接变为字符串即可*/
arr = arr.toString().split(',').map(item => {
return Number(item);
});
/*方案3:JSON.stringify*/
arr = JSON.stringify(arr).replace(/(\[|\])/g, '').split(',').map(item => Number(item));
/*方案4:基于数组的some方法进行判断检测*/
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
/*方案5:基于递归深度遍历*/
Array.prototype.myFlat = function myFlat() {
let result = [];
//=>循环数组中的每一项,把不是数组的存储到新数组中
let fn = (arr) => {
for (let i = 0; i < arr.length; i++) {
let item = arr[i];
if (Array.isArray(item)) {
fn(item);
continue;
}
result.push(item);
}
};
fn(this);
return result;
}
斐波那契数列
// [1,1,2,3,5,8,13,21,34,...]
// 经典题:爬楼梯,可以一步爬1层或者2层,请问要爬到45层有多少中方案
// 这一题的思路是:45层可以从43层和44层一步上来,那他就是43层和44层方案的总和,而44层可以从42层和43层一步上来,那他就是42层和43层方案的总和】
function fib(n){
if(n==1 || n==2){
return 1
}
return fib(n-1) + fib(n-2)
}
// 优化
function fib1(n){
let arr = []
return fibCache(arr, n)
// 带临时存储的
}
function fibCache(arr, n){
if(n==1 || n==2){
return 1
}
if(arr[n]) return arr[n]
arr[n] = fibCache(arr, n-1) + fibCache(arr, n-2)
return arr[n]
}
function fib3(n){
// 递归是从上到下
// 动态规划,从下到上
let dp = [1,1]
for(let i=3;i<=n;i++){
dp[i] = dp[i-1] + dp[i-2]
}
return dp[n]
}
今天就到这吧,后续想到的我会再更新上来!