常见面试题

137 阅读6分钟

html+css

元素水平垂直居中

<div class="item-1">
    <div class="item-2"></div>
</div>

// 1
.item-1 {
    width: 100px;
    height:100px;
    background-color: pink;
    position: relative;
}
.item-2 {
    width: 50px;
    height: 50px;
    background-color: aqua;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    margin: auto;
}

// 2
.item-1 {
    width: 100px;
    height:100px;
    background-color: pink;
    position: relative;
}
.item-2 {
    width: 50px;
    height: 50px;
    background-color: aqua;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
// 3 
.item-1 {
    width: 100px;
    height:100px;
    background-color: pink;
    display: flex;
    align-items: center;
    justify-content: center;
}

js

promise

const PENDING = 'PENDING';
const FUFILLED = 'FUFILLED';
const REJECTED = 'REJECTED';

function resolvePromise(promise, x, resolve, reject) {
	if (x === promise) {
		throw new Error('错误');
	}
	if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
		// x 有可能是promise
		let called = false;
		try {
			let then = x.then;
			if (typeof then === 'function') {
				// 认定是promise
				then.call(
					x,
					(y) => {
						if (called) return;
						called = true;
						// 防止y 还是一个promise
						resolvePromise(promise, y, resolve, reject);
					},
					(r) => {
						if (called) return;
						called = true;
						reject(r);
					}
				);
			} else {
				resolve(x);
			}
		} catch (e) {
			if (called) return;
			called = true;
			reject(e);
		}
	} else {
		resolve(x);
	}
}

class Promise {
	constructor(executor) {
		this.status = PENDING;
		this.value = null;
		this.reason = null;

		this.onResolvedCallback = [];
		this.onRejectedCallback = [];

		const resolve = (value) => {
			if (value instanceof Promise) {
				return value.then(resolve, reject);
			}
			if (this.status === PENDING) {
				this.status = FUFILLED;
				this.value = value;
				this.onResolvedCallback.forEach((fn) => {
					fn();
				});
			}
		};

		const reject = (reason) => {
			if (this.status === PENDING) {
				this.status = REJECTED;
				this.reason = reason;
				this.onRejectedCallback.forEach((fn) => fn());
			}
		};
		try {
			executor(resolve, reject);
		} catch (e) {
			reject(e);
		}
	}

	then(onFulfilled, onRejected) {
		onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (v) => v;
		onRejected =
			typeof onRejected === 'function'
				? onRejected
				: (err) => {
						throw err;
					};
		// promise 的链式调用要求返回一个promise
		let promise = new Promise((resolve, reject) => {
			if (this.status === PENDING) {
				this.onResolvedCallback.push(() => {
					setTimeout(() => {
						try {
							let x = onFulfilled(this.value);
							resolvePromise(promise, x, resolve, reject);
						} catch (e) {
							reject(e);
						}
					}, 0);
				});
				this.onRejectedCallback.push(() => {
					setTimeout(() => {
						try {
							let x = onRejected(this.reason);
							resolvePromise(promise, x, resolve, reject);
						} catch (e) {
							reject(e);
						}
					}, 0);
				});
			}

			if (this.status === FUFILLED) {
				setTimeout(() => {
					try {
						let x = onFulfilled(this.value);
						resolvePromise(promise, x, resolve, reject);
					} catch (e) {
						reject(e);
					}
				}, 0);
			}
			if (this.status === REJECTED) {
				setTimeout(() => {
					try {
						let x = onRejected(this.reason);
						resolvePromise(promise, x, resolve, reject);
					} catch (e) {
						reject(e);
					}
				}, 0);
			}
		});
		return promise;
	}

