当前有一个元素特别多且乱序的数组,数组里每个元素都是独一无二的。我希望找到数组里前十大的元素,但不能对数组进行sort查找。有什么方法。
在不使用排序的情况下找到数组中前十大元素的问题可以通过使用最小堆(min-heap)数据结构来解决,这是一种高效的解决方案,特别适用于处理大量数据和内存使用受限的场景。
最小堆是一种二叉树结构,其中每个父节点的值都小于或等于其子节点的值。这样,树的根节点始终是当前堆中最小的元素。对于找到前十大元素的问题,我们可以维护一个大小为10的最小堆。
以下是具体的步骤:
- 初始化:创建一个大小为10的最小堆。如果数组的元素少于10个,那么就使用数组中所有的元素初始化堆。
- 遍历数组:遍历数组中的每个元素。对于每个元素,比较它与堆顶(即堆中的最小元素)的大小。
-
- 如果当前元素大于堆顶元素,那么将堆顶元素移除,并用当前元素替换堆顶,然后重新调整堆以维持最小堆的性质。
- 如果当前元素小于或等于堆顶元素,那么不做任何操作,继续遍历下一个元素。
- 维护堆:在每次替换堆顶元素后,都需要通过“下沉”操作来调整堆,确保最小堆的性质得到保持。下沉操作是将当前节点与其子节点中较小的一个进行交换,直到满足最小堆的性质或者到达叶子节点。
- 结果:遍历完数组后,堆中的元素即为数组中前十大的元素。
这种方法的时间复杂度为O(nlogk),其中n是数组的大小,k是堆的大小(在这个问题中k=10)。这是因为每次插入堆中的操作需要logk的时间,而最多需要插入n次。
需要注意的是,由于堆的大小固定,这种方法只适用于找到前k个最大的元素,如果需要找到前k个最小的元素,可以使用最大堆(max-heap)并进行类似的操作。
这种方法不需要对整个数组进行排序,因此避免了排序带来的O(nlogn)时间复杂度,特别适用于大规模数据集。同时,由于堆是内存中的数据结构,它不需要额外的磁盘空间,适合内存使用受限的环境。
const [1,2,3].map(parseInt)
parseInt是数字的一个方法。第一个参数是要操作的对象,第二个参数是要将第一个
参数转为多少的进制。范围是2 - 36。为0就是10进制
比如说 parseInt(10, 2). 将10以2进制的方式转为10进制。。
0 * 2 ^ 0 + 1 * 2 ^ 1 = 2. 从右往左。
parseInt(1, 0) // 将1转为10进制。 1
parseInt(2, 1) // NaN
parseInt(3, 2) // NaN
当我把style标签放在body中,有什么问题?
将 style 标签放在 body 标签内,而不是 head 标签内,可能会导致以下几个问题:
- 加载顺序问题: 浏览器在解析HTML文档时,是从上到下逐行读取的。如果样式表(style 标签或外部CSS文件)在页面内容之后加载,那么在样式加载完成之前,用户可能会看到没有样式的页面内容。这会导致页面闪烁,因为一旦样式加载完毕,页面会重新渲染并应用样式。
- 性能影响: 将样式表放在 body 中会增加页面的首次加载时间,因为浏览器需要先下载并解析HTML内容,然后再处理和应用样式。这可能会导致页面的首次内容绘制(First Contentful Paint, FCP)延迟,影响用户体验。
- SEO问题: 搜索引擎优化(SEO)依赖于快速且有效的页面加载。如果样式表放在 body 中,可能会影响页面的渲染速度,从而影响SEO。此外,搜索引擎爬虫可能无法正确解析放在 body 中的样式,这可能会影响页面内容的索引。
- 可维护性和可访问性问题: 将样式表放在 head 中是一种常见的做法,它有助于保持代码的组织和可维护性。将样式表放在 body 中可能会使代码更难理解和维护。此外,对于使用屏幕阅读器等辅助技术的用户来说,样式的加载顺序和位置可能会影响他们对页面内容的理解和导航。
- 缓存问题: 外部CSS文件通常可以被浏览器缓存,从而加快后续访问的速度。如果样式表放在 body 中,每次页面更新时都可能需要重新加载样式,这会减少缓存的机会,并可能增加服务器的负载。
为了解决这些问题,最佳实践是将样式表放在 head 标签内,或者使用外部CSS文件并通过 link 标签在 head 中引入。这样可以确保样式表在页面内容加载之前被加载和应用,从而提供更好的性能和用户体验。
页面隐藏元素的方法,以及每种方法的区别
display: none
将元素设置为display:none后,元素在页面上将彻底消失
元素本身占有的空间就会被其他元素占有,也就是说它会导致浏览器的重排和重绘
消失后,自身绑定的事件不会触发,也不会有过渡效果
特点:元素不可见,不占据空间,无法响应点击事件
visibility:hidden
设置元素的visibility为hidden也是一种常用的隐藏元素的方法
从页面上仅仅是隐藏该元素,DOM结果均会存在,只是当时在一个不可见的状态,不会触发重排,但是会触发重绘
.hidden{
visibility:hidden
}
给人的效果是隐藏了,所以他自身的事件不会触发
特点:元素不可见,占据页面空间,无法响应点击事件
opacity:0
opacity属性表示元素的透明度,将元素的透明度设置为0后,在我们用户眼中,元素也是隐藏的
opacity: N 和 filter: opacity(N) 属性可以传递一个 0 到 1 之间的数字,或者 0% 和 100% 之间的百分比,对应地表示完全透明和完全不透明。
- opacity: N:该属性用来设置元素的透明度;
- filter: opacity(N) :filter属性用来设置元素的滤镜,opacity是滤镜重的透明度,用来设置元素的透明度。
不会引发重排,一般情况下也会引发重绘
特点:改变元素透明度,元素不可见,占据页面空间,可以响应点击事件
z-index
可以通过将元素的 z-index 属性设置为负值,以实现元素的隐藏。这实际上就是将元素放在了我们看不到的层。
div {
z-index: -1;
}
position:absolute
将元素移出可视区域
.hide {
position: absolute;
top: -9999px;
left: -9999px;
}
特点:元素不可见,不影响页面布局
- overflow: hidden;
-
- 当一个元素的内容超过其容器边界时,通过设置 overflow 属性为 hidden,可以隐藏超出部分的内容。这对于隐藏滚动条或者部分内容超出了设计范围的情况非常有用。
- Transform: scale(0);
-
- 使用CSS的 transform 属性,特别是 scale(0),可以将元素缩小至零尺寸,从而实现隐藏效果,同时元素仍存在于页面布局中。
给一个元素设置了display: none以后,还能被选择器选中吗?
给一个HTML元素设置了display: none样式后,该元素虽然在视觉上不可见,但它仍然存在于DOM树中,并且可以被各种选择器选中。这是因为display: none只是改变了元素的布局行为(使其不占用任何空间且不参与文档流),并未从DOM结构中移除该元素。
以下是一些具体场景说明:
- CSS选择器:无论是静态的CSS选择器(如类选择器、ID选择器、属性选择器等),还是动态的JavaScript CSS查询方法(如querySelector、querySelectorAll、jQuery的选择器等),都可以正常选中设置了display: none的元素。这些选择器是基于DOM结构而非元素的可视状态进行匹配的。
- JavaScript DOM方法:使用诸如getElementById、getElementsByClassName、getElementsByTagName、getElementsByName等DOM API方法,也可以顺利获取到设置了display: none的元素,因为这些方法同样是基于DOM结构而非元素的样式状态。
- 事件处理:虽然设置了display: none的元素在页面上不可见,但它所绑定的事件处理器(如通过addEventListener设置的事件)依然有效。即使元素不可见,当触发相关事件时,相应的事件处理器仍会执行。
综上所述,即使一个元素被设置为display: none,它仍然能够被选择器成功选中,并且其事件处理逻辑不受影响。这意味着开发者可以继续通过CSS选择器或DOM API对这些不可见元素进行样式调整、数据操作或事件处理,只是用户在页面上看不到它们。
for in , Object.keys(), Object.values() 三种遍历对象有什么区别 ?
const obj = {
a: 1
}
const aa = Object.create(obj)
aa.b = 2
Object.prototype.c = 3
for (let key in aa) {
console.log(key); // b, a, c.
}
for (let key in aa) {
if (aa.hasOwnProperty(key)) {
console.log(key); // b
}
}
// a这个属性本质也是通过继承得来的
console.log(Object.keys(aa)); // ['b']
console.log(Object.values(aa)); // [2]
Object.getOwnPropertyNames() 方法返回一个给定对象自身拥有的属性(包括不可枚举的属性,但不包括Symbol类型的属性)的名称组成的数组,而不包括继承自原型链上的属性和方法。
Object.getOwnPropertySymbols() 方法只返回对象自身的 Symbol 属性,不包括从原型链继承的 Symbol 属性。
如何实现js中的迭代器
在JavaScript中,迭代器是一种允许你遍历一个集合(如数组、对象、字符串等)的对象。要实现自定义的迭代器,你需要定义一个对象,该对象包含一个 next() 方法,并且该方法遵循迭代器协议。迭代器协议要求 next() 方法返回一个对象,该对象具有两个属性:value 和 done。value 属性表示当前迭代到的值,done 是一个布尔值,表示迭代是否完成。
以下是一个简单的自定义迭代器的示例,它用于遍历一个数组:
function arrayIterator(array) {
let currentIndex = 0;
return {
next: function() {
if (currentIndex < array.length) {
return {
value: array[currentIndex++],
done: false
};
} else {
return {
value: undefined,
done: true
};
}
}
};
}
// 使用示例
const myArray = [1, 2, 3, 4, 5];
const iterator = arrayIterator(myArray);
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: 4, done: false }
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
在这个例子中,arrayIterator 函数接受一个数组作为参数,并返回一个包含 next() 方法的对象。每次调用 next() 方法时,它都会检查当前索引是否小于数组的长度。如果是,它返回一个包含当前值和 done 为 false 的对象,并将索引递增。如果索引已经到达数组的末尾,它返回一个表示迭代完成的对象,其中 done 为 true。
你也可以使用生成器函数来简化迭代器的实现。生成器函数使用 function* 语法定义,并使用 yield 关键字来产生序列的值。生成器函数在每次 yield 时暂停执行,并在下一次调用 next() 方法时从上次停下的地方继续执行。
以下是使用生成器函数实现迭代器的示例:
复制
function* generatorArrayIterator(array) {
for (const value of array) {
yield value;
}
}
// 使用示例
const myArray = [1, 2, 3, 4, 5];
const generator = generatorArrayIterator(myArray);
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: false }
console.log(generator.next()); // { value: 3, done: false }
console.log(generator.next()); // { value: 4, done: false }
console.log(generator.next()); // { value: 5, done: false }
console.log(generator.next()); // { value: undefined, done: true }
在这个例子中,generatorArrayIterator 是一个生成器函数,它通过 yield 为数组的每个元素产生值。当你创建一个生成器对象并调用它的 next() 方法时,它会按照生成器函数中的逻辑逐个产生值,直到遍历完数组。
js中怎么捕获全局错误
下面是如何使用 window.onerror 来尝试捕获 Promise 内部通过 setTimeout 抛出的错误的示例:
window.onerror = function(message, source, lineno, colno, error) {
console.error('Global error caught:', message, source, lineno, colno, error);
// 这里可以执行错误处理逻辑,比如记录错误信息等
return true; // 返回true以阻止默认的错误处理(弹出错误对话框)
};
react中的声明多个hooks,这多个hooks按顺序排列,基于链表这种数据结构。这也就是为什么hooks不能写在条件判断语句中,因为会有某个hooks在某次渲染中没用执行,导致hooks值对应错误的问题。 所以,hooks为什么要这么设计?
React中的Hooks是基于链表数据结构来存储和管理的,这是为了确保Hooks的调用顺序始终保持一致。在React的内部,每个Hook调用都会生成一个状态节点,并将这些节点链接在一起形成一个链表。这样做有几个重要的原因:
- 状态隔离: 通过链表结构,React能够为每个Hook维护独立的状态。这意味着即使在同一个组件中有多个同名的Hook,它们的状态也不会相互影响。例如,如果你在组件中多次调用useState,每次调用都会生成一个新的、独立的state值。
- 顺序执行: Hooks的执行顺序非常重要,因为它直接影响到状态的更新和副作用的处理。链表结构确保了即使在组件的渲染过程中有跳过或重复的部分,Hooks也会按照它们被声明的顺序执行。这有助于避免状态更新的竞态条件和不一致性。
- 调试和错误追踪: 当React组件出现问题时,能够追踪到每个Hook的调用顺序和状态变化是非常有用的。链表结构使得React能够在开发者工具中提供清晰的调用栈和状态历史,从而帮助开发者更容易地定位和解决问题。
- 性能优化: 链表结构允许React在更新组件时跳过那些没有变化的Hook。这意味着React可以避免不必要的状态重新计算和副作用执行,从而提高性能。
正因为Hooks是基于链表结构的,所以它们必须按照声明的顺序依次调用。如果在条件判断语句中使用Hooks,那么在某些渲染周期中可能会跳过某些Hook的调用,导致状态更新和副作用执行的顺序与预期不符。例如,如果你在一个条件判断中使用useState,那么在条件为false时,这个useState调用将不会执行,这可能会导致状态丢失或不一致。
为了确保Hooks的正确性和一致性,React规定Hooks必须在组件的最顶层调用,不能放在条件判断语句、循环语句或嵌套函数中。这样可以保证每次渲染时,所有的Hook都被按照相同的顺序调用,从而避免了潜在的问题。这种设计使得Hooks更加强大和灵活,同时也保持了React的可预测性和一致性。
react hooks中出现的闭包陷阱。 怎么去解决它
在React Hooks中,闭包陷阱通常与状态更新和副作用处理有关。闭包陷阱可能导致组件状态不一致或副作用执行不符合预期。以下是一些常见的闭包陷阱示例以及如何解决它们:
1. 在useEffect中使用循环引用
当你在useEffect中使用循环引用的变量时,可能会导致闭包陷阱。例如,如果你在useEffect中更新了状态,但是更新后的值没有被useEffect中的闭包捕获,这可能会导致问题。
示例:
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
const handleResize = () => {
setCount(count + 1); // 闭包捕获的是旧的count值
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, [count]); // 依赖项正确
}
解决方案:
使用useRef来存储一个变量,这样每次组件渲染时,该变量都会更新为最新的值。
function MyComponent() {
const countRef = useRef(0);
const [count, setCount] = useState(0);
useEffect(() => {
countRef.current = count; // 更新ref的值
}, [count]);
useEffect(() => {
const handleResize = () => {
setCount(countRef.current + 1); // 使用最新的count值
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // 不依赖count
}
2. 在useEffect中使用回调函数
在useEffect中使用回调函数时,如果回调函数依赖于组件的状态,而这些状态在回调函数执行前已经更新,可能会导致闭包捕获的是旧的状态值。
示例:
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
const timeoutId = setTimeout(() => {
setCount(count + 1); // 闭包捕获的是旧的count值
}, 1000);
return () => {
clearTimeout(timeoutId);
};
}, [count]);
return <div>{count}</div>;
}
解决方案:
使用useRef来存储最新的状态值,或者使用useEffect的依赖项数组来确保setTimeout回调在状态更新后重新执行。
function MyComponent() {
const countRef = useRef(0);
const [count, setCount] = useState(0);
useEffect(() => {
countRef.current = count;
}, [count]);
useEffect(() => {
const timeoutId = setTimeout(() => {
setCount(countRef.current + 1); // 使用最新的count值
}, 1000);
return () => {
clearTimeout(timeoutId);
};
}, []); // 不依赖count
return <div>{count}</div>;
}
3. 在useCallback中使用依赖项
useCallback用于返回一个记忆化的回调函数,该函数仅在依赖项变化时才会更新。如果不正确地使用依赖项,可能会导致闭包陷阱。
示例:
function MyComponent() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount(count + 1); // 闭包捕获的是旧的count值
}, [count]);
return <button onClick={increment}>Count: {count}</button>;
}
解决方案:
确保useCallback的依赖项数组包含所有回调函数内部使用的变量。
function MyComponent() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount(count + 1);
}, [count]); // 正确依赖count
return <button onClick={increment}>Count: {count}</button>;
}
总的来说,解决闭包陷阱的关键在于确保闭包捕获的是最新的状态值,并且正确地使用依赖项数组。使用useRef来存储状态值是一种常见的解决方案,它可以帮助我们在闭包中访问到最新的状态。此外,确保useEffect、useCallback和useMemo的依赖项数组正确无误也是避免闭包陷阱的重要措施。
解析url
提供的URL字符串是 www.example.com/path?query=…
- 协议(Protocol): https 这是URL的第一部分,定义了使用的网络协议,本例中为HTTPS,表示安全的超文本传输协议。
- 域名(Domain): www.example.com 这是URL中定义主机名的部分,本例中为 www.example.com。它包括子域(如果有的话)和顶级域(TLD)。
- 端口号(Port): 不明确(默认为HTTPS协议的端口) 在提供的URL中没有明确指定端口号。对于HTTPS协议,如果没有指定端口号,那么默认端口号通常是 443。
- 路径(Path): /path 这是服务器上资源的路径,本例中为 /path。
- 查询字符串(Query String): query=123&vd_source=5b82e764befc715bb&aa=bb 这是跟在问号(?)后面的部分,包含了一个或多个参数,用于传递额外的信息给服务器。
const aa = 'https://www.example.com:8080/video/BV1MZ4y1c7SJ/fewfw/?query=123&vd_source=5b82e764befc715bb&aa=bb'
function fn(str) {
let protocol = ""
let host = ""
let port = str[4] === "s" ? 443 : 80
let part = ''
let queryString = {}
protocol = str.split(':')[0]
if (str.split(':')[2]) {
port = ""
host = str.split(':')[1].slice(2)
for (let val of str.split(':')[2]) {
if (!isNaN(val)) {
port += val
} else {
break
}
}
part = str.split(':')[2].split('?')[0]
} else {
host = str.split(':')[1].split('?')[0].slice(2).split('/')[0]
}
let hostAndportAndpart = str.split('//')[1].split('?')[0].split('/')
hostAndportAndpart[0] = ""
part = hostAndportAndpart.join('/')
let partAll = str.split('?')[1].split('&')
for (let value of partAll) {
queryString[value.split('=')[0]] = value.split('=')[1]
}
return {
protocol: protocol,
host: host,
port: port,
part: part,
queryString: queryString,
}
}
console.log(fn(aa));