写在前面
在前端的学习中,不仅要学会用,一些基础功能的手写也非常重要
食用对象:初级前端
美味指数:?????
1. 实现可拖拽DIV
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
#box {
width: 100px;
height: 100px;
background: #07d268;
position: absolute;
left: 100px;
top: 100px;
}
</style>
</head>
<body>
<div id="box">1</div>
</body>
<script>
const box = document.querySelector('#box');
let drag = false;
let position = [];
box.addEventListener('mousedown', (e) => {
drag = true
position = [e.clientX, e.clientY]
})
document.addEventListener('mousemove', (e) => {
if (drag !== true) {
return
}
const x = e.clientX;
const y = e.clientY;
const left = parseInt(getComputedStyle(box).left || 0); //js获取head里css需要用getComputedStyle
const top = parseInt(getComputedStyle(box).top || 0);
box.style.left = left + x - position[0] + 'px'
box.style.top = top + y - position[1] + 'px'
position = [x, y]
})
document.addEventListener('mouseup', () => {
drag = false
})
</script>
复制代码
2. 双飞翼布局
<style>
body {
min-width: 550px
}
.column {
float: left;
}
#center {
width: 100%;
height:200px;
background-color: #cccccc;
}
#main {
margin: 0 190px 0 190px;
}
#left {
background-color: #ffff00;
width: 190px;
height:200px;
margin-left: -100%;
}
#right {
background-color: red;
width: 190px;
height:200px;
margin-left: -190px;
}
</style>
<div id="center" class="column">
<div id="main">
this is center
</div>
</div>
<div id="left" class="column">this is left</div>
<div id="right" class="column">this is right</div>
复制代码
3. 圣杯布局
<style>
body {
min-width: 550px
}
#header {
text-align: center;
}
#container {
padding-left: 200px;
padding-right: 150px;
}
#container .column {
float: left
}
#center {
background-color: #ccc;
width: 100%
}
#left {
position: relative;
background-color: yellow;
width: 200px;
margin-left: -100%;
right: 200px
}
#right {
background-color: red;
width: 150px;
margin-right: -150px;
}
#footer {
clear: both;
text-align: center;
background-color: #f1f1f1
}
</style>
<div id="container">
<div id="center" class="column">this is center</div>
<div id="left" class="column">this is left</div>
<div id="right" class="column">this is right</div>
</div>
复制代码
4. 两栏布局
- 绝对定位方式
<style>
.wrap {
position: relative;
width: 100%;
height: 200px;
}
.box1 {
position: absolute;
width: 150px;
height: 200px;
background-color: #d43131;
}
.box2 {
position: absolute;
left: 150px;
right: 0;
height: 200px;
background-color: #07d268;
}
</style>
<div class="wrap">
<div class="box1">1</div>
<div class="box2">2</div>
</div>
复制代码
- 浮动方式
<style>
.box1 {
float: left;
width: 150px;
height: 200px;
background-color: #d43131;
}
.box2 {
margin-left: 150px;
height: 200px;
background-color: #07d268;
}
</style>
<div class="wrap">
<div class="box1">1</div>
<div class="box2">2</div>
</div>
复制代码
- flex方式
<style>
.wrap {
display: flex;
height: 200px;
}
.box1 {
width: 150px;
height: 200px;
background-color: #d43131;
}
.box2 {
flex: 1;
height: 200px;
background-color: #07d268;
}
</style>
<div class="wrap">
<div class="box1">1</div>
<div class="box2">2</div>
</div>
复制代码
5. 画三角形或梯形⭐⭐⭐⭐
- CSS方法:
<style>
.box {
width: 0;
height: 0;
border-right: 100px solid transparent;
border-left: 100px solid transparent;
border-top: 100px solid #d43131;
border-bottom: 100px solid transparent;
}
</style>
<div class="box"></div>
复制代码
如果需要画梯形就改变width的值即可
- Canvas方法:
<style>
#box2 {
width: 300px;
height: 300px;
}
</style>
<canvas id="box2"></canvas>
const canvas = document.querySelector('#box2')
const ctx = canvas.getContext('2d')
ctx.beginPath()
ctx.moveTo(0, 0);
ctx.lineTo(100, 0);
ctx.lineTo(0, 50);
ctx.closePath();
ctx.fillStyle="#8989e7";
ctx.fill();
复制代码
6. reduce模拟map
Array.prototype._map = function(fn, callbackThis) {
// 最终返回的新数组
let res = [];
// 定义回调函数的执行环境
// call第一个参数传入null,则 this指向全局对象,同 map的规则
let CBThis = callbackThis || null;
this.reduce((brfore, after, idx, arr) => {
// 传入map回调函数拥有的参数
// 把每一项的执行结果push进res中
res.push(fn.call(CBThis, after, idx, arr));
}, null);
return res;
};
复制代码
7. 手写flatter考虑多层级
function flat(arr) {
const isDeep = arr.some(item => item instanceof Array)
if (!isDeep) {
return arr //已经是flatern
}
const res = [].concat(...arr) //arr里的每个元素都会通过concat连接
return flat(res)
}
复制代码
8. 手写new
// 手写一个new
function myNew(fn, ...args) {
// 创建一个空对象
let obj = {}
// 使空对象的隐式原型指向原函数的显式原型
obj.__proto__ = fn.prototype
// this指向obj
let result = fn.apply(obj, args)
// 返回
return result instanceof Object ? result : obj
}
复制代码
9. 手写trim
String.prototype.trim = function () {
return this.replace(/^\s+/, '').replace(/\s+$/, '')
}
复制代码
10. 手写柯里化
//经典面试题:实现add(1)(2)(3)(4)=10;add(1)(1,2,3)(2)=9
//指的是将一个接受多个参数的函数,变为接受一个参数返回一个函数的固定形式
//这样便于再次调用,例如f(1)(2)
function add() {
let args = Array.from(arguments)
let adder = function() {
args.push(...arguments)
return adder
}
adder.toString = function() {
return args.reduce(function(a, b){
return a + b
}, 0)
}
return adder
}
let a = add(1,2,3)
let b = add(1)(2)(3)
console.log(a)
console.log(b)
console.log(a==6)
console.log(b==6)
复制代码
11. 手写深度比较
//判断是否是对象或数组
function isObject(obj) {
return typeof obj === 'object' && obj !== 'null'
}
function isEqual(obj1, obj2) {
if (!isObject(obj1) || !isObject(obj2)) {
//值类型(注意,参与equal的一般不会是函数)
return obj1 === obj2
}
//两个都是对象或数组,而且不相等
//1.先取出obj1和obj2的keys,比较个数
const obj1Keys = Object.keys(obj1)
const obj2Keys = Object.keys(obj2)
if (obj1Keys.length !== obj2Keys.length) {
return false
}
//2.以obj1为基准,和obj2一次递归比较
for (let key in obj1) {
if (!isEqual(obj1[key], obj2[key])) {
return false
}
}
return true
}
复制代码
12. 手写深拷贝
function deepClone(obj = {}) {
if(typeof obj !== 'object' || obj == null) {
//obj是null,或者不是对象或数组,直接返回
return obj;
}
//初始化返回结果
let result;
if (obj instanceof Array) {
result = [];
} else {
result = {};
}
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
//递归调用
result[key] = deepClone(obj[key]);
}
}
return result;
}
复制代码
13. 手写apply
Function.prototype.myApply = function(context) {
const ctx = context || window
//最重要的一步,1.myApply内部的this是指向调用者fn函数的。2.ctx.func就是fn函数,ctx调用了fn函数,因此fn函数内部的this指向ctx
ctx.func = this
let result = arguments[1] ? ctx.func([...arguments[1]]) : ctx.func()
delete ctx.func
return result
}
obj = {c: 2}
function a(x, y) {
console.log(this, x, y)
}
a.apply(obj, [1, 2])//{c:2} 1 2
a.myApply(obj, [1, 2])//{c:2,func:[function a]} 1 2
复制代码
14. 手写bind
//模拟bind
Function.prototype.bind1 = function () {
//将参数拆解为数组
const args = Array.prototype.slice.call(arguments);
//获取this(数组第一项)
const t = args.shift();
//fn1.bind(...)中的fn1
const self = this
//返回一个函数
return function () {
return self.apply(t, args)
}
}
复制代码
15. 手写发布订阅模式
// 发布订阅模式
class EventEmitter {
constructor() {
// 事件对象,存放订阅的名字和事件
this.events = {};
}
// 订阅事件的方法
on(eventName,callback) {
if (!this.events[eventName]) {
// 注意时数据,一个名字可以订阅多个事件函数
this.events[eventName] = [callback]
} else {
// 存在则push到指定数组的尾部保存
this.events[eventName].push(callback)
}
}
// 触发事件的方法
emit(eventName) {
// 遍历执行所有订阅的事件
this.events[eventName] && this.events[eventName].forEach(cb => cb());
}
// 移除订阅事件
removeListener(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(cb => cb !== callback)
}
}
// 只执行一次订阅的事件,然后移除
once(eventName,callback) {
// 绑定的时fn, 执行的时候会触发fn函数
let fn = () => {
callback(); // fn函数中调用原有的callback
this.removeListener(eventName,fn); // 删除fn, 再次执行的时候之后执行一次
}
this.on(eventName,fn)
}
}
复制代码
16. 手写promise
class MyPromise2 {
constructor(executor) {
// 规定状态
this.state = "pending"
// 保存 `resolve(res)` 的res值
this.value = undefined
// 保存 `reject(err)` 的err值
this.reason = undefined
// 成功存放的数组
this.successCB = []
// 失败存放的数组
this.failCB = []
let resolve = (value) => {
if (this.state === "pending") {
this.state = "fulfilled"
this.value = value
this.successCB.forEach(f => f())
}
}
let reject = (reason) => {
if (this.state === "pending") {
this.state = "rejected"
this.reason = reason
this.failCB.forEach(f => f())
}
}
try {
// 执行
executor(resolve, reject)
} catch (error) {
// 若出错,直接调用reject
reject(error)
}
}
then(onFulfilled, onRejected) {
if (this.state === "fulfilled") {
onFulfilled(this.value)
}
if (this.state === "rejected") {
onRejected(this.reason)
}
if (this.state === "pending") {
this.successCB.push(() => { onFulfilled(this.value) })
this.failCB.push(() => { onRejected(this.reason) })
}
}
}
复制代码
17. 手写promise.all
Promise.all = function (promises) {
let list = []
let count = 0
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(res => {
list[i] = res
count ++
if (count === promises.length) {
resolve(list)
}
}, err => reject(err))
}
})
}
复制代码
18. 大数相加
function add(a, b) {
var maxLength = Math.max(a.length, b.length)
a = a.padStart(maxLength, 0)
b = b.padStart(maxLength, 0)
var t = 0
var f = 0
var sum = ''
for(let i = a.length - 1; i >= 0; i--) {
t = parseInt(a[i]) + parseInt(b[i]) + f
f = Math.floor(t / 10)
sum = t % 10 + sum
console.log(sum)
}
if(f === 1){
sum = '1' + sum
}
return sum
}
复制代码
19. 手写防抖函数
function debounce(fn, delay = 500) {
let timer = null
return function () {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
//为了使函数的参数和this正确
fn.apply(this, arguments)
timer = null
}, delay)
}
}
复制代码
20. 手写节流函数
function throttle(fn, delay = 100) {
let timer = null
return function () {
if (timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}
复制代码
21. 手写快排
Array.prototype.quickSort = function() {
const rec = (arr) => {
if (arr.length <= 1) { return arr }
const left = []
const right = []
const mid = arr[0]
for (let i = 1; i < arr.length; i++) {
if (arr[i] < mid) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
return [...rec(left), mid, ...rec(right)]
}
const res = rec(this)
res.forEach((n, i) => this[i] = n)
}
复制代码
22. 手写二分搜索
Array.prototype.binarySearch = function (item) {
if (typeof item === 'undefined') return -1
let low = 0
let high = this.length -1
while (low <= high) {
const mid = Math.floor((low + high) / 2)
const element = this[mid]
if (element < item) {
low = mid + 1
} else if (element > item) {
high = mid - 1
} else {
return mid
}
}
return -1
}
复制代码
23. 手写instanceof
//遍历A的原型链,如果找到B.prototype,返回true,否则返回false
const _instanceof = (A, B) => {
let p = A
while(p) {
if(p === B.prototype) {
return true
}
p = p.__proto__
}
return false
}
复制代码
感谢阅读
非常感谢您到阅读到最后,如果有错误希望您能够指出,以免误导其他人,如果您觉得对您有帮助的话,希望能够点个赞,加个关注,有任何问题都可以联系我,希望能够一起进步。
最后祝您前程似锦,我们各自攀登,高处相见?!
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END