	catch(onRejected) {
		return this.then(null, onRejected);
	}
	static resolve(value) {
		return new Promise((resolve, reject) => {
			resolve(value);
		});
	}
	static reject(value) {
		return new Promise((resolve, reject) => {
			reject(value);
		});
	}
	// all 有一个失败就停止,全部成功才是成功, 有一个失败了,就报错了
	static all(promises) {
		return new Promise((resolve, reject) => {
			let result = [];
			let times = 0;
			const processSuccess = (index, val) => {
				result[index] = val;
				if (++times === promises.length) {
					resolve(result);
				}
			};
			for (let i = 0; i < promises.length; i++) {
				let p = promises[i];
				if (p && typeof p.then === 'function') {
					p.then((data) => {
						processSuccess(i, data);
					}, reject);
				} else {
					processSuccess(i, p);
				}
			}
		});
	}
	// 有一个成功或失败了,就停止,返回不是数组
	static race(promises) {
		return new Promise((resolve, reject) => {
			for (let i = 0; i < promises.length; i++) {
				let p = promises[i];
				if (p && typeof p.then === 'function') {
					p.then(resolve, reject);
				} else {
					resolve(p);
				}
			}
		});
	}
	// 不管失败还是成功都能得到所有的结果
	static allSettled(promises) {
		return new Promise((resolve, reject) => {
			let result = [];
			let times = 0;
			const process = (index, val) => {
				result[index] = val;
				if (++times === promises.length) {
					resolve(result);
				}
			};
			for (let i = 0; i < promises.length; i++) {
				let p = promises[i];
				if (p && typeof p.then === 'function') {
					p.then(
						(data) => {
							process(i, {
								status: p.status,
								value: data
							});
						},
						(e) => {
							process(i, {
								status: p.status,
								reason: e
							});
						}
					);
				} else {
					process(i, p);
				}
			}
		});
	}
	// 有一个成功了就走成功,都失败了,才会走失败
	static any(promises) {
		return new Promise((resolve, reject) => {
			let result = [];
			let times = 0;
			const process = (index, val) => {
				result[index] = val;
				if (++times === promises.length) {
					resolve(result[result.length - 1]);
				}
			};
			for (let i = 0; i < promises.length; i++) {
				let p = promises[i];
				if (p && typeof p.then === 'function') {
					p.then(
						(data) => {
							resolve(data);
						},
						(e) => {
							process(i, e);
						}
					);
				} else {
					process(i, p);
				}
			}
		});
	}
}

export default Promise;

promise 的缺点

1、状态不可追踪 2、函数一旦触发,不能取消 3、函数的状态必须靠resolve,reject才能获取

函数状态悬而为未绝的时候,需要设定超时来控制函数返出

柯里化

function curry(fn, ...args) {
	if (args.length < fn.length) {
		return (...innerArgs) => curry(fn, ...args, ...innerArgs);
	} else {
		return fn(...args);
	}
}

function _add(a, b, c, d, e) {
	return a + b + c + d + e;
}

const add = curry(_add);

console.log(add(1));
console.log(add(1, 2, 3, 4, 5));
// console.log(add(1, 2)(3)(4)(5));

数组手写方法

let arr = [ 1, 2, 3, 4, 5, 6, 7 ];
let arr1 = [ 1, 1, 1, 1, 1 ];
Array.prototype.wyMap = function(callback, thisArgs) {
	let res = [];
	let arr = this;
	const ctx = thisArgs ? thisArgs : arr;

	for (let i = 0; i < arr.length; i++) {
		res.push(callback.call(ctx, arr[i], i));
	}
	return res;
};
let wymap = arr.wyMap(
	function(item, index) {
		return item + 1;
	},
	[ 1, 2, 3 ]
);

let eachRes = [];

let wyEach = arr.forEach((item) => {
	eachRes.push(item * 2);
});
console.log(eachRes);

Array.prototype.wySome = function(callback) {};

Array.prototype.wyfilter = function(callback, thisArg) {
	let res = [];
	let arr = this;
	for (let i = 0; i < arr.length; i++) {
		let current = callback(thisArg, arr[i]);
		if (current) {
			res.push(arr[i]);
		}
	}
	return res;
};
let wyfilter = arr.wyfilter((item) => {
	return item % 2 === 0;
});
Array.prototype.wyReduce = function(callback, sum) {
	let list = this;
	let res = undefined;
	if (sum === undefined) {
		res = list[0];
	} else {
		res = sum;
	}
	for (let i = sum === undefined ? 1 : 0; i < list.length; i++) {
		res = callback(res, list[i], i, list);
	}
	return res;
};
arr.wyReduce((pre, current, index, list) => {}, 0);

let obj1 = {
	name: 'wy',
	foo: {
		age: 10,
		arr: [ 1, 2, 3 ]
	}
};
function clone(obj, map = new Map()) {
	let res = {};
	for (let key in obj) {
		if (typeof obj[key] !== 'object') {
			res[key] = obj[key];
		} else {
			if (map.get(key)) {
				res[key] = map.get(key);
			}
			map.set(key, obj[key]);
			res[key] = clone(obj[key], map);
		}
	}
	return res;
}
let obj2 = clone(obj1);
obj2.name = 'ee';
obj2.foo.age = 30;
obj2.foo.arr[0] = 6;
function _add(a, b, c, d) {
	return a + b + c + d;
}
function curry(fn, ...args) {
	if (args.length < fn.length) {
		return (...curryArgs) => curry(fn, ...args, ...curryArgs);
	} else {
		return fn(...args);
	}
}
let add = curry(_add);
let sum = add(1, 2, 3, 4);
let sum1 = add(1)(2)(3)(4);
// console.log(sum1);

