文章目录
- 函数防抖和函数节流
- 函数柯里化
- 使用setTimeOut实现SetIterval
- 实现instanceof
- 实现new
- 实现call,apply,bind
- 深浅拷贝
- 实现Object.creat()
- 去除字符串首尾空格
- 数组扁平化flaten
- 数组去重
- 实现Arr.reduce
- setTimeout实现setInterval
- Once,函数只执行一或n次
- 实现Promise
- 改写promise并添加超时处理
- 用Promise封装ajax
- 排序算法
- 二分查找
- 虚拟DOM到真实DOM转化
- 对象混合
- 下划线驼峰互转
- sleep链式调用
- js 链式调用sleep、定时器清除、链式取消(nodejs版本)
- js的链式调用和流程控制(sleep)
- 发布订阅模式
- 观察者模式
- 单例模式
- 实现一个 Storage(单例模式)
- 实现一个全局的模态框(单例模式)
- parseURL:parseUrl("https://a.b.come?aaa=123&bbb=hhah&ccc=456")
- 查找json中的children路径
- 全排列
- 六种继承
- 打乱数组
- 限制异步的并发次数
- 实现能够控制并发数的fetch方法, 要求每次最多发送max个, 所有请求完成后, 返回结果
- 红绿灯交替闪烁
- 一秒打印一个数
- 异步超时判断,用race
- 异步加载一个图片
- mergePromise 实现mergePromise函数,把传进去的数组按顺序先后执行,//并且把返回的数据先后放到数组
- 获取浏览器最小字体
- 如何实现一个私有变量,用getName方法可以访问,不能直接访问
- 在obj上找到protoName的方法
- JSONP
- js怎么控制一次加载一张图片,加载完后再加载下一张
- js->tree
- Once
- 解析url
- 千位分隔符,数字转字符串,每3位用逗号分隔
- Iterator
- 所有的key换成下划线UserList -> user_list
- 实现一个对象,该对象有五个方法可以判断不同类型,要鉴别出function、null、NaN、数组、对象
- 实现format函数来摊平字符串: 输入:3(x)2(yz)1(x) 输出:xxxyzyzx
- 输入给定数字的下一个比他大的数字 比如输入 1234 输出 1243
- 实现数组a = [1,[2,[3,[4,null]]]]转换[4,[3,[2,[1,null]]]]
- 模版字符串替换
- 三角形
- 实现一个函数,把一个字符串数组(['zm', 'za', 'b', 'lm', 'ln', 'k']) 格式化成一个对象 { 'b': ['b'], 'k': ['k'], 'l': ['lm', 'ln'], 'z': ['za', 'zm'] }
- vue中对数组方法的重写
函数防抖和函数节流
防抖和节流都是处理处理频繁调用的函数时的一个优化
函数防抖
在规定是一段时间内,只执行最后一次的函数调用,前面的不生效
场景:在搜索框发请求时,避免用户打字幅度较快而发送太多没有必要的请求
function debounce(func, wait) {
var timer = null
return function () {
var ctx = this
var args = arguments
if (timer) clearTimeout(timer)
timer = setTimeout(function () {
func.apply(ctx, args)
}, wait)
}
}
document.getElementsByTagName('button')[0].onclick = debounce(function () { console.log('btn'); }, 1000)
函数节流
函数节流始在规定时间内,只处理第一次调用的函数,后面的函数调用都不处理
场景:在游戏的普通攻击按钮中,无论电机的多快,普通攻击总是在一个规定时间里只触发一次
function throttle(func, wait) {
var pre = new Date()
return function () {
var cur = new Date()
var ctx = this
var args = arguments
if (cur - pre > wait) {
func.apply(ctx, args)
pre = cur
}
}
}
document.onscroll = throttle(function () { console.log('srcoll事件'); }, 1000)
函数柯里化
函数柯里化是将一个可以接受多个参数的函数转变为接受单一参数的函数,并且返回一个函数,这个函数用来接受剩下的参数,并且对这些参数进行处理并返回结果的一个技术
//1,
function curry(fn, args) {
// 获取函数需要的参数长度let length = fn.length;
args = args || [];
return function() {
let subArgs = args.slice(0);
// 拼接得到现有的所有参数for (let i = 0; i < arguments.length; i++) {
subArgs.push(arguments[i]);
}
// 判断参数的长度是否已经满足函数所需参数的长度if (subArgs.length >= length) {
// 如果满足,执行函数return fn.apply(this, subArgs);
} else {
// 如果不满足,递归返回科里化的函数,等待参数的传入return curry.call(this, fn, subArgs);
}
};
}
// 2,es6 实现function curry(fn, ...args) {
return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);
//题
function sum(...args1) {
var res1 = args1.reduce((pre, cur) => { return pre + cur }, 0)
return function (...args2) {
if (args2.length === 0) return res1
var res2 = args2.reduce((pre, cur) => { return pre + cur }, 0)
return sum(res1, res2)
}
}
console.log(sum(1, 2)(3)(4, 5)());
使用setTimeOut实现SetIterval
具体就是在setTimeOut里调用完函数之后(函数的this指向需要改变,函数的this 要跟最外层函数this一样)再调用setTimeOut,从而实现setInterval
function my_setInterval(func, time) {
var ctx = this
setTimeout(function () {
func.call(ctx)
my_setInterval(func, time)
}, time)
}
my_setInterval(function () {
console.log('hahahaha');
}, 500)
实现instanceof
instanceof 用来检测一个构造函数的prototype是否出现在一个对象的原型链上
function my_instanceof(obj, con) {
let left = obj.__proto__, right = con.prototype
while (true) {
if (left === null) return false
if (left === right) return true
left = left.__proto__
}
}
function add() {
return 0
}
console.log(my_instanceof(add, Function)) //true
console.log(my_instanceof(add, Array)) //false
实现new
new的过程:创建一个空对象obj,将obj的__ proto__指向构造函数的prototype,调用构造函数(this改变为obj)将结果给res,返回结果(如果res是对象,返回res,否则返回obj)
function my_new(func, ...args) {
var obj = {}
obj.__proto__ = func.prototype
var res = func.apply(obj, args)
return res instanceof Object ? res : obj
}
function Person(name) {
this.name = name
}
Person.prototype = {
age: 18
}
var me = my_new(Person, 'kangyi')
console.log(me.name); //kangyi
实现call,apply,bind
call 、bind 、apply三个都是用来改变函数的this对象的指向,
第一个参数都是this要指向的对象,也就是想指定的上下文。call和apply是立即调用函数,
而bind是返回一个函数,需要调用的时候再执行
//方法或函数.call(obj, 参数1,参数2,...)
Function.prototype.mycall = function (ctx, ...args) {
ctx = ctx || window
ctx.func = this //这里的this是调用call的那个函数,这一句就把这个函数挂载在了ctx上了
let res = ctx.func(...args)
delete ctx.func
return res
}
//方法或函数.apply(obj, [参数1,参数2,...])
Function.prototype.my_apply = function (ctx, args = []) {
if (!args instanceof Array) thow('只接受数组')
ctx = ctx || window
ctx.func = this
var res = ctx.func(...args)
delete ctx.func
return res
}
//方法或函数.bind(obj)
Function.prototype.mybind = function (ctx, ...args1) {
return (args2) => {
ctx.func = this
var res = ctx.func(...args1.concat(args1))
delete ctx.func
return res
}
}
// 测试
var obj = {
name: 'yiyiyi',
show: function () {
console.log(this.name);
console.log(arguments);
}
}
var person = { name: 'kang' }
// obj.show.mycall(person, 1, 2, 3)
// obj.show.my_apply(person, [1, 2, 3, 4])
obj.show.mybind(person, 1, 2, 3)(4, 5, 6)
深浅拷贝
// -------------------------------浅拷贝
// (1)for···in只循环第一层
// 只复制第一层的浅拷贝
function simpleCopy(obj1) {
var obj2 = Array.isArray(obj1) ? [] : {};
for (let i in obj1) {
obj2[i] = obj1[i];
}
return obj2;
}
var obj1 = {
a: 1,
b: 2,
c: {
d: 3
}
}
var obj2 = simpleCopy(obj1);
obj2.a = 3;
obj2.c.d = 4;
alert(obj1.a); // 1
alert(obj2.a); // 3
alert(obj1.c.d); // 4
alert(obj2.c.d); // 4
// // (2)Object.assign方法
// // Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
var obj = {
a: 1,
b: 2
}
var obj1 = Object.assign(obj);
obj1.a = 3;
console.log(obj.a) // 3
// (3)直接用=赋值
let a=[0,1,2,3,4],
b=a;
console.log(a===b);
a[0]=1;
console.log(a,b);
// --------------------------深拷贝
// 深拷贝方法一:对属性中所有引用类型的值,递归到是基本类型的值为止。
// typeof是无法判断array的,它会返回object
function deepClone(obj) {
let res = null;
const reference = [Date, RegExp, Map, WeakMap, Set, WeakSet];
if (reference.includes(obj?.constructor)) {
res = new obj.constructor(obj)
} else if (obj instanceof Array) {
res = []
obj.forEach((item, index) => {
res[index] = deepClone(item),
})
} else if (obj instanceof Object && obj !== null) {
res = {}
for (let key in obj) {
res[key] = deepClone(obj[key])
}
} else {
res = obj
}
return res
}
//深拷贝方法二: 通过js的内置对象JSON来进行数组对象的深拷贝
function deepClone(obj) {
var newObj = JSON.stringify(obj),
objClone = JSON.parse(newObj);
return objClone;
}
// 测试
var obj = {
a: [1, 2, 3],
b: 1233444,
c: {
ccc: 111,
cbb: [2, 3, 4],
ddd: {
jj: 'hjkl'
}
}
}
var cloneRes = deepClone(obj)
obj = {}
console.log(cloneRes);
console.log(Object.prototype.toString(cloneRes));
实现Object.creat()
function create(obj) {
function F() { }
F.prototype = obj
return nre F()
}
去除字符串首尾空格
function my_trim(str) {
var head = 0
var tail = str.length
for (var i = 0; i < str.length; i++) {
if (str[i] === ' ') head++
else break
}
for (var j = str.length - 1; j > 0; j--) {
if (str[j] === ' ') tail--
else break
}
var res = str.slice(head, tail)
return res
}
var str = ' dfrg sd aefe '
var res = my_trim(str)
console.log(res);
数组扁平化flaten
// 1,使用join函数和split函数,
// join 如果数组中的元素是数组,会将里面的数组也调用join(),将数组转化为字符串
// function flatten(arr) {
// return arr.join().split(',')
// }
//2, 递归 遍历数组,如果是数组,继续遍历
function flatten(arr) {
var res = []
for (var i = 0; i < arr.length; i++) {
if (arr[i] instanceof Array) {
res = res.concat(flatten(arr[i]))
} else {
res.push(arr[i])
}
}
return res
}
var arr = [0, 1, 2, [3, 4, 5], 6, [7, [8, 9]]]
var res = flatten(arr)
console.log(res);
数组去重
// 1,es6新增方法set
function unique(arr) {
return [...(new Set(arr))]
}
// 2,使用filter过滤和indexOf
function unique(arr) {
return arr.filter((item, index) => {
return arr.indexOf(item) === index
})
}
//3,使用indexOf
function unique(arr) {
var res = []
for (var i = 0; i < arr.length; i++) {
if (res.indexOf(arr[i]) === -1) {
res.push(arr[i])
}
}
return res
}
// 4,使用splice 如果后面有与i相等的数,就删除这个数
function unique(arr) {
for (var i = 0; i < arr.length - 1; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1)
j--
}
}
}
return arr
}
var arr = [1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 6, 7]
console.log(unique(arr));
// 5. reduce 方法
let myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd']
let myArrayWithNoDuplicates = myArray.reduce(function (previousValue, currentValue) {
if (previousValue.indexOf(currentValue) === -1) {
previousValue.push(currentValue)
}
return previousValue
}, [])
console.log(myArrayWithNoDuplicates)
实现Arr.reduce
Array.prototype.my_reduce = function (func, initValue) {
var hasInitValue = initValue === 'undefined'
var value = hasInitValue ? this[0] : initValue
for (let i = hasInitValue ? 1 : 0; i < this.length; i++) {
value = func(value, this[i], i, this)
}
return value
}
var arr = [1, 2, 3]
var res = arr.my_reduce((a, b) => {
return a + b
}, 0)
console.log(res);
setTimeout实现setInterval
function my_setInterval(func, time) {
var ctx = this
setTimeout(function () {
func.call(ctx)
my_setInterval(func, time)
}, time)
}
my_setInterval(function () {
console.log('hahahaha');
}, 500)
Once,函数只执行一或n次
function once(func) {
var count = 1; //只执行n次这里变成n即可
var res = null
return function () {
if (count === 1) { //只执行n次这里变成n即可
res = func.apply({}, arguments)
count--
return res
}
else {
return 'used'
}
}
}
var a = 1, b = 2;
function add(a, b) {
return a + b
}
var once_add = once(add)
console.log(once_add(a, b));
实现Promise
// Promise是一个构造函数,可以封装异步的任务,并且对结果进行处理
// promise支持链式调用,可以用来解决回调地狱的问题,在指定回调和错误处理方面更加灵活
function my_Promise(executor) {
this.promiseStatus = 'pending'
this.promiseResult = null
// 如果给一个promise对象指定多个回调函数,当状态改变时,这些回调都会生效
this.callbacks = []
// 因为执行resolve/reject的时候函数的this指向window而不是实例对象,所以需要保存这里的this
const self = this
function resolve(data) {
if (self.promiseStatus !== 'pending') return
self.promiseStatus = 'resolved'
self.promiseResult = data
self.callbacks.forEach(item => {
item.onResolved(data)
});
}
function reject(data) {
if (self.promiseStatus !== 'pending') return
self.promiseStatus = 'rejected'
self.promiseResult = data
self.callbacks.forEach(item => {
item.onReject(data)
});
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
my_Promise.prototype.then = function (onResolved, onReject) {
if (this.promiseStatus === 'resolved') onResolved(this.promiseResult)
if (this.promiseStatus === 'rejected') onReject(this.promiseResult)
if (this.promiseStatus === 'pending')
this.callbacks.push({ onResolved, onReject })
}
// 测试
// var p = new my_Promise((resolve, reject) => {
// setTimeout(() => {
// resolve('okkkkkk')
// }, 2000);
// })
// p.then(value => {
// console.log(value);
// }, reason => {
// console.warn(reason);
// })
// p.then(value => {
// alert(value);
// }, reason => {
// console.warn(reason);
// })
// Promise.all方法:可以将多个Promise实例包装成一个新的Promise实例。
// 同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
my_Promise.all = function (promises) {
return new my_Promise((resolve, reject) => {
var arr = []
promises.forEach((item, index) => {
item.then(value => {
arr[index] = value
if (arr.length === promises.length) {
resolve(arr)
}
}, reason => {
reject(reason)
})
})
})
}
// // 测试
// var p1 = new my_Promise((resolve, reject) => {
// resolve('okk')
// })
// var p2 = new my_Promise((resolve, reject) => {
// resolve('hello')
// })
// var p3 = new my_Promise((resolve, reject) => {
// reject('kang')
// })
// var pros = [p1, p2, p3]
// console.log(my_Promise.all(pros));
// Promise.race方法:Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])
// 里面哪个结果获得的快,就返回那个结果
my_Promise.race = function (promises) {
return new my_Promise((resolve, reject) => {
for (var i = 0; i < promises.length; i++) {
promises[i].then(value => {
resolve(value)
}, reason => {
reject(reason)
})
}
})
}
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('failed')
}, 5000)
})
// 测试
my_Promise.race([p1, p2]).then((result) => {
console.log(result)
}, reason => {
console.log(reason);
})
改写promise并添加超时处理
const promiseall = (all) => {
const promiseTimeout = new Promise((resolve, reject) => {
setTimeout(() => {
reject('请求超时')
},1000)
})
return new Promise((resolve, reject) => {
const res = new Array(all.length)
let count = 0
for(let i=0;i<all.length;i++){
Promise.race([all[i], promiseTimeout]).then((value) => {
console.log(value)
count ++
res[i] = value
if (count === all.length) {
return resolve(res)
}
},(e)=>{
count ++
res[i] = e
if (count === all.length) {
return resolve(res)
}
})
}
})
}
let p = new Promise(function(reslove,reject){
reslove('成功1')
})
let p1 = new Promise(function(reslove,reject){
setTimeout(() => {
reslove('成功2')
},2000)
})
promiseall([p,p1]).then((res)=> {
console.log(res)
}).catch((e) => {
console.log(e)
})
用Promise封装ajax
function ajax(url, method) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.open(method, url, true)
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status === 300) {
resolve(xhr.response)
} else {
reject(xhr.staus)
}
}
}
})
}
排序算法
// 快排 方法一
function quickSort(arr) {
if (arr.length < 1)
return arr
var index = Math.floor(arr.length / 2)
var center = arr.splice(index, 1)[0]
var left = [], right = []
arr.forEach((item) => {
if (item < center) {
left.push(item)
} else {
right.push(item)
}
})
return ([...quickSort(left), center, ...quickSort(right)])
}
var arr = [2, 4, 2, 1, 46, 2, 3, 56, 8, 5, 4, 0]
var res = quickSort(arr)
console.log(res);
//快排方法二
function quickSort(arr, left, right) {
if (left >= right) return;
var value = arr[left]
var i = left
var j = right
while (i < j) {
while (arr[j] >= value && i < j) j--
while (arr[i] <= value && i < j) i++
var temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
arr[left] = arr[i]
arr[i] = value
quickSort(arr, left, i - 1)
quickSort(arr, i + 1, right)
}
var arr = [0, 4, 5, 2, -1, 2]
quickSort(arr, 0, arr.length - 1)
console.log(arr);
// 最差是O(n^2) 最差情况是数组反着来的,这样每次选取的基准数都是最值,然后一趟下来只能把一个数归位
// 最好和平均都是O(nlogn)
// 为什么要让右边的先走?
// 这个要看选取基准数的位置,如果基准数在最左边,就要右边的哨兵先走,
// 这样当i=j的时候能够保证找到的数数小于基准数的,做交换就不会出错
// 选择排序
function selectSort(arr) {
for (let i = 0; i < arr.length; i++) {
var minIndex = i
for (let j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j
}
}
var t = arr[i]
arr[i] = arr[minIndex]
arr[minIndex] = t
}
return arr
}
var arr = [2, 4, 2, 1, 46, 2, 3, 56, 8, 5, 4, 0]
var res = selectSort(arr)
console.log(res);
// 冒泡排序
function bubbleSort(arr) {
for (var i = 0; i < arr.length - 1; i++) {
for (var j = 0; j < arr.length - i; j++) {
if (arr[j] > arr[j + 1]) {
var t = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = t
}
}
}
return arr
}
var arr = [2, 4, 2, 1, 46, 2, 3, 56, 8, 5, 4, 0]
var res = bubbleSort(arr)
console.log(res);
二分查找
var arr = [1, 2, 3, 5, 6, 7, 8, 9]
function binarySearch(arr, left, right, findVal) {
if (left > right) { return -1 }
var center = Math.floor((left + right) / 2)
var val = arr[center]
if (findVal > val) {
return binarySearch(arr, center + 1, right, findVal)
} else if (findVal < val) {
return binarySearch(arr, left, center - 1, findVal)
} else return center
}
console.log(binarySearch(arr, 0, arr.length, 7)); //5
console.log(binarySearch(arr, 0, arr.length, 4)); //-1
虚拟DOM到真实DOM转化
var obj = {
tagName: 'ul',
props: { 'class': 'list' },
children: [
{ tagName: 'li', children: ['item1'] },
{ tagName: 'li', children: ['item2'] }
]
};
function render(obj) {
if (typeof obj === 'string') {
return document.createTextNode(obj)
}
let el = document.createElement(obj.tagName)
for (var i in obj.props) {
el.setAttribute(i, obj.props[i])
}
for (let j of obj.children) {
el.appendChild(render(j))
}
return el
}
var el = render(obj)
var body = document.getElementsByTagName('body')[0].appendChild(el)
对象混合
function mix(obj, bemix) {
for (var p in bemix) {
if (!obj.hasOwnProperty(p) && bemix.hasOwnProperty(p)) {
obj[p] = bemix[p]
}
}
}
var obj1 = {
a: 111,
b: 222
}
var obj2 = {
c: 333,
d: 444
}
mix(obj1, obj2)
console.log(obj1);
下划线驼峰互转
toUpperCase() toLowerCase()
// 下划线到大写
function underlineToUpper(str) {
var arr = str.split('_')
var res = arr[0]
for (var i = 0; i < arr.length; i++) {
res = res + arr[i].slice(0, 1).toUpperCase() + arr[i].slice(1)
}
return res
}
console.log(underlineToUpper('my_apple_bnana_you')); //myMyAppleBnanaYou
// 大写到下划线
function upperToUnderline(str) {
var res = str.split('')
for (var i = 0; i < str.length; i++) {
if (str[i] >= 'A' && str[i] <= 'Z') {
var t = '_' + str[i].toLowerCase()
res.splice(i, 1, t)
}
}
return res.join('')
}
console.log(upperToUnderline('myAppleBnanaYou'));
sleep链式调用
实现需求如下:
实现一个可像以下code般链式调用的"变量"。其中 sleep指会停留,类似暂停;work直接打印;firstSleep也是暂停,但它相当特殊,首先只能调用一次,其次,无论在链路上何处调用,都必须第一个执行。因此,以下code执行顺序(结果)为:
man.work().sleep(1000).firstSleep(5000).work().sleep(1000);
// 执行结果
// 暂停 5 秒 firstSleep(5000)
// 暂停 1 秒 sleep(1000)
// 打印 work()
// 暂停 1 秒 sleep(1000)
2.1 思路
链式调用: 每次调用方法后都返回的是这个this本身,那么就实现了链式调用。
顺序执行:既然要顺序执行,在执行函数之前利用队列先收集所有的函数,然后依次执行
firstSleep首先执行:既然能够收集所有的函数,把firstSleep直接放到队列头最先执行就可以了
class Man {
constructor() {
this.queue = []; // 记录调用的函数,全部是同步收集然后依次执行
this.index = 0; // 执行函数的索引
this.firstSleepWatch = false; // 检查 firstSleep 调用次数,最多调用一次
this.init();
}
// 因为执行需要在函数收集之前,因此需要利用事件机制,先收集后执行
init() {
setTimeout(() => this.run() , 0);
}
run() {
const fn = this.queue[this.index++];
// 最后一个 fn 为 undefined, 因此需要做处理
fn && fn();
}
firstSleep(delay) {
if(this.firstSleepWatch) {
throw Error("Already declared firstSleep!!");
}
this.queue.unshift(() => {
setTimeout(() => {
console.log('firstSleep end!!');
this.run();
}, delay);
});
this.firstSleepWatch = true;
return this;
}
sleep(delay) {
this.queue.push(() => {
setTimeout(() => {
console.log('sleep end!!');
this.run();
}, delay);
});
return this;
}
work() {
this.queue.push(() => {
console.log('work runing!!');
this.run();
});
return this;
}
};
const man = new Man();
// 子类在链式调用之前会先收集所有的调用函数,按照顺序放入队列中,收集完成后顺序执行
man.work().sleep(1000).firstSleep(5000).work().sleep(1000);
// 执行结果
// 5 秒后打印 firstSleep end!!
// 直接打印 work runing!!
// 1 秒后打印 sleep end!!
// 直接打印 work runing!!
// 1 秒后打印 sleep end!!
js 链式调用sleep、定时器清除、链式取消(nodejs版本)
class person {
/** 用户类 - 姓名 */
name = ''
/** 执行函数 栈 */
funs = []
/** 定时器 数据对象 */
timeOutVal = { timeobj: {}, time: 0 }
/** 构造函数 */
constructor(name) {
/** 姓名负值 */
this.name = name
/** 加入执行函数 */
this.task({ fun: () => console.info(`Hi! ${this.name}`) })
/** 执行函数栈 */
this.next()
return this
}
/** 函数栈执行器 */
next() {
const fn = this.funs.shift()
fn && fn()
}
/** 延迟定时器 */
sleep(time) {
this.task({ fun: () => console.info(`sleep ${time}`) })
this.task({ time })
return this
}
/** 清除定时器 */
clearSleep() {
this.task({ fun: () => {
clearTimeout(this.timeOutVal.timeobj)
console.info(`clear sleep ${this.timeOutVal.time}`)
this.next()
}})
return this
}
/** 追加栈 */
task({ fun, time }) {
if (time) this.time = time
this.funs.push(() => {
const timeobj = setTimeout(() => {
fun && fun()
this.next()
}, time || 0)
if (time) this.timeOutVal = { timeobj, time }
})
}
/** 吃的的函数 */
eat(food) {
this.task({ fun: () => console.info(`${this.name} eat ${food}`) })
return this
}
/** 玩的的函数 */
paly(what) {
this.task({ fun: () => console.info(`${this.name} paly ${what}`) })
return this
}
/** 取消上一个步骤 */
cannel() {
this.funs.splice(this.funs.length - 1, 1)
return this
}
}
new person('Tomr').eat('even').paly('bad').cannel().eat('brid').paly('good').cannel().sleep(4000).cannel().eat('678687').sleep(20000).paly('444').eat('111').clearSleep().sleep(3000).eat('222')
js的链式调用和流程控制(sleep)
链式调用的原理:一个对象中有多个方法,处于前面的每个方法都返回this,这样后面的方法就可以继续this环境下执行,也就是在当前执行环境下执行,实现链式调用效果
实现一个类(或 构造函数),其实例可以链式调用,它有一个 sleep 方法,可以 sleep 一段时间后再后续调用 const boy = new PlayBoy(‘Tom’) boy.sayHi().sleep(1000).play(‘王者’).sleep(2000).play(‘跳一跳’)
实现思想:创建一个任务队列,在每个方法中都往任务队列里追加一个函数,利用队列的先进先出的思想 来控制函数的执行顺序
class PlayBoy {
constructor(name) {
this.name = name
this.queue = [] //创建一个任务队列(利用队列的先进先出性质来模拟链式调用函数的执行顺序)
setTimeout(()=>{ // 进入异步任务队列 也是开启 自定义任务队列 queue 的入口
this.next() // next是类PlayBoy 原型上的方法,用来从queue 任务队列中取出函数执行
},0)
return this
}
}
PlayBoy.prototype.sayHi = function () {
const fn = () => {
console.log('hi')
this.next()
}
this.queue.push(fn)
return this
}
PlayBoy.prototype.sleep = function (timer) {
const fn = () => {
setTimeout(() => {
this.next()
}, timer)
}
this.queue.push(fn)
return this
}
PlayBoy.prototype.play = function () {
const fn = () => {
console.log('play')
this.next()
}
this.queue.push(fn)
return this
}
PlayBoy.prototype.next = function () {
const fn = this.queue.shift() // 从任务队列中取出函数 函数存在的话即调用
fn && fn()
}
new PlayBoy().sayHi().sleep(5000).play()
lazy man
class LazyMan{
constructor(data) {
this.queue = [{
id: 0,
data
}]
setTimeout(() => {
this.init()
}, 0)
}
init() {
while(this.queue.length) {
const item = this.queue.shift();
if(item.id === 0) {
console.log(item.data);
} else {
setTimeout(() => {
this.init();
}, item.delay)
break;
}
}
}
eat(data) {
this.queue.push({
id: 0,
data
})
return this;
}
sleep(delay) {
this.queue.push({
id: 1,
delay
})
return this;
}
}
function lazyMan(data) {
return new LazyMan(data)
}
lazyMan('name').eat('apple').sleep(1000).eat('orange')
lazyMan('apple').sleep(1000).eat('aaaa').sleep(1000).eat('bbbbb').eat('cccc')
发布订阅模式
首先:DOM0和DOM2的区别
- 语法上的不同
event.οnclick=function(){}
event.addEventListener(‘click’,function(){}) - 底层实现不同
DOM0:给元素的某一个事件绑定一个函数(有效函数只是一个)
DOM2:给元素的某个事件建立一个事件池,当这个事件发生的时候,会依次执行事件池中的函数(可以有多个有效函数)=>订阅发布就是模拟事件池机制 - DOM2可以给一些特殊的事件添加处理函数,例如 DOMContentLoaded(当DOM树加载完成时触发)
事件池机制:
1,addEventListener(‘click’,function(){})想事件池中追加方法(这里有去重处理)
2,当事件触发的时候,按照添加的顺序依次执行事件池里的事件
class sub {
constructor() {
this.pond = []
}
add(func) {
var flag = this.pond.some((item) => {
return item === func
})
if (!flag) this.pond.push(func)
}
//执行事件池中的所有事件
emit(...args) {
var pond = this.pond
for (var i = 0; i < pond.length; i++) {
if (pond[i] instanceof Function) {
pond[i].apply(this, args)
} else {
//如果不是函数,就删除此项
this.pond.splice(i, 1)
i--
}
}
}
remove(func) {
this.pond.forEach((item, index) => {
if (item === func) {
this.pond[index] = null //防止数组塌陷
}
})
}
}
function func1() {
console.log(1);
}
function func2() {
pond.remove(func1)
console.log(2);
} function func3() {
console.log(3);
} function func4() {
console.log(4);
}
var btn = document.getElementsByTagName('button')[0]
var pond = new sub()
pond.add(func1)
pond.add(func2)
pond.add(func3)
pond.add(func4)
btn.onclick = function (e) {
pond.emit(e)
}
观察者模式
请实现一个观察者模式,拥有四个方法on, of, once和trigger
const Event = {
on() {} //绑定
off() {} //解绑
once() {} //绑定一次
trigger() {} //触发事件
}
function echo(msg) { console.log(msg);
var. event= new Event();
event.on(‘test’, echo);
event.trigger(‘test’, 123); //输出123
event.off(‘test’, echo); //删除某个事件回调
event.off(‘test’); //删除所有事件回调
event.once(‘test’, echo);//只触发一次
class Event {
constructor() {
this.events = {};
}
on(event,callback){
if(!this.events.hasOwnProperty(event)){
this.events[event] = []
}
if(typeof callback == 'function'){
this.events[event].push(callback)
}else{
throw new Error('缺少回调函数');
}
return this
}
trigger(event,...args){
if(this.events.hasOwnProperty(event)){
this.events[event].forEach((item,index,arr)=>{
item.apply(this,args)
})
}else {
throw new Error(`"${event}"事件未注册`);
}
return this
}
off(event,callback){
if(!this.events.hasOwnProperty(event)){
throw new Error(`"${event}"事件未注册`);
}else if(typeof callback != 'function'){
<!--如果callback不传,则表示移除事件中的所有回调函数-->
this.events[event] = []
}else{
this.events[event].forEach((item,index,arr)=>{
if(item == callback){
arr.splice(index,1)
}
})
}
return this
}
once(event,callback){
var onceFunc = (...args) => {
callback.apply(this,args)
this.off(event,onceFunc)
}
this.on(event,onceFunc)
return this
}
}
function echo(msg) { console.log(msg)};
function echo2(msg) { console.log(msg + 'sss')};
function echo3(msg) { console.log(msg + 'ddd')};
var event = new Event();
event.on('testOn',echo)
event.on('testOn',echo2)
event.on('testOn',echo3)
event.trigger('testOn','11111')
event.once('test', echo);
event.trigger('test','222')
event.trigger('test','333')
event.trigger('testOn','44444')
event.off('testOn',echo)
event.trigger('testOn','offff')
event.off('testOn')
event.trigger('testOn','offffall')
单例模式
只允许一个类有一个实例,一般先判断实例是否存在,如果有实例就返回该实例,如果没有就创建一个实例再返回
例:Person用来构造对象,SinglePerson 用来判断Person是否已经构造了函数,来确定是返回原有的实例还是新构造一个实例再返回,这里使用Symbol来实现私有变量name
const name = Symbol()
function Person(name) {
this[name] = name
this.getName = function () {
return this[name]
}
}
var SinglePerson = (function () {
var instance = null
return function (name) {
if (!instance) {
instance = new Person(name)
}
return instance
}
})()
var p1 = new SinglePerson('kang1')
var p2 = new SinglePerson('kang2')
console.log(p1.name); //undefined
console.log(p1[name]); //undefined
console.log(p1.getName());//kang
console.log(p1 === p2); //true
实现一个 Storage(单例模式)
// 实现Storage,使得该对象为单例,基于 localStorage 进行封装。
// 实现方法 setItem(key,value) 和 getItem(key)。
// 1. 静态方法
// 定义Storage
class Storage {
static getInstance() {
// 判断是否已经new过1个实例
if (!Storage.instance) {
// 若这个唯一的实例不存在,那么先创建它
Storage.instance = new Storage()
}
// 如果这个唯一的实例已经存在,则直接返回
return Storage.instance
}
getItem (key) {
return localStorage.getItem(key)
}
setItem (key, value) {
return localStorage.setItem(key, value)
}
}
const storage1 = Storage.getInstance()
const storage2 = Storage.getInstance()
storage1.setItem('name', '李雷')// 李雷
storage1.getItem('name')// 也是李雷
storage2.getItem('name')
storage1 === storage2 // 返回true
// 闭包
// 先实现一个基础的StorageBase类,把getItem和setItem方法放在它的原型链上
function StorageBase () {}
StorageBase.prototype.getItem = key => {
return localStorage.getItem(key)
}
StorageBase.prototype.setItem = (key, value) => {
return localStorage.setItem(key, value)
}
// 以闭包的形式创建一个引用自由变量的构造函数
const Storage = (() => {
let instance = null
return function(){
// 判断自由变量是否为null
if(!instance) {
// 如果为null则new出唯一实例
instance = new StorageBase()
}
return instance
}
})()
// 这里其实不用 new Storage 的形式调用,直接 Storage() 也会有一样的效果
const storage1 = new Storage()
const storage2 = new Storage()
storage1.setItem('name', '李雷')
// 李雷
storage1.getItem('name')
// 也是李雷
storage2.getItem('name')
storage1 === storage2 // 返回true
实现一个全局的模态框(单例模式)
<!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>
<style>
#modal{
height: 200px;
width: 200px;
line-height: 200px;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border: 1px solid black;
text-align: center;
}
</style>
</head>
<body>
<button id="open">打开弹框</button>
<button id="close">关闭弹框</button>
<script>
const Modal = (function(){
let modal = null;
return function() {
if(!modal) {
modal = document.createElement('div');
modal.innerHTML = '我是一个全局唯一的modal';
modal.id = 'modal';
modal.style.display = 'none';
document.body.appendChild(modal);
}
return modal;
}
})()
document.getElementById('open').addEventListener('click', function(){
const modal = new Modal();
modal.style.display = 'block';
})
document.getElementById('close').addEventListener('click', function(){
const modal = new Modal();
modal.style.display = 'none'
})
</script>
</body>
</html>
parseURL:parseUrl(“https://a.b.come?aaa=123&bbb=hhah&ccc=456”)
function parseUrl(url) {
// 代码实现这个函数
let [pre, cur] = url.split('?');
let arr = cur.split('&');
let map = new Map();
for (let i = 0; i < arr.length; i++) {
let [key, value] = arr[i].split('=');
if (!map.has(key)) {
map.set(key, value);
} else {
map.set(key, [map.get(key)].concat(value));
}
}
return Object.fromEntries(map);
}
查找json中的children路径
function findIndexArray(data, id, indexArray) {
let arr = Array.from(indexArray);
for (let i = 0, len = data.length; i < len; i++) {
arr.push(data[i].id);
if (data[i].id === id) {
return arr;
}
let children = data[i].children;
if (children && children.length) {
let result = findIndexArray(children, id, arr);
if (result) return result;
}
arr.pop();
}
return false;
}
console.log(findIndexArray(obj, 5, [])); // [1, 4, 5]
全排列
function full(str) {
var res = []
if (str.length > 1) {
for (var i = 0; i < str.length; i++) {
var cur = str[i]
var other = str.slice(0, i) + str.slice(i + 1, str.length)
var arr = full(other)
for (var j = 0; j < arr.length; j++) {
var t = cur + arr[j]
res.push(t)
}
}
} else {
res.push(str)
}
return res
}
console.log(full('abc')); //['abc', 'acb', 'bac', 'bca', 'cab', 'cba'
六种继承
2,使用Symbol来定义一个属性,这样的话外面就拿不带这个唯一值,也就访问不到了
const name = Symbol()
class Person {
constructor(str) {
this[name] = str
}
getName = function () {
return this[name]
}
}
var p = new Person('kang')
console.log(p.name);
console.log(p.getName());
打乱数组
const fn = (arr) => {
return arr.sort(() => {
return Math.random() - 0.5
})
}
console.log(fn([1, 2, 3, 4, 5]));
限制异步的并发次数
class Scheduler {
constructor(max) {
this.max = max;
this.count = 0;
this.queue = [];
}
async add(fn) {
if (this.count >= this.max) {
await new Promise(resolve => {
this.queue.push(resolve)
})
}
this.count++;
const res = await fn()
this.count--;
this.queue.length && this.queue.shift()();
return res;
}
}
// 延迟函数
const sleep = time => new Promise(resolve => setTimeout(resolve, time));
// 同时进行的任务最多2个
const scheduler = new Scheduler(2);
// 添加异步任务
// time: 任务执行的时间
// val: 参数
const addTask = (time, val) => {
scheduler.add(() => {
return sleep(time).then(() => console.log(val));
});
};
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
实现能够控制并发数的fetch方法, 要求每次最多发送max个, 所有请求完成后, 返回结果
// 实现能够控制并发数的fetch方法, 要求每次最多发送max个, 所有请求完成后, 返回结果
function mulitFetch(urls, max) {
if (!Array.isArray(urls) || urls.length === 0) {
return null;
}
return new Promise((resolve, reject) => {
const res = [];
let temp = Math.floor(urls.length / max);
let current = Promise.all(urls.slice(0, max));
let count = 1;
while (temp !== 0) {
current = current
.then((data) => {
res.push(...data);
return Promise.all(urls.slice(count * max, max + count * max));
})
.catch((err) => {
temp = 0;
reject(err);
});
temp--;
}
current.then(() => {
resolve(res);
});
});
}
mulitFetch(
[
"https://www.example.com/a.json",
"https://www.example.com/b.json",
"https://www.example.com/c.json",
"https://www.example.com/d.json",
],
2
).then((responses) => {
console.log(responses); // [a, b, c, d]
});
红绿灯交替闪烁
function red() {
console.log("red");
}
function green() {
console.log("green");
}
function yellow() {
console.log("yellow");
}
const light = (color, time) => {
return new Promise((resolve) => {
setTimeout(() => {
color()
resolve()
}, time)
})
}
const step = () => {
Promise.resolve().then(() => {
return light(red, 3000)
}).then(() => {
return light(green, 2000)
}).then(() => {
return light(yellow, 1000)
}).then(() => {
return step()
})
}
step()
一秒打印一个数
arr = [1, 2, 3]
//方法一
for (let i = 0; i < arr.length; i++) {
setTimeout(function () {
console.log(arr[i]);
}, 1000 * i)
}
//方法二
arr.reduce((pre, cur) => {
return pre.then(() => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(cur);
resolve()
}, 1000)
})
})
}, Promise.resolve())
异步超时判断,用race
const func1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok')
}, 3000)
})
const testTime = new Promise((resolve, reject) => {
setTimeout(() => {
reject('timeout')
}, 2000)
})
Promise.race([func1, testTime]).then(res => {
console.log(res, 'kkkk');
}).catch(err => {
console.log(err);
})
异步加载一个图片
const loadImg = (url) => {
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = () => {
resolve(img)
}
img.onerror = () => {
reject('error')
}
img.src = url
})
}
const pic = document.getElementsByTagName('img')[0]
loadImg(url).then(res => {
pic.src = res.src
})
mergePromise 实现mergePromise函数,把传进去的数组按顺序先后执行,//并且把返回的数据先后放到数组
const time = (timer) => {
return new Promise(resolve => {
setTimeout(() => {
resolve()
}, timer)
})
}
const ajax1 = () => time(2000).then(() => {
console.log(1);
return 1
})
const ajax2 = () => time(1000).then(() => {
console.log(2);
return 2
})
const ajax3 = () => time(1000).then(() => {
console.log(3);
return 3
})
const arr = [ajax1, ajax2, ajax3]
const mergePromise = (arr) => {
var resArr = []
return arr.reduce((pre, cur) => {
return pre.then(() => {
return new Promise((resolve) => {
cur().then((res) => {
resArr.push(res)
resolve(resArr)
})
})
})
}, Promise.resolve())
}
mergePromise(arr).then(data => {
console.log(data);
})
在这里插入代码片
获取浏览器最小字体
const getMinFontSize = (el) => {
let fontSize = 0
for (let i = 20; i >= 0; i--) {
el.style.fontSize = i + 'px';
let elFontSize = getComputedStyle(div1).fontSize
console.log(elFontSize);
if (fontSize === elFontSize) {
return fontSize
} else {
fontSize = elFontSize
}
}
}
console.log(getMinFontSize(div1));
如何实现一个私有变量,用getName方法可以访问,不能直接访问
1,使用闭包
function Person(str) {
var name = str
this.getName = function () {
return name
}
}
var p = new Person('kang')
console.log(p.name); //undefined
console.log(p.getName()); //kang
在obj上找到protoName的方法
function findProp(obj, prop) {
while (1) {
if (obj === null) {
return false
}
if (obj.hasOwnProperty(prop)) {
return true
}
obj = obj.__proto__
}
}
function Person() {
name: 'kang'
}
Person.prototype.age = 18
var per = new Person
console.log(findProp(per, 'age')); //true
JSONP
<!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>
</body>
<script>
function demo(data) {
console.log(data);
}
var script = document.createElement('script')
var url = "https://xxxx"
script.setAttribute('src', url + '?callback=demo')
</script>
</html>
js怎么控制一次加载一张图片,加载完后再加载下一张
// 只要能监控到图片是否加载完成 就能实现了
// 要把图片当成是图片对象才行;
//方法一
var obj=new Image(); // <img> 标签每出现一次,一个 Image 对象就会被创建。
obj.src="http://www.phpernote.com/uploadfiles/editor/201107240502201179.jpg";
// onload当图像装载完毕时调用的事件句柄。
obj.onload=function(){ // 这个就是加载完成,当加载完成之后可以执行一个函数比如
alert('图片的宽度为:bai'+obj.width+';图片的高度为:'+obj.height);
document.getElementById("mypic").innnerHTML="<img src='"+this.src+"' />";
}
//方法二
let obj = new Image()
obj.src = '..'
obj.onreadystatechange = function () {
if(thids.readyState === 'complate') {
document.getElementById('mypic').innerHTML = '<img src=" '+this.src +'">'
}
}
//不确定
//const imgArrs = ['imageurl1', 'imageurl2', 'imageurl3']
// const loadImg = () => {
// if (!imgArrs.length) return;
// const img = new Image()
// img.src = imgArrs[0]
// img.onload = () => {
// setTimeout(() => {
// document.body.appendChild(this)
// imgArrs.shift()
// loadImg()
// }, 2000)
// }
// }
// loadImg()
js->tree
function jsonToTree(data) {
let res = [];
if(!Array.isArray(data)) {
return res;
}
let map = {};
data.forEach(item => {
map[item.id] = item;
});
data.forEach(item => {
let parent = map[item.pid];
if(parent) {
(parent.children || (parent.children = [])).push(item);
} else {
res.push(item)
}
})
return res;
}
console.log(jsonToTree(source));
Once
function once(func) {
var tag = true;
return function () {
if(tag) {
func.apply(null, arguments);
tag = false;
}
return undefined;
}
}
console.log(once(f()));
解析url
function parseURL(str) {
let url = decodeURI(str);
const cur = url.indexOf("?");
const index = url.indexOf("#");
if(url !== -1) {
const res = {};
const params = url.substring(cur + 1, index).split('&');
for(let item of params) {
const [key, value] = item.split('=');
if(key in res) {
if(!Array.isArray(res[key])) {
res[key] = [res[key]];
}
res[key].push(value);
} else {
value ? (res[key] = value) : (res[key] = true);
}
}
return res;
}
}
const url = 'www.baidu.com?a=1&b=3#hash'
console.log(parseURL(url));
千位分隔符,数字转字符串,每3位用逗号分隔
// 数字转字符串,每3位用逗号分隔
function formatMoney(money) {
if (!money) {
return
}
const [left, right] = String(money).split('.')
let res = ''
for (let i = left.length - 1, j = 0; i >= 0; i--, j++) {
if (j % 3 === 0 && j !== 0) {
res = ',' + res
}
res = left[i] + res
}
res = right ? res + '.' + right : res
return res
}
Iterator
function createIterator(items) {
var i = 0;
return {
next: function () {
var done = i >= items.length;
var value = !done ? items[i++] : undefined;
return {
value: value,
done: done,
};
},
};
}
var iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
// 后续的所有调用返回的结果都一样
console.log(iterator.next()); // "{ value: undefined, done: true }"
所有的key换成下划线UserList -> user_list
let obj = {
UserList: [
{
UserId: '1244',
Nickname: 'aaaa',
Friends: {
UserId: '2222',
Nickname: 'bbbb'
}
},
{
UserId: '1244',
Nickname: 'aaaa'
}
],
Total: 1111
}
// 将这种数据格式,所有的key换成下划线UserList -> user_list
// 1. 不用正则
function splitCamel(str){
var res = [];
for(var i in str){
var asc = str.charAt(i).charCodeAt();
if(asc>=65 && asc<=90){
res.push(' ');
res.push(String.fromCharCode(asc+32));
}
else{
res.push(str.charAt(i));
}
}
return res.join('').trim().replace(" ", "_");
}
function parseName(obj) {
if(!obj || typeof obj !== 'object') {
return obj;
}
const parse_name = target => {
if(!target || typeof target !== 'object') {
return target;
}
const result = Array.isArray(target) ? [] : {};
for(const key in target) {
const newKey = splitCamel(key);
result[newKey] = parse_name(target[key]);
}
return result;
}
return parse_name(obj);
}
console.log(parseName(obj));
// 2. 正则表达式
function splitCamel(str) {
return str.replace(/([A-Z])/g, function(s) {
return ' ' + s.toLowerCase();
}).trim().split(' ').join('_');
}
function parseName(obj) {
if(!obj || typeof obj !== 'object') {
return obj;
}
const parse_name = target => {
if(!target || typeof target !== 'object') {
return target;
}
const result = Array.isArray(target) ? [] : {};
for(const key in target) {
const newKey = splitCamel(key);
result[newKey] = parse_name(target[key]);
}
return result;
}
return parse_name(obj);
}
console.log(parseName(obj));
实现一个对象,该对象有五个方法可以判断不同类型,要鉴别出function、null、NaN、数组、对象
const is = {
Array: (arr) => {
return Array.isArray(arr)
},
Function: (func) => {
return func instanceof Function
},
Object: (obj) => {
return Object.prototype.toString.call(obj) === ['object Object']
},
Null: (a) => {
return a === null
},
NaN: (a) => {
return Number.isNaN(a)
}
}
function func () {}
const isFunc = is.Function(func)
console.log(isFunc) // true
实现format函数来摊平字符串: 输入:3(x)2(yz)1(x) 输出:xxxyzyzx
function format(str) {
let result = ''
let strList = str.match(/\(([^)]*)\)/g);
strList = strList.map(item => {
return item.slice(1, item.length-1)
})
let nums = str.match(/\d+(\d+)?/g)
strList.forEach((item, index) => {
result += item.repeat(parseInt(nums[index]))
})
console.log(result);
}
输入给定数字的下一个比他大的数字 比如输入 1234 输出 1243
// 如何变大:从低位挑一个大一点的数,交换前面一个小一点的数。
// 变大的幅度要尽量小。
function nextPermutation(nums) {
let i = nums.length - 2;
// 向左遍历,i从倒数第二开始是为了nums[i+1]要存在
while (i >= 0 && nums[i] >= nums[i + 1]) { // 寻找第一个小于右邻居的数
i--;
}
if (i >= 0) {
// 这个数在数组中存在,从它身后挑一个数,和它换
let j = nums.length - 1; // 从最后一项,向左遍历
while (j >= 0 && nums[j] <= nums[i]) { // 寻找第一个大于 nums[i] 的数
j--;
}
[nums[i], nums[j]] = [nums[j], nums[i]]; // 两数交换,实现变大
}
// 如果 i = -1,说明是递减排列,如 3 2 1,没有下一排列,直接翻转为最小排列:1 2 3
let l = i + 1;
let r = nums.length - 1;
while (l < r) {
// i 右边的数进行翻转,使得变大的幅度小一些
[nums[l], nums[r]] = [nums[r], nums[l]];
l++;
r--;
}
}
实现数组a = [1,[2,[3,[4,null]]]]转换[4,[3,[2,[1,null]]]]
function main(a) {
const reverse = (arr,tmp) =>{
if(arr[1] == null) return [arr[0],tmp]
tmp = tmp ? [arr[0],tmp]:[arr[0],null]
console.log(arr[1],tmp)
return reverse(arr[1],tmp)
}
return reverse(a);
}
模版字符串替换
function a(str, obj) {
var str1 = str;
for (var key in obj) {
var re = new RegExp("{{" + key + "}}", "g");
console.log(re)
str1 = str1.replace(re, obj[key]);
}
return str1;
}
let str = "{{name}}很厉name害{{name}},才{{age}}岁";
let obj = { name: "jawil", age: "15" };
a(str, obj);
三角形
https://blog.csdn.net/xingxinglinxi/article/details/108647293
实现一个函数,把一个字符串数组([‘zm’, ‘za’, ‘b’, ‘lm’, ‘ln’, ‘k’]) 格式化成一个对象 { ‘b’: [‘b’], ‘k’: [‘k’], ‘l’: [‘lm’, ‘ln’], ‘z’: [‘za’, ‘zm’] }
let arr = ['zm', 'za', 'b', 'lm', 'ln', 'k', 'kkk'];
function parse(arr) {
let obj = {};
arr.forEach(item => {
let key = item.slice(0, 1);
obj[key] = [];
});
for (let i in arr) {
for (let j in obj) {
if (arr[i].slice(0, 1) === j) {
obj[j].push(arr[i]);
}
}
}
return obj;
}
console.log(parse(arr));
vue中对数组方法的重写
let oldArrayPrototype = Array.prototype; // 原始的数组方法
let arrayMethods = Object.create(oldArrayPrototype); // 数组方法的重写
// 其中arrayMethos_proto_ = Array.prototype
let methods = [
'push',
'shift',
'unshift',
'pop',
'reverse',
'sort',
'splice'
]
methods.forEach(method => {
// 用户调用了以上几个方法 会用自己重写的,否者使用原来的数组方法
arrayMethods[method] = function (...args) {
// 先调用自己写的方法,之后调用内部原有的方法
oldArrayPrototype[method].call(this, ...args);
// 假如args传入的实参为 对象时候 则需要特殊处理
// let inserted;
// let ob = this.__ob__; // 根据当前数组实例获取到observer实例对象
switch (method) {
case "push":
console.log(111111111);
case "unshift":
console.log(args);; // 就是新增的内容
case "splice":
default:
break;
}
// 不用这几步:如果有新增的内容 要进行继续劫持, 我需要观测的数组里的每一项,而不是数组
// if (inserted) ob.observeArray(inserted);
// 如果数组中的数据是对象类型,需要监控对象的变化
}
})
function init(arr, obj) {
arr.__proto__ = obj
}
let arr = [1, 2, 3]
init(arr, arrayMethods)
arr.unshift(0)
console.log(arr);