JS篇
*Promise相关
class myPromise{
constructor(fn){
this.state = "pending"
this.successFun = []
this.failFun = []
let resolve = (val)=>{
if(this.state!=="pending") return;
this.state = "success"
setTimeout(()=>{
this.successFun.forEach((item)=>item.call(this,val))
})
};
let reject = (err)=>{
if(this.state!=="pending")return;
this.state = "fail"
setTimeout(()=>{
this.failFun.forEach((item)=>item.call(this,err))
})
}
try{
fn(resolve,reject);
}catch(error){
reject(error)
}
}
then(resolveCallback,rejectCallback){
resolveCallback = typeof resolveCallback!=="function"?(v)=>v:resolveCallback;
rejectCallback = typeof rejectCallback!=="function"?(v)=>v:rejectCallback;
return new myPromise((resolve,reject)=>{
this.successFun.push((val)=>{
try{
let x = resolveCallback(val)
x instanceof myPromise?x.then(resolve,reject):resolve(x);
}catch(error){
reject(error)
}
});
this.failFun.push((val)=>{
try{
let x = rejectCallback(val);
x instanceof myPromise?x.then(resolve,reject):reject(x);
}catch(error){
reject(error)
}
});
});
}
static all(promiseArr){
let result = [];
let count = 0;
return new myPromise((resolve,reject)=>{
for(let i=0;i<promiseArr.length;i++){
promiseArr[i].then((res)=>{
result[i] = res
count++
if(count===promiseArr.length){
resolve(result)
}
},(err)=>{
reject(err)
})
}
})
}
static race(promiseArr){
return new myPromise((resolve,reject)=>{
for(let i=0;i<promiseArr.length;i++){
promiseArr[i].then((res)=>{resolve(res)},(err)=>{reject(err)})
}
})
}
}
let promise1 = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(123);
}, 2000);
});
let promise2 = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(1234);
}, 1000);
});
myPromise.all([promise1, promise2]).then(res => {
console.log(res);
})
myPromise.race([promise1, promise2]).then(res => {
console.log(res);
});
promise1.then(res => {
console.log(res); //过两秒输出123
return new myPromise((resolve, reject) => {
setTimeout(() => {
resolve("success");
}, 1000);
});
},
err => {console.log(err);}).then(
res => {
console.log(res); //再过一秒输出success
},
err => {
console.log(err);
}
);
1.实现一个节流函数
function throttle(fn,delay=500){
let timer = null
return function(){
if(!timer){
fn.apply(this,arguments)
timer = setTimeout(()=>{
timer = null
},delay)
}
}
}
2.实现一个防抖函数
function debounce(fn,delay=500){
let timer = null
return function(){
clearTimeout(timer)
timer = setTimeout(()=>{
fn.apply(this,arguments)
},delay)
}
}
应用 通过防抖和节流,来获取滚动条距离顶部的距离
<html>
<head>
<style>
div{
height:2000px;
background-color:blue
}
</style>
</head>
<body>
<div>233</div>
<script>
//节流函数,同一个单位时间内只执行一次函数,
function throttle(fn,delay = 2000){
let timer = null
return function(){
if(timer==null){
fn.apply(this,arguments)
timer = setTimeout(()=>{
timer = null
},delay)
}
}
}
//防抖函数,在一段时间后,函数执行,如果期间再次触发函数,则重新计时
function debounce(fn,delay=2000){
let timer = null
return function(){
clearTimeout(timer)
timer = setTimeout(()=>{
fn.apply(this,arguments)
},delay)
}
}
function showTop(){
let scrollTop = document.body.scrollTop||document.documentElement.scrollTop;
console.log("滚动条位置:",scrollTop)
}
// window.onscroll=throttle(showTop,3000)
window.onscroll=debounce(showTop)
</script>
</body>
</html>
3.对象的继承
ES5
function Parents(name){
this.name = name
}
Parents.prototype.eat = function(){
console.log(this.name," is eating")
}
function Child(name,age){
Parents.call(this,name)
this.age = age
}
Child.prototype = Object.create(Parents.prototype)
let xiaoming = new Child("xiaoming",20)
console.log(xiaoming.name)
console.log(xiaoming.age)
console.log(xiaoming.eat())
//xiaoming
//20
//xiaoming is eating
ES6
class Parents{
constructor(name){
this.name = name
}
eat(){
console.log(this.name," is eating")
}
}
class Child extends Parents{
constructor(name,age){
super(name)
this.age = age
}
}
let xiaoming = new Child("xiaoming",20)
console.log(xiaoming.name)
console.log(xiaoming.age)
console.log(xiaoming.eat())
//xiaoming
//20
//xiaoming is eating
4.浅拷贝
浅拷贝的方法
- Object.assign()
- Array.prototype.concat()
- Array.prototype.slice()
function shallowCopy(object){
if(!object||typeof object !=='object'){
return object
}
let newObject = Array.isArray(object)?[]:{}
for(let key in object){
if(object.hasOwnProperty(key)){
newObject[key] = object[key]
}
}
return newObject
}
5.深拷贝
深拷贝的方法
- json.parse(json.stringify())
- lodash的cloneDeep方法
function deepCopy(target){
let type = Object.prototype.toString.call(target)
let res = undefined
if(type ==='[object Object]'){
res = {}
}else if(type==='[object Array]'){
res = []
}else{
return target
}
for(let key in target){
let item = target[key]
let itemType = Object.prototype.toString.call(item)
if(itemType==='[object Object]'||itemType==='[object Array]'){
res[key] = deepCopy(item)
}else{
res[key] = item
}
}
return res
}
应用
let a = [1,2,3,{a:"a1",b:{name:"张三"}}]
let b = deepCopy(a)
let c = shallowCopy(a)
b[3].b.name = "我是b"
c[3].b.name = "我是c"
console.log(a)
console.log(b)
console.log(c)
//[ 1, 2, 3, { a: 'a1', b: { name: '我是c' } } ]
//[ 1, 2, 3, { a: 'a1', b: { name: '我是b' } } ]
//[ 1, 2, 3, { a: 'a1', b: { name: '我是c' } } ]
6.call
fn.call(上下文环境,执行所需的单个参数)
会使当前函数立即执行,用来改变函数的this指向
Function.prototype.MyCall = function(ctx){
//判断对象是否为函数
if(typeof this!== 'function'){
throw new TypeError("类型错误")
}
//获取参数
let args = [...arguments].slice(1)
//判断上下文是否为null
ctx = ctx||window
//将调用函数设为对象的方法
ctx.fn = this
let result = null
result = ctx.fn(...args)
delete ctx.fn
return result
}
7.apply
fn.apply(上下文环境,执行所需的数组) 会使当前函数立即执行,用来改变函数的this指向
Function.prototype.MyApply = function(ctx){
if(typeof this!=='function'){
throw new TypeError("类型错误")
}
ctx = ctx||window
ctx.fn = this
let result = null
if(arguments[1]){
result = ctx.fn(...arguments[1])
}else{
result = ctx.fn
}
delete ctx.fn
return result
}
8.bind
bind 的作用与 call 和 apply 相同,
区别是后两者是立即调用函数,而bind是返回了一个函数,需要调用时再执行
Function.prototype.MyBind=function(ctx){
if(typeof this!=='function'){
throw new TypeError("类型错误")
}
let fn = this
return function(){
fn.apply(ctx,arguments)
}
}
9.jsonp
function jsonp(url,data={},callback = 'callback'){
data.callback = callback
let params = []
for(let key in data){
params.push(key+"="+data[key])
}
let script = document.createElement('script')
script.src = url+'?'+params.join("&")
document.body.appendChild(script)
return new Promise((resolve,reject)=>{
window[callback] = (data)=>{
try{
resolve(data)
}catch{
reject(data)
}finally{
//移除插入的script元素
script.parentNode.removeChild(script)
}
}
})
}
10.instanceof
顺着原型链去找,直到找到相同的原型对象
function myInstanceof(left,right){
// 这里先用typeof来判断基础数据类型,如果是,直接返回false
if(typeof left !== 'object' || left === null){
return false
}
//getProtypeOf是Object对象自带的API,能够拿到参数的原型对象
let proto = Object.getPrototypeOf(left)
while(proto!==null){
if(proto===right.prototype){
return true
}
proto = Object.getPrototypeOf(proto)
}
return false
}
11.render虚拟DOM渲染函数
function render(elementObj) {
//1.根据tag名称创建节点
let el = document.createElement(elementObj.tagName)
//2.为创建的节点添加属性
for (let propName in elementObj.props) {
let propValue = elementObj.props[propName]
el.setAttribute(propName, propValue)
}
//3.如果存在内容,通过 innerText 加入
if (elementObj.content) {
el.innerText = elementObj.content
}
//4.如果存在子节点,依次遍历递归添加
if (elementObj.children) {
elementObj.children.forEach((child) => {
el.appendChild(render(child))
})
}
return el
}
设计模式
1.观察者模式
观察者定义了一种一对多的依赖关系,让多个观察者同时监听某一个目标对象
当这个目标对象的状态发生了变化时,会通知所有观察者对象,使它们能够自动更新
class Publisher{
constructor(){
this.observers = []
}
addObserver(observer){
this.observers.push(observer)
}
removeObserver(observer){
this.observers.forEach((item,index)=>{
if(observer===item){
this.observers.splice(index,1)
}
})
}
notifyObservers(){
this.observers.forEach(item=>{
item.update(this)
})
}
}
class Observer{
update(Publisher){}
}
class StarPublisher extends Publisher{
constructor(){
super()
this.state = null
}
getData(){
return this.state
}
setData(data){
this.state = data
this.notifyObservers()
}
}
class StarObserver extends Observer{
constructor(id){
super()
this.id = id
this.state = null
}
update(publisher){
this.state = publisher.getData()
this.remind()
}
remind(){
console.log(this.id+" 接受消息: ",this.state)
}
}
let star = new StarPublisher()
let fun1 = new StarObserver("#01")
let fun2 = new StarObserver("#02")
let fun3 = new StarObserver("#03")
star.addObserver(fun1)
star.addObserver(fun2)
star.addObserver(fun3)
star.setData("Jay Chou 出新专辑了!")
star.removeObserver(fun2)
star.setData("不信谣不传谣...")
//#01 接受消息: Jay Chou 出新专辑了!
//#02 接受消息: Jay Chou 出新专辑了!
//#03 接受消息: Jay Chou 出新专辑了!
//#01 接受消息: 不信谣不传谣...
//#03 接受消息: 不信谣不传谣...
2.发布订阅模式
观察者模式:发布者直接接触订阅者;
发布——订阅模式:发布者和订阅者通过中间平台间接接触, 发布者无法感知订阅者
class EventEmitter {
constructor() {
this.handlers = {}
}
//注册事件监听器
on(event, callback) {
if (!this.handlers[event]) {
this.handlers[event] = []
}
this.handlers[event].push(callback)
}
//移除某个事件回调队列里的指定回调函数
off(event, callback) {
const callbacks = this.handlers[event],
index = callbacks.indexOf(callback)
if (index !== -1) {
callbacks.splice(index, 1)
}
}
// 触发目标事件
emit(event, ...params) {
if (this.handlers[event]) {
this.handlers[event].forEach((callback) => {
callback(...params);
});
}
}
// 为事件注册单次监听器
once(event, callback) {
// 对回调函数进行包装,使其执行完毕自动被移除
const wrapper = (...params) => {
callback(...params);
this.off(event, wrapper);
};
this.on(event, wrapper);
}
}
排序算法
排序算法的讲解可以看我这篇文章八大算法的Golang实现,这里只贴出相关代码
冒泡排序
/**
* @param {number[][]} grid
* @return {number[]}
* @param {number[]} array
*/
function bubbleSort(array){
let sortBorder=array.length-1
let lastExchangeIndex =0
for(let i=0;i<array.length-1;i++){
let isSort = true
for(let j=0;j<sortBorder;j++){
if(array[j]>array[j+1]){
let temp = array[j]
array[j] = array[j+1]
array[j+1] = temp
isSort = false
lastExchangeIndex = j
}
}
sortBorder = lastExchangeIndex
if(isSort){
return array
}
}
return array
}
var arr = [3,1,4,6,5,7,2,3]
console.log(bubbleSort(arr));
选择排序
/**
*
* @param {Array} array
*/
let array = [5,9,6,1,3,4,7,2,8,5]
function selectionSort(array){
let minIndex = 0
for(let i=0;i<array.length-1;i++){
minIndex = i
for(let j=i+1;j<array.length;j++){
if(array[j]<array[minIndex]){
minIndex = j
}
}
[array[i],array[minIndex]] = [array[minIndex],array[i]]
}
return array
}
console.log(selectionSort(array))
插入排序
/**
* @param {Array} array
*/
function insertionSort(array){
for(let i=1;i<array.length;i++){
let insertValue = array[i]
let j = i-1
for(j;j>=0&&array[j]>insertValue;j--){
array[j+1]=array[j]
}
array[j+1] = insertValue
}return array
}
let array = [5,9,6,1,3,4,7,2,8,5]
console.log(insertionSort(array))
希尔排序
/**
*
* @param {int[]} array
*/
function ShellSort(array){
let n =array.length
// console.log(n)
for(let gap=Math.floor(n/2);gap>0;gap=Math.floor(gap/2)){
for(let i=gap;i<n;i++){
let temp=array[i]
let j=i-gap
while(j>=0&&array[j]>temp){
array[j+gap]=array[j]
j=j-gap
}
array[j+gap] = temp
}
}
}
let array=[5,3,9,12,6,1,7,2,13,4,11,8,10]
ShellSort(array)
console.log(array)
归并排序
/**
*
* @param {number[]} array
* @param {number} start
* @param {number} end
*/
function mergeSort(array,startIndex,endIndex){
if(startIndex<endIndex){
let mid=parseInt((startIndex+endIndex)/2)
mergeSort(array,startIndex,mid)
mergeSort(array,mid+1,endIndex)
merge(array,startIndex,mid,endIndex)
}
}
function merge(array,start,mid,end){
let tempArray = Array(end-start+1)
let p1 = start
let p2 = mid +1
let p=0
while(p1<=mid&&p2<=end){
if(array[p1]<=array[p2]){
tempArray[p]=array[p1]
p1++
}else{
tempArray[p]=array[p2]
p2++
}
p++
}
while(p1<=mid){
tempArray[p]=array[p1]
p++
p1++
}
while(p2<=end){
tempArray[p]=array[p2]
p++
p2++
}
for(let i=0;i<tempArray.length;i++){
array[start+i]=tempArray[i]
}
}
let array=[5,3,9,12,6,1,7,2,13,4,11,8,10]
mergeSort(array,0,array.length-1)
console.log(array)
快速排序
/**
* @param {number[]} array
*/
function quickSort(array,startIndex,endIndex){
if(startIndex>endIndex){
return
}
let pivotIndex = partition(array,startIndex,endIndex)
quickSort(array,startIndex,pivotIndex-1)
quickSort(array,pivotIndex+1,endIndex)
}
function partition(array,startIndex,endIndex){
let pivot = array[startIndex]
let left = startIndex,right = endIndex
while(left!=right){
while(left<right&&array[right]>pivot){
right--
}
while(left<right&&array[left]<=pivot){
left++
}
if(left<right){
[array[left],array[right]] = [array[right],array[left]]
}
}
[array[left],array[startIndex]] = [array[startIndex],array[left]]
return left
}
桶排序
/**
*
* @param {number[]} array
*/
function bucketSort(array){
//1.获取数组的最大值和最小值,算出差值 d
let min = Math.min.apply(null,array)
let max = Math.max.apply(null,array)
//2.初始化桶
let bucketNum = parseInt((max-min)/array.length)+1
let bucketList = Array(bucketNum).fill(0).map(()=>Array())
//3.遍历原始数组,将每个元素放入桶中
for(let i=0;i<array.length;i++){
let num = parseInt((array[i]-min)/array.length)
bucketList[num].push(array[i])
}
//4.对每个桶中的数组进行排序
for(let i=0;i<bucketList.length;i++){
bucketList[i].sort()
}
//5.输出
let index=0
for(let i=0;i<bucketList.length;i++){
for(let j=0;j<bucketList[i].length;j++){
array[index] = bucketList[i][j]
index++
}
}
}
let array=[5,16,95,1,45,66,75,20,62,38,76,94,43]
bucketSort(array)
console.log(array)