javascript 的new 原理
// Func 为构造函数,args表示传参
function myNew(Func,...args){
// 在内存中创建一个新对象
const obj = {
}
if(Func.prototype){
// 把新对象的原型指向Func构造函数的原型
Object.setPrototypeOf(obj,Func.prototype)
}
// 改变this指向,并执行构造函数
const result = Func.apply(obj,args)
// 判断执行结果的类型,如果是基本类型则直接返回obj
// 如果是function或者 object 则返回result,否者不执行,直接返回
if(typeof result === 'function' || typeof result === 'object' && result !== null{
return result
}
return obj
}
// 测试
function Person(name){
this.name = name
}
Person.prototype.sayName = function (){
alert(`my name is ${
this.name}`)
}
let ming = myNew(Person,'xiaoming')
console.log(ming);
ming.sayName()
bind
//要手写一个bind函数的话,我们需要知道bind拿了什么参数,
//干了什么:1、接收一个对象(this);2、接受n个参数;3、返回函数。
Function.prototype.myBind = function() {
// 保存当前的调用者(普通函数this指向调用者)
let self = this
// 不确定bind的参数传几个,我们所知道的只是第一个参数是this要指的对象
// 第二个一直到最后一个参数我们都不确定,所以我们没法一个一个接收参数,
// 所以要将第一个参数this对象和后面的参数分离。
// 这里的this就是调用myBind的函数
// 参数我们通过arguments获取,arguments可以获取一个函数的所有参数。
// arguments是一个列表的形态(叫:类数组,伪数组),不是真正数组,不好处理,所以我们要把它拆解为数组。
// slice是Array.prototype上的方法,这块可以理解为将arguments这个类数组赋予数组的slice功能
const args = Array.prototype.slice.call(arguments)
// Array.from想要转换成数组的伪数组对象或可迭代对象
// const args = Array.from(arguments)
// 获取this (及数组的第一项)
// const args0 = args.slice(1)
// shift方法将数组的第一项从数组中剔除,改变了原数组,返回了被剔除的这个元素
const _this = args.shift()
// 需要返回一个函数
return function(){
return self.apply(_this,args)
}
}
function fn1(a,b,c){
console.log(this) // this 指向obj
console.log(this.num*a*b*c)
}
const obj = {
num:10}
const fn2 = fn1.myBind(obj,1,2,3)
fn2()
数组扁平化
// 方案1,递归实现
let arr =[1,[2,[3,4,5]]];
function flatten(arr){
const result = []
for(let i = 0;i<arr.length;i++){
if(Array.isArray(arr[i])){
result = result.concat(flatten(arr[i]))
} else {
result.push(arr[i])
}
}
return result
}
// 方案2,reduce 函数迭代
let arr =[1,[2,[3,4,5]]];
function flatten2(arr=[]){
//reduce() 方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,
//每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。
return arr.reduce((prev,next)=>{
// prev(前一次调用 callbackfn 得到的返回值)
// next(数组中正在处理的元素)
// Index(数组中正在处理的元素的索引)
return prev.concat(Array.isArray(next) ? flatten2(next) : next)
},[]) //第一次执行回调函数时,不存在“上一次的计算结果”,所以用一个空数组去接收。
}
// 方案3 split 和 toString
// 实现数组扁平化,由于数组会默认带⼀个 toString 的⽅法,所以可以把数组直接转换成逗号分
// 隔的字符串,然后再⽤ split ⽅法把字符串重新转换为数组,如下⾯的代码所⽰:
let arr =[1,[2,[3,4,5]]];
function flatten3(arr=[]){
return arr.toString().split(',')
}
//方案4、ES6 中的 flat
//法来实现数组扁平化。flat方法的语法:arr.flat ( [depth] )
//其中 depth 是 flat 的参数,depth 是可以传递数组的展开深度(默认不填、数值是 1)
// 即展开一层数组。
//如果层数不确定,参数可以传Infinity,代表不论多少层都要展开
function flatten(arr=[]){
return arr.flat(Infinity)
}
异步Promise 并发限制
/*
new promise一经创建立即执行
使用promise.resolve().then()可以把任务加入到微任务队列,防止立即执行迭代方法
微任务处理过程中,产生新的微任务,会在同一事件循环内,追加到微任务队列里
在完成某个任务时,继续追加任务,保持任务的最大并发数进行执行
任务完成后,从任务队列里移除
promise.all方法可以执行多个promise,你给他多少个他就执行多少个,而且是一起执行,也就是并发执行。
*/
// limit限制并发数,array接收一个数组,所有的并发,iteratorFn执行函数
function asyncPool(limit,array,iteratorFn){
let i = 0
const tasks = []
const doingTasks = [] // 用于保存正在执行的promise
const enqueue = ()=>{
// 空数组处理
if(array.length === 0){
return Promise.resolve()
}
// 每调用一次,初始化一个promise
const task = Promise.resolve().then(()=>{
iteratorFn(array[i++])
})
// 将初始化的promise放入tasks数组
tasks.push(task)
// promise执行完毕后,从doingTasks数组中删除
const doing = task.then(()=>{
doingTasks.splice(doingTasks.indexOf(doing),1)
})
doingTasks.push(doing)
// 使用promise.race 获得doingTasks中promise的情况
// 如果正在执行的promise数量高于limit就执行一次
// 否则继续实例化新的Promise,直到达到limit时执行
const result = doingTasks.length >= limit ? Promise.race(doingTasks) : Promise.resolve()
// 递归,直到遍历完array
return result.then(enqueue)
}
// 所有的promise 都执行完了,调用Promise.all返回
return enqueue().then(()=>{
Promise.all(tasks)
})
}
//异步并发测试shadowscoks
const fn = i=> new Promise(resolve=>{
setTimeout(()=> resolve(i),i)
})
asyncPool(2,[1000,1000,1000,1000],fn).then(res=>{
console.log(res)
})