这也是一个经典题目了,首先要知道什么是防抖,什么是节流。
防抖: 在一段时间内,事件只会最后触发一次。
节流: 事件,按照一段时间的间隔来进行触发。
实在不懂的话,可以去这个大佬的Demo地址玩玩防抖节流DEMO
// 防抖
function debounce(fn) {
let timeout = null;
return function () {
// 如果事件再次触发就清除定时器,重新计时
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, 500);
};
}
// 节流
function throttle(fn) {
let flag = null; // 通过闭包保存一个标记
return function () {
if (flag) return; // 当定时器没有执行的时候标记永远是null
flag = setTimeout(() => {
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为null(关键)
// 表示可以执行下一次循环了。
flag = null;
}, 500);
};
}
这道题主要还是考查对 防抖 节流 的理解吧,千万别记反了!
要求写出 区号+8位数字,或者区号+特殊号码: 10010/110,中间用短横线隔开的正则验证。 区号就是三位数字开头。
例如 010-12345678
let reg = /^\d{3}-(\d{8}|10010|110)/g
这个比较简单,熟悉正则的基本用法就可以做出来了。
// 通过 window.open 和 location.href 方法其实就可以实现。 // 分别对应了a标签的 blank 和 self 属性
这个题的意思就是,不能循环的API(如 for filter之类的)。
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 方法一 : splice 操作数组 会改变原数组
arr.splice(2, 1)
// 方法二 : slice 截取选中元素 返回新数组 不改变原数组
arr.slice(0, 2).concat(arr.slice(3,))
// 方法三 delete数组中的元素 再把这个元素给剔除掉
delete arr[2]
arr.join("").replace("empty", "").split("")
深拷贝和浅拷贝的区别就在于
所以关键点就在于对复杂数据类型的处理,这里我写了两种写法,第二中比第一种有部分性能提升
const isObj = (val) => typeof val === "object" && val !== null;
// 写法1
function deepClone(obj) {
// 通过 instanceof 去判断你要拷贝的变量它是否是数组(如果不是数组则对象)。
// 1. 准备你想返回的变量(新地址)。
const newObj = obj instanceof Array ? [] : {}; // 核心代码。
// 2. 做拷贝;简单数据类型只需要赋值,如果遇到复杂数据类型就再次进入进行深拷贝,直到所找到的数据为简单数据类型为止。
for (const key in obj) {
const item = obj[key];
newObj[key] = isObj(item) ? deepClone(item) : item;
}
// 3. 返回拷贝的变量。
return newObj;
}
// 写法2 利用es6新特性 WeakMap弱引用 性能更好 并且支持 Symbol
function deepClone2(obj, wMap = new WeakMap()) {
if (isObj(obj)) {
// 判断是对象还是数组
let target = Array.isArray(obj) ? [] : {};
// 如果存在这个就直接返回
if (wMap.has(obj)) {
return wMap.get(obj);
}
wMap.set(obj, target);
// 遍历对象
Reflect.ownKeys(obj).forEach((item) => {
// 拿到数据后判断是复杂数据还是简单数据 如果是复杂数据类型就继续递归调用
target[item] = isObj(obj[item]) ? deepClone2(obj[item], wMap) : obj[item];
});
return target;
} else {
return obj;
}
}
这道题主要是的方案就是,递归加数据类型的判断。
如是复杂数据类型,就递归的再次调用你这个拷贝方法 直到是简单数据类型后可以进行直接赋值
call bind apply的作用都是可以进行修改this指向
// call
Function.prototype.MyCall = function (context) {
if (typeof this !== "function") {
throw new Error('type error')
}
if (context === null || context === undefined) {
// 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
context = window
} else {
// 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
context = Object(context)
}
// 使用Symbol 来确定唯一
const fnSym = Symbol()
//模拟对象的this指向
context[fnSym] = this
// 获取参数
const args = [...arguments].slice(1)
//绑定参数 并执行函数
const result = context[fnSym](...args)
//清除定义的this
delete context[fnSym]
// 返回结果
return result
}
// call 如果能明白的话 apply其实就是改一下参数的问题
// apply
Function.prototype.MyApply = function (context) {
if (typeof this !== "function") {
throw new Error('type error')
}
if (context === null || context === undefined) {
// 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
context = window
} else {
// 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
context = Object(context)
}
// 使用Symbol 来确定唯一
const fnSym = Symbol()
//模拟对象的this指向
context[fnSym] = this
// 获取参数
const args = [...arguments][1]
//绑定参数 并执行函数 由于apply 传入的是一个数组 所以需要解构
const result = arguments.length > 1 ? context[fnSym](...args) : context[fnSym]()
//清除定义的this
delete context[fnSym]
// 返回结果 //清除定义的this
return result
}
// bind
Function.prototype.MyBind = function (context) {
if (typeof this !== "function") {
throw new Error('type error')
}
if (context === null || context === undefined) {
// 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
context = window
} else {
// 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
context = Object(context)
}
//模拟对象的this指向
const self = this
// 获取参数
const args = [...arguments].slice(1)
// 最后返回一个函数 并绑定 this 要考虑到使用new去调用,并且bind是可以传参的
return function Fn(...newFnArgs) {
if (this instanceof Fn) {
return new self(...args, ...newFnArgs)
}
return self.apply(context, [...args, ...newFnArgs])
}
}
这里我就只实现两种方法了,ES6之前的寄生组合式继承 和 ES6之后的class继承方式。
/**
* es6之前 寄生组合继承
*/
{
function Parent(name) {
this.name = name
this.arr = [1, 2, 3]
}
Parent.prototype.say = () => {
console.log('Hi');
}
function Child(name, age) {
Parent.call(this, name)
this.age = age
}
// 核心代码 通过Object.create创建新对象 子类 和 父类就会隔离
// Object.create:创建一个新对象,使用现有的对象来提供新创建的对象的__proto__
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
}
/**
* es6继承 使用关键字class
*/
{
class Parent {
constructor(name) {
this.name = name
this.arr = [1, 2, 3]
}
}
class Child extends Parent {
constructor(name, age) {
super(name)
this.age = age
}
}
}
补充一个小知识, ES6的Class继承在通过 Babel 进行转换成ES5代码的时候 使用的就是 寄生组合式继承。
继承的方法有很多,记住上面这两种基本就可以了!
首先我们要知道 new一个对象的时候他发生了什么。
其实就是在内部生成了一个对象,然后把你的属性这些附加到这个对象上,最后再返回这个对象。
function myNew(fn, ...args) {
// 基于原型链 创建一个新对象
let newObj = Object.create(fn.prototype)
// 添加属性到新对象上 并获取obj函数的结果
let res = fn.call(newObj, ...args)
// 如果执行结果有返回值并且是一个对象, 返回执行的结果, 否则, 返回新创建的对象
return res && typeof res === 'object' ? res : newObj;
}
这道题考察的是,js的任务执行流程,对宏任务和微任务的理解
console.log("start");
setTimeout(() => {
console.log("setTimeout1");
}, 0);
(async function foo() {
console.log("async 1");
await asyncFunction();
console.log("async2");
})().then(console.log("foo.then"));
async function asyncFunction() {
console.log("asyncFunction");
setTimeout(() => {
console.log("setTimeout2");
}, 0);
new Promise((res) => {
console.log("promise1");
res("promise2");
}).then(console.log);
}
console.log("end");
提示:
开始执行:
start宏任务队列async 1执行await的函数asyncFunction放到宏任务队列promise1放到微任务队列微任务队列foo.thenendpromise2async2setTimeout1setTimeout2、
画工不好,能理解到意思就行?。 看看你们的想法和答案是否和这个流程一致
这道题我是没写出来,最开始想着 trycatch 但这个并不是全局的。
后续查了资料才发现 是用一个window上面的方法
// 使用Try catch 只能拦截try语句块里面的
try {
new Promise((resolve, reject) => {
reject("WTF 123");
});
} catch (e) {
console.log("e", e);
throw e;
}
// 使用 unhandledrejection 来拦截全局错误 (这个是对的)
window.addEventListener("unhandledrejection", (event) => {
event && event.preventDefault();
console.log("event", event);
});
这个我只通过了一种方法实现,就是刚刚我们在上面js执行流程中我有提过。 await 会有异步堵塞的意思
还有一个方法是我在网上找到的方法,通过完全堵塞进程的方法来实现 这个有点吊
// 使用 promise 配合await的异步方法来实现 sleep
{
(async () => {
console.log('start');
await sleep(3000)
console.log('end');
function sleep(timer) {
return new Promise(res => {
setTimeout(() => {
res()
}, timer);
})
}
})();
}
// 方法二 这是完全堵塞进程来达到sleep
{
(async () => {
console.log('start');
await sleep(3000)
console.log('end');
function sleep(delay) {
let t = Date.now();
while (Date.now() - t <= delay) {
continue;
}
};
})()
}
光这个的话,可以通过闭包的方式实现了
我给这个加了一个难度,如何才能实现一直调用
// 题意的答案
const add = (num1) => (num2)=> num2 + num1;
// 我自己整了一个加强版 可以无限链式调用 add(1)(2)(3)(4)(5)....
function add(x) {
// 存储和
let sum = x;
// 函数调用会相加,然后每次都会返回这个函数本身
let tmp = function (y) {
sum = sum + y;
return tmp;
};
// 对象的toString必须是一个方法 在方法中返回了这个和
tmp.toString = () => sum
return tmp;
}
alert(add(1)(2)(3)(4)(5))
无限链式调用实现的关键在于 对象的 toString 方法: 每个对象都有一个 toString() 方法,当该对象被表示为一个文本值时,或者一个对象以预期的字符串方式引用时自动调用。
也就是我在调用很多次后,他们的结果会存在add函数中的sum变量上,当我alert的时候 add会自动调用 toString方法 打印出 sum, 也就是最终的结果
就是找到仅在两个数组中出现过一次的数据
var a = [1, 2, 4], b = [1, 3, 8, 4]
const newArr = a.concat(b).filter((item, _, arr) => {
return arr.indexOf(item) === arr.lastIndexOf(item)
})
最终出来的结果是 [2,3,8], 原理其实很简单: 合并两个数组,然后查找数组的第一个出现的索引和最后一个出现的索引是否一致就可以判断是否是独立的数据了。
就是判断一个数字能不能被开平方, 比如9的开平方是3 是对的。 5没法开平方就是错的。
var fn = function (num) {
return num ** 0.5 % 1 == 0
};
原理就是,开平方后判断是否是正整数就行了
function Foo() {
getName = function () {
console.log(1);
};
return this;
}
Foo.getName = function () {
console.log(2);
}
Foo.prototype.getName = function () {
console.log(3);
}
var getName = function () {
console.log(4);
}
function getName() {
console.log(5)
}
Foo.getName();
getName();
Foo().getName()
getName();
new Foo.getName();
new Foo().getName()
new new Foo().getName()
这道题其实就是看你对作用域的关系的理解吧
执行结果:
执行 Foo.getName(), 执行Foo函数对象上的的静态方法。打印出 2
执行 getName(), 就是执行的getName变量的函数。打印 4
js的预编译函数提升 和 变量提升函数声明的优先级最高,会被提升至当前作用域最顶端4 的这个函数赋值给变量执行 Foo().getName(), 调用Foo执行后返回值上的getName方法。 Foo函数执行了,里面会给外面的getName函数重新赋值,并返回了this。 也就是执行了this.getName。所以打印出了 1
执行 getName(), 由于上一步,函数被重新赋值。所以这次的结果和上次的结果是一样的,还是为1
执行 new Foo.getName(), 这个 new 其实就是new了Foo上面的静态方法getName 所以是2。 当然如果你们在这个函数里面打印this的话,会发现指向的是一个新对象 也就是new出来的一个新对象
这里 . 的优先级比 new 高执行 new Foo().getName(),这里函数执行 new Foo() 会返回一个对象,然后调用这个对象原型上的getName方法, 所以结果是 3
执行 new new Foo().getName(), 这个和上一次的结果是一样,上一个函数调用后并咩有返回值,所以在进行new的时候也没有意义了。 最终结果也是3
function Foo() {
Foo.a = function () {
console.log(1);
};
this.a = function () {
console.log(2);
};
}
Foo.prototype.a = function () {
console.log(4);
};
Function.prototype.a = function () {
console.log(3);
};
Foo.a();
let obj = new Foo();
obj.a();
Foo.a();
执行结果:
执行Foo.a(),Foo本身目前并没有a这个值,就会通过 __proto__ 进行查找,但是
, 所以输出是
3
new 实例化了 Foo 生成对象 obj,然后调用 obj.a(),但是在Foo函数内部给这个obj对象附上了a函数。 所以结果是2。 如果在内部没有给这个对象赋值a的话,就会去到原型链查找a函数,就会打印4.
执行Foo.a(), 在上一步中Foo函数执行,内部给Foo本身赋值函数a,所以这次就打印1
这个题的意思就是 [5, [[4, 3], 2, 1]] 变成 (5 - ((4 - 3) - 2 - 1)) 并执行。 且不能使用eval()
方法一: 既然不能用 eval, 那我们就用new Function吧?
方法二: 当然方法一有点违背了题意,所以还有第二种方法
var newArr = [5, [[4, 3], 2, 1]]
// 1. 取巧
// 转为字符串
let newStringArr = `${JSON.stringify(newArr)}`
// 循环修改括号和减号
let fn = newStringArr.split("").map((el) => {
switch (el) {
case "[":
return '('
case "]":
return ')'
case ",":
return '-'
default:
return el
}
}).join("")
// 最终通过new Function 调用可以了!
new Function("return " + fn)()
// 2. 方法二
function run(arr) {
return arr.reduce((pre, cur) => {
let first = Array.isArray(pre) ? run(pre) : pre
let last = Array.isArray(cur) ? run(cur) : cur
return first - last
})
}
run(nweArr)
方法一的原理就很简单,转成字符串循环修改括号和减号在进行拼接。最终通过 new Function 调用就可以了
方法二的意思就是通过 reduce 进行一个递归调用 的意思。 如果左边不是数组就可以减去右边的,但如果右边是数组的话,就要把右边的数组先进行减法运算。也是就减法括号运算的的优先级.
const flat = function (arr, deep = 1) {
// 声明一个新数组
let result = []
arr.forEach(item => {
if (Array.isArray(item) && deep > 0) {
// 层级递减
// deep-- 来自评论区的大佬指正:deep - 1
// 使用concat链接数组
result = result.concat(flat(item, deep - 1))
} else {
result.push(item)
}
})
return result
}
原理就是,先在内部生成一个新数组,遍历原来的数组
当原数组内 存在数组并且层级deep大于等于1时进行递归, 如果不满足这个条件就可以直接push数据到新数组去
递归同时要先把层级减少, 然后通过 concat 链接递归出来的数组
最终返回这个数组就可以了
最顶层的parent 为 -1 ,其余的 parent都是为 上一层节点的id
let arr = [
{ id: 0, name: '1', parent: -1, childNode: [] },
{ id: 1, name: '1', parent: 0, childNode: [] },
{ id: 99, name: '1-1', parent: 1, childNode: [] },
{ id: 111, name: '1-1-1', parent: 99, childNode: [] },
{ id: 66, name: '1-1-2', parent: 99, childNode: [] },
{ id: 1121, name: '1-1-2-1', parent: 112, childNode: [] },
{ id: 12, name: '1-2', parent: 1, childNode: [] },
{ id: 2, name: '2', parent: 0, childNode: [] },
{ id: 21, name: '2-1', parent: 2, childNode: [] },
{ id: 22, name: '2-2', parent: 2, childNode: [] },
{ id: 221, name: '2-2-1', parent: 22, childNode: [] },
{ id: 3, name: '3', parent: 0, childNode: [] },
{ id: 31, name: '3-1', parent: 3, childNode: [] },
{ id: 32, name: '3-2', parent: 3, childNode: [] }
]
function arrToTree(arr, parentId) {
// 判断是否是顶层节点,如果是就返回。不是的话就判断是不是自己要找的子节点
const filterArr = arr.filter(item => {
return parentId === undefined ? item.parent === -1 : item.parent === parentId
})
// 进行递归调用把子节点加到父节点的 childNode里面去
filterArr.map(item => {
item.childNode = arrToTree(arr, item.id)
return item
})
return filterArr
}
arrToTree(arr)
这道题也是利用递归来进行的,在最开始会进行是否是顶层节点的判断
如果是就直接返回,如果不是则判断是不是自己要添加到父节点的子节点
然后再一层一层把节点加入进去
最后返回这个对象
题意就是, 我有两个数组,把他们两个合并。然后并去重,去重的逻辑是哪儿边的重复次数更多,我就留下哪儿边的。
比如下面的数组中,一边有两个数字5,另一半有三个数字5 。则我需要留下三个数字5,去掉两个数字5。 循环往复,最后得到的结果在进行排序。
数组一: [1, 100, 0, 5, 1, 5]
数组二: [2, 5, 5, 5, 1, 3]
最终的结果: [0, 1, 1, 2, 3, 5, 5, 5, 100]
// 判断出现次数最多的次数
function maxNum(item, arr) {
let num = 0;
arr.forEach(val => {
item === val && num++
})
return num
}
function fn(arr1, arr2) {
// 使用Map数据类型来记录次数
let obj = new Map();
// 合并数组并找出最多的次数, 并以键值对存放到Map数据类型
[...arr1, ...arr2].forEach(item => {
let hasNum = obj.get(item)
let num = 1
if (hasNum) {
num = hasNum + 1
}
obj.set(item, num)
})
// 存放合并并去重之后的数组
let arr = []
// 遍历Map数据类型 然后把次数最多的直接push到新数组
for (const key of obj.keys()) {
if (obj.get(key) > 1) {
for (let index = 0; index < Math.max(maxNum(key, arr1), maxNum(key, arr2)); index++) {
arr.push(key)
}
} else {
arr.push(key)
}
}
// 最后进行排序
return arr.sort((a, b) => a - b)
}
这个题的思路其实就是,我先把两个数组合并起来
并以键值对的方式存放到Map数据类型, 键就是数据,而值就是这个数据出现的次数
生成一个新数组,用来存放合并之后的数组
遍历这个Map数据类型, 如果这个数据出现的次数大于一,那么就去寻找两个数组中谁出现的次数更多,把出现次数更多的这个数据,循环push到新数组中。 如果出现次数等于一,那就直接push到新数组中即可。
最后再把数组进行排序,然后返回新数组就可。
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa
需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc
目录第1题连续问题分析:解法:第2题分组问题分析:解法:第3题间隔连续问题分析:解法:第4题打折日期交叉问题分析:解法:第5题同时在线问题分析:解法:第1题连续问题如下数据为蚂蚁森林中用户领取的减少碳排放量iddtlowcarbon10012021-12-1212310022021-12-124510012021-12-134310012021-12-134510012021-12-132310022021-12-144510012021-12-1423010022021-12-154510012021-12-1523.......找出连续3天及以上减少碳排放量在100以上的用户分析:遇到这类
我正在尝试将以下SQL查询转换为ActiveRecord,它正在融化我的大脑。deletefromtablewhereid有什么想法吗?我想做的是限制表中的行数。所以,我想删除少于最近10个条目的所有内容。编辑:通过结合以下几个答案找到了解决方案。Temperature.where('id这给我留下了最新的10个条目。 最佳答案 从您的SQL来看,您似乎想要从表中删除前10条记录。我相信到目前为止的大多数答案都会如此。这里有两个额外的选择:基于MurifoX的版本:Table.where(:id=>Table.order(:id).
我目前正在用Ruby编写一个项目,它使用ActiveRecordgem进行数据库交互,我正在尝试使用ActiveRecord::Base.logger记录所有数据库事件具有以下代码的属性ActiveRecord::Base.logger=Logger.new(File.open('logs/database.log','a'))这适用于迁移等(出于某种原因似乎需要启用日志记录,因为它在禁用时会出现NilClass错误)但是当我尝试运行包含调用ActiveRecord对象的线程守护程序的项目时脚本失败并出现以下错误/System/Library/Frameworks/Ruby.frame
我有一个应用需要发送用户事件邀请。当用户邀请friend(用户)参加事件时,如果尚不存在将用户连接到该事件的新记录,则会创建该记录。我的模型由用户、事件和events_user组成。classEventdefinvite(user_id,*args)user_id.eachdo|u|e=EventsUser.find_or_create_by_event_id_and_user_id(self.id,u)e.save!endendend用法Event.first.invite([1,2,3])我不认为以上是完成我的任务的最有效方法。我设想了一种方法,例如Model.find_or_cr
在许多ruby类之间共享记录器实例的最佳(正确)方法是什么?现在我只是将记录器创建为全局$logger=Logger.new变量,但我觉得有更好的方法可以在不使用全局变量的情况下执行此操作。如果我有以下内容:moduleFooclassAclassBclassC...classZend在所有类之间共享记录器实例的最佳方式是什么?我是以某种方式在Foo模块中声明/创建记录器还是只是使用全局$logger没问题? 最佳答案 在模块中添加常量:moduleFooLogger=Logger.newclassAclassBclassC..
如何在出现异常时指定全局救援,如果您将Sinatra用于API或应用程序,您将如何处理日志记录? 最佳答案 404可以在not_found方法的帮助下处理,例如:not_founddo'Sitedoesnotexist.'end500s可以通过调用带有block的错误方法来处理,例如:errordo"Applicationerror.Plstrylater."end错误的详细信息可以通过request.env中的sinatra.error访问,如下所示:errordo'Anerroroccured:'+request.env['si
例如,假设我有一个名为Products的模型,并且在ProductsController中,我有以下代码用于product_listView以显示已排序的产品。@products=Product.order(params[:order_by])让我们想象一下,在product_listView中,用户可以使用下拉菜单按价格、评级、重量等进行排序。数据库中的产品不会经常更改。我很难理解的是,每次用户选择新的order_by过滤器时,rails是否必须查询,或者rails是否能够以某种方式缓存事件记录以在服务器端重新排序?有没有一种方法可以编写它,以便在用户排序时rails不会重新查询结果