面试前端面试常考手写题 - JavaScript - CSS
Posted YK菌
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试前端面试常考手写题 - JavaScript - CSS相关的知识,希望对你有一定的参考价值。
持续更新中…
文章目录
1. CSS布局
1.1 盒子居中对齐
1.2 两栏布局
1.3 三栏布局
双飞翼
圣杯
1.4 品字布局
2. JS手写函数
2.1 手写原生
Promise
/**
* Promise构造函数
* @param {*} executor 执行器函数(同步执行)(resolve, reject) => {}
*/
function Promise(executor) {
const self = this; // 保存当前实例对象的this的值
// 添加属性
self.PromiseState = PENDING // 给promise对象指定status属性,初始值为pending
self.PromiseResult = null // 给promise对象指定一个用于存储结果数据的属性
self.callbacks = [] // 存的是对象 每个元素的结构:{onResolved() {}, onRejected() {}}
/**
* executor内部定义成功时调用的函数
* @param {*} value 成功的值
* @returns
*/
function resolve(value) {
// 如果当前状态不是pending,直接结束
if (self.PromiseState !== PENDING) return
// 1. 修改对象的状态(promiseState)为 fulfilled
self.PromiseState = RESOLVED
// 2. 设置对象结果值(promiseResult)为 value
self.PromiseResult = value
// 如果有待执行的callback函数,立即【异步】执行回调函数onResolved
if (self.callbacks.length > 0) {
setTimeout(() => { // 放入队列中执行所有成功的回调
self.callbacks.forEach(callbacksObj => {
callbacksObj.onResolved(value)
})
}, 0)
}
}
/**
* executor内部定义失败时调用的函数
* @param {*} reason 失败的原因
* @returns
*/
function reject(reason) {
// 如果当前状态不是pending,直接结束
if (self.PromiseState !== PENDING) return
// 1. 修改对象的状态(promiseState)为 rejected
self.PromiseState = REJECTED
// 2. 设置对象结果值(promiseResult)为 reason
self.PromiseResult = reason
// 如果有待执行的callback函数,立即【异步】执行回调函数onRejected
if (self.callbacks.length > 0) {
setTimeout(() => { // 放入队列中执行所有失败的回调
self.callbacks.forEach(callbacksObj => {
callbacksObj.onRejected(reason)
})
}, 0)
}
}
// 立即【同步】执行executor函数
try {
executor(resolve, reject)
} catch(error) { // 如果执行器抛出异常,promise对象变成rejected状态
reject(error)
}
}
Promise.all
接收的是一个promise数组promises
当传入的所有promise都成功,则返回一个成功的promise,值为所有成功promise的值的数组【注意顺序】
当传入的promises中有一个不成功,则返回一个失败的promise,其值为这个失败的promise的值
思路
首先创建一个结果数组,再创建要给计数变量
Promise.all = function(promises){
return new Promise((resolve, reject)=>{
let count = 0
const values = []
for(let i = 0; i < promises.length; i++){
Promise.resolve(promise[i]).then(value => {
count++
values[i] = value
if(count === promises.length){
resolve(values)
}
}, reason => {
reject(reason)
})
}
})
}
Promise.race
接收的是一个promise数组promises
返回一个promise,状态和值都取决于第一个改变状态的promise
Promise.race = function(promises) {
return new Promise((resolve, reject)=>{
for(let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(value => {
resolve(value)
}, reason => {
reject(reason)
}
}
})
}
Promise.resolve()
Promise.resolve = function(value) {
return new Promise((resolve, reject) => {
if(value instanceof Promise) {
value.then(resolve, reject)
} else {
resolve(value)
}
}
}
Promise.reject()
Promise.reject = function(reason) {
return new Promise((resolve, reject)=> {
reject(reason)
})
}
call
function call(Fn, obj, ...args) {
if (obj === undefined || obj === null) {
// 表示全局对象(ES11新增特性)
obj = globalThis;
}
// 为 obj 添加临时的方法
obj.temp = Fn;
// 调用 temp 方法
let result = obj.temp(...args);
// 删除tempfangfa
delete obj.temp;
// 返回执行结果
return result;
}
apply
function apply(Fn, obj, arr) {
if (obj === undefined || obj === null) {
obj = globalThis;
}
// 为obj添加临时方法
obj.temp = Fn;
// 执行方法
let result = obj.temp(...arr);
// 删除临时属性
delete obj.temp;
// 返回结果
return result;
}
bind
function bind(Fn, obj, ...args) {
// 返回一个新的函数
return function (...args2) {
// 执行 call 函数、
return call(Fn, obj, ...args, ...args2);
};
}
new
function newInstance(Fn, ...args) {
// 1. 创建新对象
// 创建空的object实例对象,作为Fn的实例对象
const obj = {};
// 修改新对象的原型对象
// 将Fn的prototype(显式原型)属性赋值给obj的__proto__(隐式原型)属性
obj.__proto__ = Fn.prototype;
// 2. 修改函数内部this指向新对象,并执行
//
const result = Fn.call(obj, ...args);
// 3. 返回新对象
// return obj
// 与new保持一直,如果构造函数有返回值,返回值是对象a就返回对象a,否则返回实例对象
return result instanceof Object ? result : obj;
}
instanceof
function myInstanceOf(obj, Fn) {
// 得到obj的隐式原型对象
let protoObj = obj.__proto__;
// 原型对象存在,就遍历原型链
while (protoObj) {
// 实例对象的隐式原型 等于 构造函数的显式原型 就返回true
if (protoObj === Fn.prototype) {
return true;
}
// 不相等就根据原型链一直往上找 直到最后为null
protoObj = protoObj.__proto__;
}
return false;
}
2.2 自定义函数
数组去重
function unique1(arr) {
const result = [];
const obj = {};
arr.forEach((item) => {
if (!obj.hasOwnProperty(item)) {
obj[item] = true;
result.push(item);
}
});
return result;
}
function unique(arr) {
return [...new Set(arr)];
}
function objSort(obj){
let newObj = {}
//遍历对象,并将key进行排序
Object.keys(obj).sort().map(key => {
newObj[key] = obj[key]
})
//将排序好的数组转成字符串
return JSON.stringify(newObj)
}
function unique(arr){
let set = new Set();
for(let i=0;i<arr.length;i++){
let str = objSort(arr[i])
set.add(str)
}
//将数组中的字符串转回对象
arr = [...set].map(item => {
return JSON.parse(item)
})
return arr
}
数组扁平化
function flatten(arr) {
let result = [];
arr.forEach((item) => {
// 判断是不是数组
if (Array.isArray(item)) {
result = result.concat(flatten(item));
} else {
result = result.concat(item);
}
});
return result;
}
function flatten1(arr) {
let result = [...arr];
// 判断result里有没有子数组
while (result.some((item) => Array.isArray(item))) {
result = [].concat(...result);
}
return result;
}
深拷贝
function deepClone1(target) {
// 通过数据创建JSON格式的字符串
let str = JSON.stringify(target);
// 将 JSON 字符串创建为JS数据
let data = JSON.parse(str);
return data;
}
function deepClone(target) {
if (typeof target === "object" && target !== null) {
// 创建一个容器
const result = Array.isArray(target) ? [] : {};
// 遍历target
for (let key in target) {
// 检测该属性是否为对象本身的属性(不能拷贝原型对象上的属性)
if (target.hasOwnProperty(key)) {
result[key] = deepClone2(target[key]);
}
}
return result;
} else {
return target;
}
}
function deepClone3(target, map = new Map()) {
if (typeof target === "object" && target !== null) {
// 克隆数据之前进行判断,查看数据之前是否被克隆过
let cache = map.get(target);
if (cache) {
return cache;
}
// 创建一个容器
const result = Array.isArray(target) ? [] : {};
// 将新的结果保存到容器中
map.set(target, result);
// 遍历target
for (let key in target) {
// 检测该属性是否为对象本身的属性(不能拷贝原型对象上的属性)
if (target.hasOwnProperty(key)) {
result[key] = deepClone3(target[key], map);
}
}
return result;
} else {
return target;
}
}
深比较
function isObject(obj) {
return typeof obj === object && obj !== null;
}
// 深度比较
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
// 值类型,直接判断【一般不会传函数,不考虑函数】
return obj1 === obj2;
}
if (obj1 === obj2) {
return true;
}
// 两个都是对象或数组,而且不相等
// 1. 先判断键的个数是否相等,不相等一定返回false
const obj1Keys = Object.keys(obj1);
const obj2Keys = Objext.keys(obj2);
if (obj1Keys.length !== obj2Keys.length) {
return false;
}
// 2. 以obj1为基准,和obj2依次递归比较
for (let key in obj1) {
// 递归比较
const res = isEqual(obj1[key], obj2[key]);
if (!res) {
return false;
}
}
// 3. 全相等
return true;
}
节流
使用定时器版本
function throttle(callback, wait=100){
let flag = true
return function(){
if(flag){
flag = false
setTimeout(()=>{
flag = true
callback(...arguments)
}, wait)
}
}
}
不使用定时器版本
function throttle(callback, wait=100){
let start = 0
return function(){
let now = Date.now()
if(now - start >= wait) {
callback(...arguments)
start = now
}
}
}
防抖
连续触发事件,只有最后一次成功
function debounce(callback, wait=100) {
// 定时器变量
let timeId = undefined;
// 返回一个函数
return function () {
// 有定时器,就清空,取消定时器,阻止回调执行
if (timeId !== undefined) {
// 清空定时器
clearTimeout(timeId);
}
// 启动定时器
timeId = setTimeout(() => {
// 执行回调
callback(...arguments);
// 执行完了重置id
timeId = undefined;
}, wait);
};
}
寄生式组合继承
function Person(obj) {
this.name = obj.name
this.age = obj.age
}
Person.prototype.add = function(value){
console.log(value)
}
var p1 = new Person({name:"番茄", age: 18})
function Person1(obj) {
Person.call(this, obj)
this.sex = obj.sex
}
// 这一步是继承以上是关于面试前端面试常考手写题 - JavaScript - CSS的主要内容,如果未能解决你的问题,请参考以下文章