/**
 * instanceOf
 */
function wyIntanceOf(left, right) {
	let proto = left.__proto__;
	while (true) {
		if (proto === right.prototype) return true;
		proto = proto.__proto__;
	}
	return false;
}
function Person() {
	this.name = 'wy';
}
let person = new Person();

// console.log(wyIntanceOf(person, Person));

call-apply-bind

let obj = {
	name: '章三'
};

function getName(age, address) {
	console.log('this.name', this.name, age, address);
}

Function.prototype.wyCall = function(content, ...args) {
	content.$fn = this;
	content.$fn(...args);

	delete content.$fn;
};

Function.prototype.wyApply = function(content, args) {
	content.$fn = this;
	content.$fn(...args);
	delete content.$fn;
};

Function.prototype.wyBind = function(content, ...args) {
	content = Object(content);
	const _this = this;
	return function(...fnArgs) {
		content.$fn = _this;
		content.$fn(...[ ...args, ...fnArgs ]);
		delete content.$fn;
	};
};

getName.wyCall(obj, 10, '北京');
getName.wyApply(obj, [ 10, '北京' ]);
const bindName = getName.wyBind(obj, 10);
bindName('上海');

clone

/**
 * 对象拷贝
 */
let obj = {
    name: '张三',
    info: {
        like: 'eee',
        age: 20
    }
};

obj.obj = obj

function cloneHandle(source, target, map) {
    if (map.get(source)) {
        return map.get(source)
    }
    map.set(source, target)
    for(let key in source) {
        target[key] = clone(source[key], map)
    }
    return target
}

function clone(source, map = new Map()) {
    let target = {}
    let type = Object.prototype.toString.call(source).toLowerCase();
    if (type === '[object object]') {
        target = {}
       return cloneHandle(source, target, map)
    } else if (type === '[object array]') {
        target = []
        return cloneHandle(source, target, map)
    } else {
        return source
    }
}


// function clone(source) {
//     if (typeof source === 'object') {
//         let target = Array.isArray(source) ? [] : {}
//         for(let key in source) {
//             target[key] = clone(source[key])
//         }
//         return target
//     } else {
//         return source 
//     }
// }


let obj1 = clone(obj)
obj.info.like = '222334'
console.log('-----------------------------', obj1);

创造对象

// 创建对象的几种方式
// 1、Object.create
// 缺点: 代码麻烦
let obj1 = Object.create({});
obj1.name = '章三';

// 字面量 缺点代码重复
let obj2_1 = {
	name: '章三'
};

let obj2_2 = {
	name: '里斯'
};
// console.log(obj2_1, obj2_2);

// 工厂模式 代码可以复用
// 缺点: 没法确定类型,因为最终得到的都是对象
function createPerson(name, age) {
	let obj = {
		name: name,
		age: age
	};
	return obj;
}
let obj31 = createPerson('章三', 12);
let obj32 = createPerson('李四', 13);

function createCat(name, age) {
	let obj = {
		name: name,
		age: age
	};
	return obj;
}
let obj32 = createCat('李四', 13);

// 自定义函数
// 缺点: getName的代码一样,但是必须复写,占用内存
function createPerson1(name) {
	this.name = name;
	this.getName = function() {
		return this.name;
	};
}

function createPerson2(name) {
	this.name = name;
	this.getName = function() {
		return this.name;
	};
}

// 构造函数

function createPerson3(name) {
	this.name = name;
}
createPerson3.prototype.getName = function() {
	return this.name;
};

防抖截流

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>

    <button id="debounce">防抖</button>

    <button id="throttle">截流</button>
    <script>

        // 防抖和截流
        // 防抖是每一次都清空之前的,重新开始
        // 截流是在同一个时间,不管操作几次,都只执行第一次的操作

        document.getElementById("debounce").addEventListener('click', debounce(() => {
            console.log('-----------------------------', 11233);
        }, 2000)) 
        
        function debounce(fn, delay) {
            let timer = null;
            return function() {
                timer && clearTimeout(timer)
                timer = setTimeout(() => {
                    fn.call(this)
                }, delay);
            }
        }
        
        document.getElementById("throttle").addEventListener('click', throttle(() => {
            console.log('--------------截流---------------');
        }, 1000))   
        
        
        function throttle(fn, time) {
            let start = true
            return function () {
                if (!start) return false;
                start = false
                let timer = setTimeout(() => {
                    console.log('======this', this)
                    fn.call(this)
                    start = true
                }, time)
            }
        }

    </script>
</body>
</html>

继承

// 继承的方式
/**
 * 原型继承
 * 公用一个原型,重复声明实例会污染对方
 */
