1.如何使用JS实现数组的乱序输出?请写出具体代码
在JavaScript中,可以通过多种方法实现数组的乱序输出。其中,最常用的方法之一是使用Fisher-Yates洗牌算法(也称为Knuth洗牌算法)。该算法可以高效地打乱数组的顺序。以下是具体的代码实现:
// Fisher-Yates 洗牌算法实现数组的乱序
function shuffleArray(array) { for (let i = array.length - 1; i > 0; i--) { // 生成一个0到i之间的随机整数 const j = Math.floor(Math.random() * (i + 1)); // 交换元素 array[i] 和 array[j] [array[i], array[j]] = [array[j], array[i]]; } return array;
} // 示例数组
const originalArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // 打乱数组顺序
const shuffledArray = shuffleArray(originalArray); // 输出乱序后的数组
console.log(shuffledArray);
代码解释:
- 函数定义:
shuffleArray(array)
:接收一个数组作为参数。
- 循环遍历:
- 从数组的最后一个元素开始向前遍历(
i
从array.length - 1
到1
)。
- 从数组的最后一个元素开始向前遍历(
- 生成随机索引:
const j = Math.floor(Math.random() * (i + 1));
:生成一个0到i
之间的随机整数。
- 交换元素:
[array[i], array[j]] = [array[j], array[i]];
:使用ES6的解构赋值语法交换当前索引i
和随机索引j
处的元素。
- 返回打乱后的数组:
- 函数返回打乱后的数组。
- 示例使用:
- 定义一个示例数组
originalArray
。 - 调用
shuffleArray
函数打乱数组顺序,并将结果存储在shuffledArray
中。 - 使用
console.log
输出打乱后的数组。
- 定义一个示例数组
这个算法的时间复杂度是O(n),因为它只需要遍历数组一次,并且在每次迭代中执行一个常数时间的交换操作。此外,这个算法保证了每个排列的概率是相等的,即每个排列的出现概率是1/n!。
2.如何使用JS实现数组元素的求和?请写出具体代码
在JavaScript中,实现数组元素求和是一个相对简单的任务。你可以使用多种方法来完成这一操作,包括使用for
循环、forEach
方法、reduce
方法以及ES6的箭头函数和let
声明等。下面是几种具体的实现方式:
使用 reduce
方法
reduce
方法是数组的一个高阶函数,它接收一个回调函数和一个可选的初始值作为参数,并返回数组元素经过处理后的累计值。
const numbers = [1, 2, 3, 4, 5]; const sum = numbers.reduce((accumulator, currentValue) => { return accumulator + currentValue;
}, 0); // 第二个参数是初始值,这里设为0 console.log(sum); // 输出: 15--------------------------------------------------------------
const numbers = [1, 2, 3, 4, 5]; const sum = numbers.reduce((acc, curr) => acc + curr, 0); console.log(sum); // 输出: 15
3.如何使用JS实现数组的扁平化?请写出具体代码
在JavaScript中,数组扁平化是指将一个多维数组转换为一个一维数组。这通常涉及将嵌套数组中的元素“展平”到单个数组中。以下是几种实现数组扁平化的方法:
1. 使用递归
递归是一种常见且直观的方法来处理嵌套结构。你可以编写一个递归函数来遍历数组,如果遇到一个数组,则递归地调用该函数来处理它。
function flattenArray(arr) { let result = []; arr.forEach(item => { if (Array.isArray(item)) { result = result.concat(flattenArray(item)); } else { result.push(item); } }); return result;
} // 示例
const nestedArray = [1, [2, [3, [4, 5]]], 6];
const flatArray = flattenArray(nestedArray);
console.log(flatArray); // 输出: [1, 2, 3, 4, 5, 6]
2. 使用 Array.prototype.reduce
和 Array.prototype.concat
reduce
方法可以累积地处理数组中的每个元素,并返回单个结果。在这里,我们可以使用它来构建一个扁平化的数组。
function flattenArray(arr) { return arr.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenArray(val)) : acc.concat(val), []);
} // 示例
const nestedArray = [1, [2, [3, [4, 5]]], 6];
const flatArray = flattenArray(nestedArray);
console.log(flatArray); // 输出: [1, 2, 3, 4, 5, 6]
3. 使用堆栈(Stack)模拟递归
如果你不想使用递归,可以使用一个堆栈来模拟递归过程。这种方法在处理深度未知的嵌套数组时特别有用。
function flattenArray(arr) { let stack = [...arr]; let result = []; while (stack.length) { const next = stack.pop(); if (Array.isArray(next)) { stack.push(...next); } else { result.push(next); } } // 因为我们是从数组末尾开始处理的,所以结果需要反转 return result.reverse();
} // 或者,为了避免反转,你可以从数组头部开始处理并收集到另一个数组
function flattenArrayWithoutReverse(arr) { let stack = [...arr].reverse(); // 反转输入数组以从尾部开始处理 let result = []; while (stack.length) { const next = stack.pop(); if (!Array.isArray(next)) { result.unshift(next); // 使用unshift将元素添加到结果数组的开头 } else { stack.push(...next.reverse()); // 反转子数组以保持元素顺序 } } return result;
} // 示例
const nestedArray = [1, [2, [3, [4, 5]]], 6];
const flatArray = flattenArray(nestedArray);
console.log(flatArray); // 输出: [1, 2, 3, 4, 5, 6]
注意:上面的 flattenArrayWithoutReverse
函数虽然避免了最终的结果反转,但它需要在处理每个子数组时都进行反转,这可能会增加一些不必要的复杂性。通常,第一种使用堆栈的方法(之后反转结果)在实践中更为常见。
4. 使用 ES6 的 Array.prototype.flat
(最简洁)
从ECMAScript 2019(ES10)开始,JavaScript 提供了一个内置的 flat
方法来直接扁平化数组。你可以指定一个深度参数来控制扁平化的层级。
const nestedArray = [1, [2, [3, [4, 5]]], 6];
const flatArray = nestedArray.flat(Infinity); // Infinity 表示无限深度扁平化
console.log(flatArray); // 输出: [1, 2, 3, 4, 5, 6]
如果你知道数组的最大嵌套深度,也可以提供一个具体的数字作为深度参数。例如,flat(2)
会扁平化两层深度的嵌套数组。
5.如何使用JS实现数组去重?请写出具体代码
在JavaScript中,实现数组去重(即移除数组中的重复元素)有多种方法。以下是几种常见的实现方式,包括使用Set
对象、filter
方法和indexOf
方法,以及使用reduce
方法。
1. 使用 Set
对象
Set
是 ES6 引入的一个新的数据结构,它类似于数组,但是成员的值都是唯一的,没有重复的值。利用这个特性,我们可以很容易地实现数组去重。
const array = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]
2. 使用 filter
方法和 indexOf
方法
filter
方法创建一个新数组,其包含通过所提供函数实现的测试的所有元素。indexOf
方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。结合这两个方法,我们可以实现数组去重。
const array = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = array.filter((item, index) => array.indexOf(item) === index);
console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]
3. 使用 reduce
方法
reduce
方法对数组中的每个元素执行一个由您提供的 reducer 函数(升序执行),将其结果汇总为单个返回值。利用这个方法,我们也可以实现数组去重。
const array = [1, 2, 2, 3, 4, 4, 5];
const uniqueArray = array.reduce((acc, curr) => { if (!acc.includes(curr)) { acc.push(curr); } return acc;
}, []);
console.log(uniqueArray); // 输出: [1, 2, 3, 4, 5]
注意事项
- 上述方法都适用于基本数据类型的数组去重(如数字、字符串等)。如果数组中包含对象或其他复杂数据类型,需要根据具体情况实现去重逻辑。
- 在使用
indexOf
方法进行去重时,如果数组元素是对象,由于对象引用的问题,indexOf
方法可能无法正确判断元素是否重复。此时,可以考虑使用其他方法(如深度比较)来实现对象数组的去重。 Set
对象在内部使用了哈希表来实现元素的唯一性,因此其性能通常优于其他方法(特别是在处理大数据集时)。然而,需要注意的是,Set
对象不支持所有类型的数组元素(如undefined
和NaN
),在使用时需要特别注意。
Javascript中Object、Array、String_objectarray-CSDN博客
6.如何使用JS实现数组的flat方法?请写出具体代码【与3类似】
在JavaScript中,Array.prototype.flat()
方法用于将嵌套的数组“扁平化”,即递归地将一个多维数组转换为一个一维数组。如果你需要在不支持 flat()
方法的旧版JavaScript环境中实现类似的功能,你可以手动编写一个扁平化数组的函数。
以下是一个使用递归实现数组扁平化的示例代码:
function flatArray(arr, depth = 1) { // 创建一个空数组用于存储扁平化后的结果 const result = []; // 遍历数组中的每个元素 for (const item of arr) { // 如果元素是数组,并且递归深度还未达到限制 if (Array.isArray(item) && depth > 0) { // 递归调用 flatArray,并将当前元素的扁平化结果合并到 result 中 result.push(...flatArray(item, depth - 1)); } else { // 如果元素不是数组,或者递归深度已达到限制,则直接将元素添加到 result 中 result.push(item); } } // 返回扁平化后的数组 return result;
} // 示例使用
const nestedArray = [1, [2, [3, [4]], 5]];
const flattenedArray = flatArray(nestedArray, Infinity); // 使用 Infinity 表示无限深度扁平化
console.log(flattenedArray); // 输出: [1, 2, 3, 4, 5]
在这个实现中,flatArray
函数接受两个参数:要扁平化的数组 arr
和一个可选的 depth
参数,表示扁平化的深度。如果 depth
设置为 Infinity
,则表示无限深度扁平化,即无论嵌套多少层都会扁平化。
注意,这个实现使用了 ES6 的扩展运算符(...
)来合并数组。如果你需要在旧版JavaScript环境中运行此代码,你可能需要使用其他方法来合并数组,比如使用 Array.prototype.concat()
方法。
7.如何使用JS实现数组的push方法?请写出具体代码
在JavaScript中,Array.prototype.push()
方法用于将一个或多个元素添加到数组的末尾,并返回新的数组长度。虽然我们可以直接使用JavaScript内置的 push
方法,但如果你想了解如何自己实现一个类似的方法,可以按照以下步骤进行:
以下是一个简单的实现,它模拟了 push
方法的行为:
function myPush(array, ...elements) { // 遍历要添加的元素 for (const element of elements) { // 将元素添加到数组的末尾 array[array.length] = element; } // 返回新的数组长度 return array.length;
} // 示例使用
const myArray = [1, 2, 3];
const newLength = myPush(myArray, 4, 5, 6);
console.log(myArray); // 输出: [1, 2, 3, 4, 5, 6]
console.log(newLength); // 输出: 6
8.如何使用JS实现数组的filter方法?请写出具体代码
在JavaScript中,Array.prototype.filter()
方法用于创建一个新数组,其中包含通过所提供函数实现的测试的所有元素。如果你需要在不支持 filter
方法的旧版JavaScript环境中实现类似的功能,你可以手动编写一个过滤数组的函数。
以下是一个使用高阶函数(即接受函数作为参数或返回函数的函数)来实现数组过滤的示例代码:
function myFilter(array, predicate) { // 创建一个空数组用于存储过滤后的结果 const result = []; // 遍历数组中的每个元素 for (const item of array) { // 如果元素满足谓词函数(predicate)的条件,则将其添加到结果数组中 if (predicate(item)) { result.push(item); } } // 返回过滤后的数组 return result;
} // 示例谓词函数:检查数字是否为偶数
function isEven(number) { return number % 2 === 0;
} // 示例使用
const numbers = [1, 2, 3, 4, 5, 6];
const evenNumbers = myFilter(numbers, isEven);
console.log(evenNumbers); // 输出: [2, 4, 6]
在这个实现中,myFilter
函数接受两个参数:
array
:要过滤的数组。predicate
:一个函数,用于测试数组中的每个元素。该函数应该接受一个参数(即数组中的元素),并返回一个布尔值。如果返回true
,则表示该元素应该包含在结果数组中。
函数内部,我们使用一个 for...of
循环来遍历数组中的每个元素,并使用 predicate
函数来测试每个元素。如果元素满足条件(即 predicate
函数返回 true
),则将其添加到 result
数组中。
最后,函数返回过滤后的 result
数组。
9.如何使用JS实现数组的map方法?请写出具体代码
在JavaScript中,Array.prototype.map()
方法用于创建一个新数组,其结果是对原数组中的每个元素调用一次提供的函数后的返回值组成的新数组。如果你想自己实现一个类似 map
方法的功能,你可以按照以下步骤进行:
以下是一个简单的实现,它模拟了 map
方法的行为:
function myMap(array, callback) { // 创建一个空数组用于存储映射后的结果 const result = []; // 遍历数组中的每个元素 for (let i = 0; i < array.length; i++) { // 调用回调函数,并将当前元素、索引和原数组作为参数传递 // 将回调函数的返回值添加到结果数组中 result.push(callback(array[i], i, array)); } // 返回映射后的数组 return result;
} // 示例回调函数:将数字乘以2
function multiplyByTwo(number, index, array) { // 注意:这里的index和array参数在回调函数中并未使用,但它们是map方法的标准参数 return number * 2;
} // 示例使用
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = myMap(numbers, multiplyByTwo);
console.log(doubledNumbers); // 输出: [2, 4, 6, 8, 10]
在这个实现中,myMap
函数接受两个参数:
array
:要映射的数组。callback
:一个函数,用于处理数组中的每个元素。该函数应该接受三个参数:当前元素、当前元素的索引和原数组,并返回一个值。这个返回值将被添加到结果数组中。
函数内部,我们使用一个 for
循环来遍历数组中的每个元素,并对每个元素调用 callback
函数。我们将 callback
函数的返回值添加到 result
数组中。
最后,函数返回映射后的 result
数组。
10.如何使用JS实现Object.create方法?请写出具体代码
在JavaScript中,Object.create()
方法用于创建一个新对象,该对象使用现有的对象来提供其 [[Prototype]]
(即内部原型)。这意味着新创建的对象将继承现有对象的属性和方法。如果你想自己实现一个类似 Object.create()
的方法,你可以使用一种称为“原型式继承”的技术。
以下是一个简单的实现,它模拟了 Object.create()
方法的行为:
function createObject(proto) {
// 创建一个空的函数F,它的原型指向传入的proto对象
function F() {}
// 设置F的原型为proto
F.prototype = proto;
// 根据F创建一个新对象,这个新对象的原型就是F.prototype,即传入的proto对象
// 因此,这个新对象会继承proto对象的属性和方法
return new F();
}
function createObject(proto) { // 创建一个空的函数F,它的原型指向传入的proto对象 function F() {} // 设置F的原型为proto F.prototype = proto; // 根据F创建一个新对象,这个新对象的原型就是F.prototype,即传入的proto对象 // 因此,这个新对象会继承proto对象的属性和方法 return new F();
} // 示例使用
const person = { isHuman: false, printIntroduction: function() { console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`); }
}; // 创建一个新对象,其原型是person对象
const me = createObject(person); // 为新对象添加属性
me.name = 'John'; // "name" 是 me 的属性
me.isHuman = true; // "isHuman" 也是 me 的属性 // 调用继承自原型对象的printIntroduction方法
me.printIntroduction();
// 输出: "My name is John. Am I human? true"
1.如何使用JS判断对象是否存在循环引用?请写出具体代码
JS对象引用
let obj1 = {};let obj2 = {};obj1.reference = obj2; // obj1 引用 obj2obj2.reference = obj1; // obj2 引用 obj1
循环引用本身并不一定是问题,但在某些情况下,它可能会导致内存泄漏。这是因为垃圾回收器(Garbage Collector, GC)在检测对象是否应该被回收时,可能会因为循环引用而无法正确释放内存。
现在大多数现代JavaScript引擎(如V8引擎,用于Chrome和Node.js)使用标记-清除(Mark-and-Sweep)算法来管理内存。这种算法可以有效地处理循环引用。当垃圾回收器运行时,它会标记所有从根对象(通常是全局对象)可达的对象,然后清除未标记的对象。即使存在循环引用,只要这些对象不再从根对象可达,它们最终都会被清除。
注意事项
解除不必要的引用:在不需要时,手动解除对象之间的引用可以帮助垃圾回收器更快地回收内存。
使用弱引用:在ES6中,引入了
WeakMap
和WeakSet
,它们允许创建对对象的弱引用。这些引用不会阻止垃圾回收器回收对象。内存分析工具:使用Chrome开发者工具中的内存分析工具可以帮助你检测内存泄漏和不必要的循环引用。
Javascript深度遍历(递归与迭代[栈])与广度遍历 (队列)
在JavaScript中,要判断一个对象是否存在循环引用,你可以使用一种称为“深度遍历”或“深度搜索”的技术,同时跟踪你已经访问过的对象。如果在遍历过程中你遇到了一个已经访问过的对象,那么就说明存在循环引用。
下面是一个具体的代码示例,它使用了一个WeakSet
来跟踪已经访问过的对象,并递归地检查对象的属性来确定是否存在循环引用:
function hasCircularReference(obj, visited = new WeakSet()) { // 如果对象是null或undefined,或者已经访问过,则返回false if (!obj || visited.has(obj)) { return false; } // 将当前对象标记为已访问 visited.add(obj); // 遍历对象的所有可枚举属性 for (let key in obj) { if (obj.hasOwnProperty(key)) { let value = obj[key]; // 如果属性值是对象,则递归检查 if (typeof value === 'object' && value !== null) { // 如果在已访问的对象中找到了当前属性值,则说明存在循环引用 if (visited.has(value)) { return true; } // 递归检查属性值 if (hasCircularReference(value, visited)) { return true; } } } } // 如果没有找到循环引用,则返回false return false;
} // 示例用法
let obj1 = {};
let obj2 = {};
obj1.a = obj2;
obj2.b = obj1; console.log(hasCircularReference(obj1)); // 输出: true let obj3 = {};
console.log(hasCircularReference(obj3)); // 输出: false
- 函数定义:
- 定义一个名为
hasCircularReference
的函数,它接受两个参数:要检查的对象obj
和一个可选的visited
集合(默认为一个新的WeakSet
)。
- 定义一个名为
- 基础情况处理:
- 如果
obj
是null
或undefined
,或者已经存在于visited
集合中,则直接返回false
。这是因为null
和undefined
不是对象,不会形成循环引用,而已经访问过的对象则表明我们已经遍历过它的属性,无需再次检查。
- 如果
- 标记当前对象:
- 将当前对象
obj
添加到visited
集合中,表示它已被访问。
- 将当前对象
- 遍历对象属性:
- 使用
for...in
循环遍历对象的所有可枚举属性。这里需要注意的是,我们只检查对象自身的属性(使用hasOwnProperty
方法),而不包括从原型链上继承的属性。
- 使用
- 递归检查属性值:
- 对于每个属性,如果其值是对象(且不是
null
),则递归调用hasCircularReference
函数,将属性值作为新的对象传入,并传递当前的visited
集合。 - 如果在递归调用过程中发现属性值已经存在于
visited
集合中,则说明找到了循环引用,函数返回true
。
- 对于每个属性,如果其值是对象(且不是
- 完成遍历:
- 如果遍历完所有属性后都没有找到循环引用,则函数返回
false
。
- 如果遍历完所有属性后都没有找到循环引用,则函数返回
2.如何使用JS实现日期格式化函数?请写出具体代码
在JavaScript中,你可以使用内置的Date
对象以及自定义的函数来实现日期格式化。以下是一个简单的日期格式化函数的实现,它允许你将日期对象格式化为指定的字符串格式。
function formatDate(date, formatString) { const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; const padZero = (num) => num.toString().padStart(2, '0'); const formatOptions = { 'YYYY': date.getFullYear(), 'MM': padZero(date.getMonth() + 1), // Months are zero based 'DD': padZero(date.getDate()), 'dddd': days[date.getDay()], 'MMM': months[date.getMonth()], 'HH': padZero(date.getHours()), 'mm': padZero(date.getMinutes()), 'ss': padZero(date.getSeconds()) }; return formatString.replace(/YYYY|MM|DD|dddd|MMM|HH|mm|ss/g, (match) => formatOptions[match]);
} // 使用示例
const now = new Date();
console.log(formatDate(now, 'YYYY-MM-DD HH:mm:ss')); // 输出类似 "2023-10-05 14:30:00"
console.log(formatDate(now, 'dddd, MMM DD, YYYY HH:mm:ss')); // 输出类似 "Thu, Oct 05, 2023 14:30:00"
代码解释
-
定义日期和月份的数组:
days
数组包含星期的缩写,months
数组包含月份的缩写。 -
辅助函数
padZero
:这个函数用于确保数字始终是两位数。如果数字小于10,它会在前面添加一个零。 -
定义
formatOptions
对象:这个对象包含要替换的格式字符串中的键和对应的日期值。注意,月份是从0开始的,所以date.getMonth() + 1
是正确的月份。 -
使用
replace
方法和正则表达式:formatString.replace(/YYYY|MM|DD|dddd|MMM|HH|mm|ss/g, (match) => formatOptions[match])
这行代码会查找formatString
中所有的格式占位符(如YYYY
、MM
等),并将它们替换为formatOptions
对象中对应的值。 -
使用示例:创建了一个
Date
对象now
,并使用formatDate
函数将其格式化为不同的字符串格式。
你可以根据需要扩展这个函数,添加更多的格式选项,比如毫秒(mss
)、时区信息等。这个基本的实现应该能满足大多数常见的日期格式化需求。
3.如何使用JS实现字符串的repeat方法?请写出具体代码
Javascript中Repeat方法
console.log('abc'.repeat(3)); // 输出 "abcabcabc" console.log('Hello'.repeat(2)); // 输出 "HelloHello" console.log(' '.repeat(4)); // 输出 " "(四个空格) console.log('A'.repeat(0)); // 输出 ""(空字符串) console.log('B'.repeat(-1)); // 输出 ""(空字符串) console.log('C'.repeat(2.5)); // 输出 "CC"(2.5 被截断为 2)
在JavaScript中,String.prototype.repeat()
方法用于将字符串重复指定的次数,并返回一个新的字符串。但是,如果你想要自己实现一个类似的方法,你可以编写一个函数来模拟这个行为。
以下是一个简单的实现,名为 myRepeat
,它接受两个参数:要重复的字符串 str
和重复的次数 count
,并返回重复后的新字符串:
function myRepeat(str, count) { // 如果 count 不是正整数,则返回空字符串 if (count < 1 || !Number.isInteger(count)) { return ''; } // 使用空字符串作为累加器 let result = ''; // 使用循环将字符串重复 count 次 for (let i = 0; i < count; i++) { result += str; } // 返回重复后的字符串 return result;
} // 使用示例
console.log(myRepeat('abc', 3)); // 输出 "abcabcabc"
console.log(myRepeat('Hello, ', 2)); // 输出 "Hello, Hello, "
console.log(myRepeat('A', 0)); // 输出 ""
console.log(myRepeat('B', -1)); // 输出 ""
console.log(myRepeat('C', 2.5)); // 输出 "",因为2.5不是整数
4.如何使用JS实现字符串翻转?请写出具体代码
在JavaScript中,你可以通过多种方式实现字符串的翻转。以下是一个简单的实现方法,它使用了JavaScript的数组功能来轻松地反转字符串:
function reverseString(str) { // 将字符串转换为数组(因为字符串在JavaScript中是不可变的) let strArray = str.split(''); // 使用数组的reverse方法反转数组中的元素顺序 strArray.reverse(); // 使用数组的join方法将反转后的数组元素重新组合成字符串 let reversedStr = strArray.join(''); // 返回翻转后的字符串 return reversedStr;
} // 使用示例
console.log(reverseString('hello')); // 输出 'olleh'
console.log(reverseString('world')); // 输出 'dlrow'
console.log(reverseString('JavaScript')); // 输出 'tpircSavaJ'
代码解释
-
字符串转换为数组:
str.split('')
将字符串str
分割成一个字符数组strArray
。每个字符现在都是数组中的一个元素。 -
数组反转:
strArray.reverse()
方法会原地(in-place)反转数组中元素的顺序。这意味着strArray
本身会被修改,其元素顺序将变为相反的顺序。 -
数组转换回字符串:
strArray.join('')
方法将strArray
中的元素重新组合成一个单一的字符串reversedStr
。因为我们在join
方法中传递了空字符串''
作为分隔符,所以数组中的元素会被直接连接在一起,没有额外的字符或空格。 -
返回结果:最后,函数返回翻转后的字符串
reversedStr
。
这种方法利用了JavaScript数组的强大功能,使得字符串翻转变得简单且易于理解。当然,还有其他方法可以实现字符串翻转,比如使用循环或递归,但上面的方法通常是最简洁和高效的。
//reduce方法
function reverseStringReduce(str) { return str.split('').reduce((reversed, char) => char + reversed, '');
} // 使用示例
console.log(reverseStringReduce('hello')); // 输出: 'olleh'
---------------------------------------------------------------------------
//for循环
function reverseStringLoop(str) { let reversed = ''; for (let i = str.length - 1; i >= 0; i--) { reversed += str[i]; } return reversed;
} // 使用示例
console.log(reverseStringLoop('hello')); // 输出: 'olleh'
5.如何用JS实现将数字每干分位用逗号隔开(1000转为1,000)?请写出具体代码
步骤:
1)将数字转换为字符串,以便使用字符串方法进行处理。
2)使用正则表达式匹配字符串中的位置,在每三个数字前插入一个逗号。
3)返回格式化后的字符串。
function formatNumberWithCommasCustom(number) { // 将数字转换为字符串,并去掉可能的小数点 let str = Math.floor(number).toString(); // 初始化结果字符串和一个计数器 let result = ''; let count = 0; // 从字符串的最后一个字符开始遍历 for (let i = str.length - 1; i >= 0; i--) { // 将当前字符添加到结果字符串的前面 result = str[i] + result; // 每添加一个字符,计数器加1 count++; // 如果计数器达到3(意味着已经添加了3个字符),则插入一个逗号,并重置计数器 if (count === 3 && i !== 0) { result = ',' + result; count = 0; } } // 如果原始数字有小数部分,则将其添加到结果字符串的后面 if (number % 1 !== 0) { result += '.' + (number - Math.floor(number)).toFixed(2).slice(2); // 保留两位小数 } return result;
} // 使用示例(注意:自定义函数在处理小数时可能不如toLocaleString()准确)
console.log(formatNumberWithCommasCustom(1000)); // 输出: "1,000"
console.log(formatNumberWithCommasCustom(1234567)); // 输出: "1,234,567"
console.log(formatNumberWithCommasCustom(1234567.89)); // 输出可能是 "1,234,567.89"(但这种方法在处理小数时稍显笨拙)
6.如何使用JS + HTML 实现图片懒加载?请写出具体代码
图片懒加载
一、定义与原理
图片懒加载,也称为延迟加载,是指在单页面应用中,当用户滚动页面到图片位置时才加载图片的一种技术。其原理主要是利用JavaScript或CSS来判断图片是否进入用户的可视区域,从而决定是否加载图片。当用户滚动页面时,通过监听滚动事件,可以判断哪些图片进入了可视区域,并将这些图片的src属性替换为真实的图片地址,从而实现图片的加载。
二、实现方式
图片懒加载的实现方式有多种,以下是几种常见的实现方法:
- 设置img标签的loading属性:
- 在HTML中,可以直接为img标签设置loading="lazy"属性,这样浏览器就会延迟加载屏幕外的图像,直到用户滚动到它们附近。
- 这种方法简单且兼容性好,是现代浏览器推荐的实现方式。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Lazy Loading Example</title> </head> <body> <img src="placeholder.jpg" data-src="real-image.jpg" alt="Example Image" loading="lazy"> <!-- 其他内容 --> </body> </html>
- 使用JavaScript监听滚动事件:
- 通过JavaScript监听页面的滚动事件,可以判断哪些图片进入了可视区域。
- 一旦图片进入可视区域,就将其src属性替换为真实的图片地址。
- 这种方法需要手动计算图片的位置和视口的高度,并且滚动事件可能频繁触发,因此需要注意性能优化。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Lazy Loading Example</title> <style> .lazy { display: block; width: 100%; height: 200px; /* 设置占位高度 */ background: #f0f0f0; /* 占位背景色 */ } </style> </head> <body> <img class="lazy" data-src="real-image1.jpg" alt="Example Image 1"> <img class="lazy" data-src="real-image2.jpg" alt="Example Image 2"> <!-- 其他内容 --> <script> document.addEventListener('DOMContentLoaded', function() { let lazyImages = [].slice.call(document.querySelectorAll('img.lazy')); let active = false; const lazyLoad = function() { if (active === false) { active = true; setTimeout(function() { lazyImages.forEach(function(lazyImage) { if ((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display !== 'none') { lazyImage.src = lazyImage.dataset.src; lazyImage.classList.remove('lazy'); lazyImages = lazyImages.filter(function(image) { return image !== lazyImage; }); if (lazyImages.length === 0) { document.removeEventListener('scroll', lazyLoad); window.removeEventListener('resize', lazyLoad); window.removeEventListener('orientationChange', lazyLoad); } } }); active = false; }, 200); } }; document.addEventListener('scroll', lazyLoad); window.addEventListener('resize', lazyLoad); window.addEventListener('orientationChange', lazyLoad); }); </script> </body> </html>
- 使用IntersectionObserver API:
- IntersectionObserver是浏览器提供的一个API,用于异步观察目标元素与其祖先元素或顶级文档的视口相交情况的变化。
- 通过这个API,可以轻松地判断图片是否进入视口,并在进入视口时加载图片。
- 这种方法性能更好,且不需要手动计算元素位置。但需要注意的是,它可能不兼容旧的浏览器版本。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Lazy Loading Example</title> <style> .lazy { display: block; width: 100%; height: 200px; /* 设置占位高度 */ background: #f0f0f0; /* 占位背景色 */ } </style> </head> <body> <img class="lazy" data-src="real-image1.jpg" alt="Example Image 1"> <img class="lazy" data-src="real-image2.jpg" alt="Example Image 2"> <!-- 其他内容 --> <script> document.addEventListener('DOMContentLoaded', function() { let lazyImages = document.querySelectorAll('img.lazy'); let lazyImageObserver = new IntersectionObserver(function(entries, observer) { entries.forEach(function(entry) { if (entry.isIntersecting) { //视口目标对象let lazyImage = entry.target; //获取图片路径lazyImage.src = lazyImage.dataset.src; lazyImage.classList.remove('lazy'); //移除观察lazyImageObserver.unobserve(lazyImage); } }); }); lazyImages.forEach(function(lazyImage) { lazyImageObserver.observe(lazyImage); }); }); </script> </body> </html> ————————————————————————————————————————————————————————————————————————————————————————— // 创建一个IntersectionObserver实例 const observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { console.log('进入视窗'); // 在此处执行相关操作,如加载内容等 } else { console.log('离开视窗'); } }); }, { root: null, // 使用视窗作为根元素 rootMargin: '0px', threshold: [0, 0.25, 0.5, 0.75, 1] // 当目标元素0%、25%、50%、75%、100%可见时触发回调函数 }); // 获取需要观察的元素 const targetElement = document.querySelector('#target'); // 开始观察该元素 observer.observe(targetElement);
三、优点与缺点
优点:
- 提高页面加载速度:通过延迟加载非可视区域的图片,可以减少页面的初始加载时间。
- 节省用户流量:用户只加载他们实际看到的图片,从而节省了流量。
- 提升用户体验:对于网络条件较差的用户或移动设备用户来说,懒加载可以显著地提高用户体验。
缺点:
- 兼容性问题:虽然现代浏览器大多支持懒加载技术,但仍有一些旧版本的浏览器可能不兼容。
- 实现复杂度:使用JavaScript监听滚动事件或IntersectionObserver API实现懒加载需要一定的编程技能
7.如何使用JS实现非负大整数的相加?请写出具体代码
在JavaScript中,由于Number
类型只能安全地表示到 ±9007199254740991(2^53-1)
(即 Number.MAX_SAFE_INTEGER
)的整数,对于超过这个范围的非负大整数相加,我们需要使用其他方法。
一种常见的方法是使用字符串来表示这些大整数,并编写一个函数来模拟大整数的相加过程。以下是一个实现非负大整数相加的JavaScript代码示例:
function addLargeNumbers(num1, num2) { // 去除字符串两端的空格,并反转字符串以便从低位开始相加[key1]let str1 = num1.trim().split('').reverse().join(''); let str2 = num2.trim().split('').reverse().join(''); let maxLength = Math.max(str1.length, str2.length); let result = ''; let carry = 0; // 进位 // 逐位相加 for (let i = 0; i < maxLength; i++) { let digit1 = i < str1.length ? parseInt(str1[i]) : 0; let digit2 = i < str2.length ? parseInt(str2[i]) : 0; let sum = digit1 + digit2 + carry;//[key2] result += sum % 10; // 当前位的值 carry = Math.floor(sum / 10); // 进位 } // 如果最后还有进位,需要加到结果的最前面[长度相同时,key3]if (carry > 0) { result = carry.toString() + result; } // 反转结果字符串,得到最终的大整数相加结果 return result.split('').reverse().join('');
} // 测试
let num1 = "12345678901234567890";
let num2 = "98765432109876543210";
console.log(addLargeNumbers(num1, num2)); // 输出 "111111111011111111100"
--------------------------------------------------------------------------------------
function addLargeNumbersWithBigInt(num1, num2) { // 将字符串转换为BigInt let bigInt1 = BigInt(num1); let bigInt2 = BigInt(num2); // 进行相加运算 let result = bigInt1 + bigInt2; // 将结果转换回字符串(如果需要) return result.toString();
} // 测试
let num1 = "12345678901234567890";
let num2 = "98765432109876543210";
console.log(addLargeNumbersWithBigInt(num1, num2)); // 输出 "111111111011111111100"