function Person(name, age) {
	this.name = name;
	this.age = age;
}
Person.prototype.getName = function() {
	console.log('====1---1', this.name);
	return this.name;
};
Person.prototype.setName = function(name) {
	this.name = name;
	console.log(this.name);
	return this.name;
};
function Child(name, age) {
	this.name = name;
	this.age = age;
}
Child.prototype = new Person('爸爸', 40);
Child.prototype.constructor = Child;
Child.prototype.getAge = function() {
	console.log(this.age);
	return this.age;
};

let person = new Person('baba', 40);

let child = new Child('孩子', 10);

let child1_1 = new Child('孩子111', 10);
child.setName('hahahah');
child1_1.getName();

/**
 * 构造函数继承
 * 只能继承属性
 */

function Person1(name, age) {
	this.name = name;
	this.age = age;
}
Person1.prototype.getName = function() {
	console.log(this.name);
	return this.name;
};
function Child1(name, age, like) {
	Person1.call(this, name, age);
}

Child1.prototype.getAge = function() {
	console.log('===1', this.age);
	return this.age;
};

let child1 = new Child1('孩子', 10);
// child1.getName();
child1.getAge();

/**
 * 组合继承
 */

function Person3(name, age) {
	this.name = name;
	this.age = age;
}
Person3.prototype.getName = function() {
	console.log('====3-name', this.name);
	return this.name;
};
function Child3(name, age, like) {
	Person3.call(this, name, age);
}
Child3.prototype = new Person3();
Child3.prototype.constructor = Child3;

Child3.prototype.getAge = function() {
	console.log('===3', this.age);
	return this.age;
};

let child3 = new Child3('孩子', 10);
child3.getName();

/**
 * 组合寄生继承
 */

function Person4(name, age) {
	this.name = name;
	this.age = age;
}
Person4.prototype.getName = function() {
	console.log('====4-name', this.name);
	return this.name;
};
function Child4(name, age, like) {
	Person4.call(this, name, age);
}
Child4.prototype = Object.create(Person4.prototype);
Child4.prototype.constructor = Child4;

Child4.prototype.getAge = function() {
	console.log('===4', this.age);
	return this.age;
};

let child4 = new Child4('孩子4', 10);
child4.getName();

new

/**
 * new 的原理
 */

function Person(name, age) {
    this.name = name;
    this.age = age
}

Person.prototype.play = function() {
    console.log('play');
}

function _new(clazz, ...args) {
    let obj = {}
    obj.__proto__ = clazz.prototype;  // 关联原型
    clazz.call(obj, ...args)
    return obj
}
const lili = _new(Person, '丽丽', 10)  //  new Person('丽丽', 10) 
lili.play()

限制并发请求数量

class Limit {
	constructor(max) {
		this.tasks = [];
		this.max = max || 6;
		setTimeout(() => {
			this.start();
		}, 0);
	}
	addTask(task) {
		this.tasks.push(task);
	}
	start() {
		if (this.tasks.length === 0) return;
		let min = Math.min(this.tasks.length, this.max);
		for (let i = 0; i < min; i++) {
			this.max--;
			let task = this.tasks.shift(); // 从数组头部取任务
			task()
				.then((res) => {
					console.log(res);
				})
				.finally(() => {
					this.max++; // 超过最大并发6后的任务将按照任务顺序依次执行。此处可理解为递归操作。
					this.start();
				});
		}
	}
}

let q = new Limit();

for (let i = 0; i < 10; i++) {
	q.addTask(() => {
		return new Promise((resolve, reject) => {
			setTimeout(() => {
				resolve('成功' + i);
			}, 1000);
		});
	});
}

类数组

类数组具有length属性,但是没有Arrary的原型方法

    1. 使用 Array.from()
    1. 使用 Array.prototype.slice.call()
    1. 使用 Array.prototype.forEach() 进行属性遍历并组成新的数组
let obj = {
	length: 2,
	0: 1,
	a: 2,
	'-2': 3,
	3: 3
};
console.log(Array.from(obj));  // [ 1, undefined ]

忽略不是数字和和非整数的属性,按照length生成,并且自动填充

node

nodejs的轮询机制

  1. timers: 处理setInterval, setTimeout
  2. pedding callbacks:系统的回调函数
  3. idle, prepare node内部使用
  4. poll
    • 进入时有回调函数,就处理回调,当回调函数处理完或者系统达到最大的限制之后,跳过当前进入check
    • 进入时没有回调函数,等待回调函数,同时检测timers阶段是不是有超时的回调,有就进入check
  5. check: 处理setImmediate的回调
  6. close callbacks: 这个阶段用来处理如socket的close事件