牛客-js45题

牛客-js能力测评-45题

项目地址:https://www.nowcoder.com/ta/js-assessment

image-20240308204749426

文章目录

JS2 数组求和

/**
 * 描述
 * 计算并返回给定数组 arr 中所有元素的总和
 * 输入描述:
 * [ 1, 2, 3, 4 ]
 * 输出描述:
 * 10
 * 示例1
 * 输入:
 *
 * [ 1, 2, 3, 4 ]
 *
 * 输出:
 *
 * 10
 */


const arr =[ 1, 2, 3, 4 ];

/**
 * 方法1:of
 * @returns {number}
 */

function getSum(){
    var sum=0;
    for (let i of arr){
        sum=sum+i;
    }
    return sum;
}

console.log(getSum())

/**
 * 方法2:in
 */
function getSum2(){
    var sum=0;
    for (let i in arr){
        sum+=arr[i];
    }
    return sum;
}

console.log(getSum2())

JS3 移除数组中的元素

/**
 * 移除数组 arr 中的所有值与 item 相等的元素。不要直接修改数组 arr,结果返回新的数组
 * @type {number[]}
 */
const arr=[1, 2, 3, 4, 2];
const item=2;


/**
 * 方法1
 * @param arr
 * @param item
 */
// function remove(arr, item) {
//     const arr2=arr.map(function (data){
//         if(data!==item){
//             return data
//         }else{
//
//         }
//     })
//     console.log(arr2)
// }
// remove(arr, item);//[ 1, undefined, 3, 4, undefined ]

function remove(arr, item) {
    const arr2=arr.filter(data=>{
        return data!==item;
    });
    console.log(arr2)
}
remove(arr,item)

JS4 移除数组中的元素

/**
 * 描述
 * 移除数组 arr 中的所有值与 item 相等的元素,直接在给定的 arr 数组上进行操作,并将结果数组返回
 * 示例1
 *
 * 输入:
 * [1, 2, 2, 3, 4, 2, 2], 2
 *
 * 输出:
 * [1, 3, 4]
 */
function removeWithoutCopy(arr, item) {
    for(let i=arr.length-1;i>=0;i--){
        if(arr[i]===item){
            arr.splice(i,1);
        }
    }
    return arr;
}
const arr = [1, 2, 2, 3, 4, 2, 2];
const itemToRemove = 2;
const result = removeWithoutCopy(arr, itemToRemove);
console.log(result); // 输出: [1, 3, 4]

splice() 方法是 JavaScript 中一个强大而灵活的方法,用于在数组中添加、删除或替换元素,直接修改原数组。这里是 splice() 方法参数的完整介绍和使用方法:

1、参数

start:

  • 数字,指定修改的开始位置。
  • 如果大于数组长度,实际开始位置将被设置为数组长度,即在数组末尾开始操作。
  • 如果为负值,表示从数组末尾开始向前计算的位置。例如,-1 表示数组的最后一个元素。
  • 如果绝对值大于数组的长度,开始位置则为 0。

deleteCount (可选):

  • 指定要从数组中删除的元素数量。
  • 如果省略或其值大于 start 之后的元素总数,则从 start 到数组末尾的所有元素都会被删除。
  • 如果设置为 0 或负数,则不会删除任何元素。这种情况下,至少需要添加一个新元素。

item1, item2, ... (可选):

  • 要添加到数组中的元素,从 start 位置开始。
  • 如果不指定任何元素,则 splice() 只会删除数组中的元素。

2、返回值

返回一个包含被删除元素的数组。如果没有元素被删除,则返回一个空数组。

3、使用方法

删除元素

let arr = [1, 2, 3, 4, 5];
arr.splice(2, 1); // 从索引2开始删除1个元素,arr 变为 [1, 2, 4, 5]

添加元素

javascript
let arr = [1, 2, 4, 5];
arr.splice(2, 0, 3); // 从索引2开始,删除0个元素,添加3,arr 变为 [1, 2, 3, 4, 5]

替换元素

javascript
let arr = [1, 2, 4, 5];
arr.splice(2, 1, 3); // 从索引2开始,删除1个元素,并添加3,arr 变为 [1, 2, 3, 5]

删除并添加(替换)元素

javascript
let arr = [1, 2, 3, 4, 5];
arr.splice(1, 2, 'a', 'b', 'c'); // 从索引1开始,删除2个元素,添加'a', 'b', 'c',arr 变为 [1, 'a', 'b', 'c', 4, 5]

使用负数索引

let arr = [1, 2, 3, 4, 5];
arr.splice(-2, 1); // 从数组末尾第二个元素开始删除1个元素,arr 变为 [1, 2, 3, 5]

splice() 方法直接在原数组上进行操作,修改原数组的内容,而不仅仅是返回一个新数组。这使得它在处理数组时非常灵活和有用。

JS5 添加元素

在JavaScript中,数组和对象等复杂数据类型是通过引用来操作的。这意味着当你将一个数组赋值给另一个变量时,这两个变量实际上指向的是内存中同一个数组。因此,如果你通过任何一个变量修改了这个数组,那么这个修改对另一个变量也是可见的。这就是所谓的"修改原始数组"。

然而,arr.slice() 方法用于创建一个数组的浅拷贝。调用此方法时,会创建一个新的数组对象,并将原数组中的元素复制到新数组中。因此,newArrarr 指向的是内存中的两个不同的数组对象。在 newArr 上进行的任何修改(比如使用 push 方法添加元素)都不会影响到原始数组 arr

1、何时会修改原始数组:

  1. 直接使用数组方法修改数组:比如 push(), pop(), shift(), unshift(), splice() 等方法直接在原数组上进行操作,会修改原数组。
  2. 通过索引直接修改数组元素:比如 arr[0] = 5 会直接修改 arr 数组的第一个元素。

2、何时不会修改原始数组:

  1. 使用返回新数组的方法:比如 slice(), map(), filter(), concat() 等方法返回一个新数组,不会修改原始数组。
  2. 使用展开运算符(…)创建数组副本let newArr = [...arr]; 这样也不会修改原始数组,因为它创建了一个原数组的浅拷贝。

因此,要区分何时会修改原始数组的关键在于理解你是在直接操作原数组,还是在操作一个原数组的副本。直接操作原数组的方法和通过索引修改元素会改变原数组,而创建数组副本后对副本的任何操作都不会影响原数组。

/**
 * 描述
 * 在数组 arr 末尾添加元素 item。结果返回新的数组。
 * 注意:不要直接修改数组 arr!!!
 * 输入描述:
 * [1, 2, 3, 4],  10
 * 输出描述:
 * [1, 2, 3, 4, 10]
 */
// function removeWithoutCopy(arr, item) {
//     const item_arr=[...arr,item];
//     return item_arr;
// }
function removeWithoutCopy(arr, item) {
    const newArr=arr.slice();
    newArr.push(item);
    return  newArr;
}
const arr = [1, 2, 3, 4];
const itemToRemove = 10;
const result = removeWithoutCopy(arr, itemToRemove);
console.log(result); // 输出: [1, 2, 3, 4, 10]

JS6 删除数组最后一个元素

/**
 * 描述
 * 删除数组 arr 最后一个元素。不要直接修改数组 arr,结果返回新的数组
 * 示例1
 * 输入:
 * [1, 2, 3, 4]
 * 输出:
 * [1, 2, 3]
 */

function truncate(arr) {
    const res =arr.slice();
    res.splice(-1,1); //删除末尾元素
    return res;
    // res.pop();
    // return res;
}
const arr = [1, 2, 3, 4];
const result = truncate(arr);
console.log(result); // 输出: [1, 2, 3,]

通过使用 slice() 方法创建原数组 arr 的一个浅拷贝到 res,然后在这个拷贝上调用 splice(-1, 1) 来移除数组的最后一个元素,你的 truncate 函数成功地返回了一个新数组,该数组不包含原数组的最后一个元素,并且没有直接修改原数组。

这里是函数的工作流程:

  1. const res = arr.slice(); 创建了数组 arr 的一个完整拷贝 res。此时,resarr 包含相同的元素,但是是两个独立的数组对象。
  2. res.splice(-1,1); 通过指定 -1start 参数,从数组 res 的末尾移除最后一个元素。splice 方法修改了 res 数组本身,但由于 res 是原数组 arr 的拷贝,原数组不受影响。
  3. return res; 返回了修改后的数组 res,它不包括原数组的最后一个元素。

最终,当你打印函数的返回值时,你会得到一个不包含原数组最后一个元素的新数组,例如,对于原数组 [1, 2, 3, 4],函数返回 [1, 2, 3],这符合预期结果。这个方法有效且满足题目的要求,不直接修改输入的数组 arr 而返回一个新的数组。

1、slice 方法

slice 方法返回数组的一个浅拷贝,从原数组中的一个子集创建一个新数组。它不会修改原数组。

  • 参数

    • begin(可选):提取起始处的索引(包含该元素),默认值为 0。如果该值为负数,则表示倒数的索引。
    • end(可选):提取结束处的索引(不包含该元素)。默认值为数组的长度。如果该值为负数,则表示倒数的索引。

2 、splice 方法

splice 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。这个方法会改变原数组。

  • 参数

    • start:指定修改的开始位置。
    • deleteCount(可选):要删除的元素数量。如果省略,或者其值大于等于 start 之后的元素总数,则从 start 后的所有元素都会被删除。
    • item1, item2, ...(可选):要添加进数组的元素,从 start 位置开始

JS7 添加元素

/**
 * 描述
 * 在数组 arr 开头添加元素 item。不要直接修改数组 arr,结果返回新的数组
 * 示例1
 * 输入:
 *
 * [1, 2, 3, 4], 10
 *
 * 输出:
 *
 * [10, 1, 2, 3, 4]
 */
function prepend(arr, item) {
    const  res=arr.slice();
    res.splice(0,0,item);
    return res;

}
const arr = [1, 2, 3, 4];
const itemToRemove = 10;
const result = prepend(arr, itemToRemove);
console.log(result); // 输出: [ 10, 1, 2, 3, 4]

JS8 删除数组第一个元素

/**
 * 描述
 * 删除数组 arr 第一个元素。不要直接修改数组 arr,结果返回新的数组
 * 输入描述:
 * [1, 2, 3, 4]
 * 输出描述:
 * [2, 3, 4]
 */
function curtail(arr) {
    const  res=arr.slice();
    res.splice(0,1);
    return res;

}
const arr = [1, 2, 3, 4];
const result = curtail(arr);
console.log(result); // 输出: [2, 3, 4]

JS9 数组合并

/**
 * 描述
 * 合并数组 arr1 和数组 arr2。不要直接修改数组 arr,结果返回新的数组
 * 输入描述:
 * [1, 2, 3, 4], ['a', 'b', 'c', 1]
 * 输出描述:
 * [1, 2, 3, 4, 'a', 'b', 'c', 1]
 */

function concat(arr1, arr2) {
    const res=[...arr1,...arr2];
    // const c_arr1=arr1.concat(arr2);
    // console.log(c_arr1);
    return res
}
const arr1 = [1, 2, 3, 4];
const arr2 = ['a', 'b', 'c', 1];
const result = concat(arr1,arr2);
console.log(result); // 输出: [1, 2, 3, 4, 'a', 'b', 'c', 1]


JS10 添加元素

/**
 * 描述
 * 在数组 arr 的 index 处添加元素 item。不要直接修改数组 arr,结果返回新的数组
 * 示例1
 * 输入:
 *
 * [1, 2, 3, 4], 'z', 2
 *
 * 输出:
 *
 * [1, 2, 'z', 3, 4]
 */

function createInterface(arr , data, index){
    const res=arr.slice();
    res.splice(index,0,data);
    return res;

}
const arr=[1, 2, 3, 4];
const data= 'z';
const index=2;

const result=createInterface(arr , data, index);
console.log(result) //[ 1, 2, 'z', 3, 4 ]

JS11 计数

/**
 * 描述
 * 统计数组 arr 中值等于 item 的元素出现的次数
 * 示例1
 * 输入:
 *
 * [1, 2, 4, 4, 3, 4, 3], 4
 *
 * 输出:
 *
 * 3
 */

function count(arr, item){
    let sum=0
    arr.forEach(data=>{
        if(data===item){
            sum++;
        }
    })
    return sum;
}

const arr=[1, 2, 4, 4, 3, 4, 3];
const item= 4;

const result=count(arr,item);
console.log(result) //3

JS12 查找重复元素

/**
 * 描述
 * 找出数组 arr 中重复出现过的元素(不用考虑返回顺序)
 * 示例1
 * 输入:
 *
 * [1, 2, 4, 4, 3, 3, 1, 5, 3]
 *
 * 输出:
 *
 * [1, 3, 4]
 * @param arr
 */

function duplicates(arr) {
    const seen = new Set();
    const duplicates = new Set();

    for (let i = 0; i < arr.length; i++) {
        if (seen.has(arr[i])) {
            duplicates.add(arr[i]);
        } else {
            seen.add(arr[i]);
        }
    }

    return Array.from(duplicates);
}

const arr = [1, 2, 4, 4, 3, 3, 1, 5, 3];
const result = duplicates(arr);
console.log(result); // [4, 3, 1]

在JavaScript中,Set对象是ES6引入的一种新的数据结构,它允许存储任何类型的唯一值,无论是原始值或者是对象引用。Set对于处理非重复值的集合非常有用。以下是关于Set及其常用操作方法(如addhas)的关键点介绍,以及解释为什么有时需要将Set转换为数组。

1、Set

  • 唯一性Set内部的值都是唯一的,没有重复的值。这意味着向Set添加一个已经存在的值不会有任何效果。
  • 数据结构Set是一个集合数据结构,用于存储不重复的值。它支持各种操作,如添加、删除和遍历其元素。

2、add 方法

  • 用途add方法用于向Set对象添加一个新的元素。如果尝试添加的元素已经存在于Set中,则不会添加该元素,保持Set的唯一性不变。
  • 语法setObj.add(value)value是要添加到Set中的元素。

3、has 方法

  • 用途has方法用于检查Set对象是否包含一个指定的值。如果该值存在于Set中,则返回true;否则返回false
  • 语法setObj.has(value)value是要检查是否存在于Set中的元素。

4、Array.from 方法

  • 用途Array.from方法用于从一个类似数组或可迭代对象中创建一个新的、浅拷贝的数组实例。这在需要将Set转换为数组时特别有用,因为Set本身不支持数组的一些操作方法,如mapfilter等。
  • 为什么需要转换:虽然Set提供了一种高效的方式来存储唯一值,但它并不支持所有数组操作。将Set转换为数组可以让我们利用数组的丰富方法进行操作,比如排序(sort)、搜索(indexOffindincludes)等,或者是简单地需要一个数组格式来与其他API交云。

5、Set 对象的常见操作

除了addhasSet对象还支持以下操作:

  • delete(value):移除Set中与这个值相等的元素,如果该元素存在,则返回true;否则返回false
  • clear():移除Set内的所有元素。
  • forEach(callbackFn, thisArg):按插入顺序,为Set对象中的每一个值执行一次提供的回调函数。
  • size:属性返回Set对象中元素的数量。

总结

Set对象是处理集合中唯一值的强大工具,addhas方法提供了基础但实用的操作来管理这些值。然而,由于Set不支持数组的全部操作,Array.from方法成为了一种方便的工具,以将Set转换成数组,进一步扩展了其用途。这样,开发者可以在保持数据唯一性的同时,享受到数组操作的便利。

JS13 求二次方

/**
 *
 * 描述
 * 为数组 arr 中的每个元素求二次方。不要直接修改数组 arr,结果返回新的数组
 * 输入描述:
 * [1, 2, 3, 4]
 * 输出描述:F
 * [1, 4, 9, 16]
 * 示例1
 * 输入:
 *
 * [1, 2, 3, 4]
 *
 * 输出:
 *
 * [1, 4, 9, 16]
 */

function square(arr) {
    const new_arr= arr.slice();
    const res=new_arr.map(data=>{
        //return data**2;
        return data*data;
    })
    return res;
}

const arr = [1, 2, 3, 4];
const result = square(arr);
console.log(result); // [4, 3, 1]

JS14 查找元素位置

/**
 * 描述
 * 在数组 arr 中,查找值与 item 相等的元素出现的所有位置
 * 示例1
 * 输入:
 *
 * ['a','b','c','d','e','f','a','b','c'] 'a'
 *
 * 输出:
 *
 * [0, 6]
 *
 * @param arr
 * @param target
 * @returns {*[]}
 */


function findAllOccurrences(arr, target) {
    const res=[];
    for(let i in arr){
        if(arr[i]===target){
            res.push(i);
        }
    }
    return res;
}
const arr = ['a','b','c','d','e','f','a','b','c'];
const target='a';
const result = findAllOccurrences(arr,target);
console.log(result);

JS15 避免全局变量

/**
 * 描述
 * 给定的 js 代码中存在全局变量,请修复
 */
function globals() {
    // myObject = {
    //     name : 'Jory'
    // };
    const myObject = {
        name : 'Jory'
    };
}
globals();

这段代码涉及到JavaScript中变量作用域的概念,特别是全局变量和局部变量的区别。让我们一一解释你的问题:

1 默认变量的作用域

globals()函数中,myObject1没有使用varletconst关键字声明,因此它默认成为一个全局变量,即使它是在函数内部定义的。这就是为什么在函数外部仍然可以访问myObject1的原因。当你在函数内部创建变量时,如果不使用varletconst来声明,该变量自动被视为全局变量(假设没有同名的全局变量),并且可以在函数外部访问。

这种行为在严格模式(使用'use strict')下是不允许的,因为严格模式要求你显式声明变量。

2. var 作用域

使用var关键字在函数内部声明的变量是局部变量,它只能在函数内部被访问和修改。这意味着它不是全局变量,不能从函数外部直接访问。只有当变量在函数外部或没有在任何函数内部使用varletconst声明时,它才是全局变量。

然而,如果var关键字用于函数外部的根级别声明变量,那么这个变量是全局的,可以在代码的任何地方访问。

举个例子来说明:

javascript
function myFunction() {
    var localVariable = "I'm local";
    globalVariable = "I'm global";
}

myFunction();
console.log(globalVariable); // 正常工作,因为globalVariable是全局变量。
console.log(localVariable); // 报错,localVariable未定义,因为它是局部变量。

在上面的例子中,localVariable使用var在函数内部声明,因此它是局部的,而globalVariable没有使用任何关键字声明(假设在函数外部没有声明过globalVariable),所以它成为全局变量。

总的来说,函数内部声明的变量(使用varletconst)是局部变量,不可以直接从外部访问;而未使用这些关键字声明的变量(并且在全局作用域中没有同名变量)会成为全局变量,可以在函数外部访问。

JS17 正确的使用 parseInt

[JavaScript parseInt()方法详解_js parseint-CSDN博客](https://blog.csdn.net/Foggy_L/article/details/125014391?ops_request_misc=%7B%22request%5Fid%22%3A%22170970873216800213010288%22%2C%22scm%22%3A%2220140713.130102334…%22%7D&request_id=170970873216800213010288&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-2-125014391-null-null.142v99pc_search_result_base3&utm_term=JavaScript 的 parseInt() 函数&spm=1018.2226.3001.4187)

/**
 * 描述
 * 修改 js 代码中 parseInt 的调用方式,使之通过全部测试用例
 * 示例1
 * 输入:'12'
 * 输出:12
 * 示例2
 * 输入:'12px'
 * 输出:12
 * 示例3
 * 输入:'0x12'
 * 输出:0
 * @param num
 * @returns {number}
 */

function parse2Int(num) {
    return parseInt(num,10);
}
const str1='12';
const res1=parse2Int(str1);
console.log(res1)
const str2='12px';
const res2=parse2Int(str2);
console.log(res2)
const str3='0x12';
const res3=parse2Int(str3);
console.log(res3)

parseInt函数是JavaScript中的一个全局函数,用于将字符串转换成整数。这个函数对于处理和转换数据类型非常有用,特别是在你需要从字符串中提取数值时。下面是parseInt函数的基本用法和一些关键点的介绍:

1、基本语法

parseInt(string, radix);
  • string:要被解析成整数的字符串。
  • radix(可选):解析时使用的基数(即数学中的进制),取值范围是2到36。如果不提供这个参数,parseInt会根据字符串来推测基数:如果字符串以"0x"或"0X"开头,基数是16(十六进制);如果字符串以"0"开头,基数可能是8(八进制)或10(十进制),这取决于不同的JavaScript引擎;如果字符串以其他任何值开头,基数是10。

2、返回值

  • 如果parseInt成功解析输入字符串,它会返回一个整数。返回的整数是按照指定基数解析的。
  • 如果解析失败(即,字符串不包含可解析的数字),parseInt会返回NaN(不是一个数字)。

3、使用示例

javascript
console.log(parseInt("10"));       // 返回 10
console.log(parseInt("19", 10));   // 返回 19,基数是10
console.log(parseInt("1011", 2));  // 返回 11,基数是2,因此按二进制解析
console.log(parseInt("0xF", 16));  // 返回 15,基数是16,因此按十六进制解析
console.log(parseInt("20", 8));    // 返回 16,基数是8,因此按八进制解析
console.log(parseInt("zxv", 36));  // 返回 44027,基数是36
console.log(parseInt("20.5"));     // 返回 20,因为parseInt只解析整数部分
console.log(parseInt("Hello"));    // 返回 NaN,因为字符串不含可解析的数字

4、关键点

  • 如果字符串包含数字后跟非数字字符,parseInt会解析直到遇到非数字字符为止的部分。例如,parseInt("123abc")会返回123。
  • 强烈推荐在使用parseInt时提供radix参数,以避免不同环境下可能产生的歧义。
  • 对于浮点数字符串,parseInt只会解析并返回整数部分。
  • parseInt是解析字符串为整数的好工具,但如果你要解析为浮点数,则应使用parseFloat函数。

了解parseInt的这些基本用法和特性可以帮助你更有效地处理和转换数据类型,在需要时从字符串中提取整数值。

JS18 完全等同

/**
 * 描述
 * 判断 val1 和 val2 是否完全等同
 */

function identity(val1, val2) {
    return val1===val2;
}
console.log(identity(1,1));

JS19 计时器

/**
 * 描述
 * 实现一个打点计时器,要求
 * 1、从 start 到 end(包含 start 和 end),每隔 100 毫秒 console.log 一个数字,每次数字增幅为 1
 * 2、返回的对象中需要包含一个 cancel 方法,用于停止定时操作
 * 3、第一个数需要立即输出
 * @param start
 * @param end
 */

function count(start, end) {
    // 立即输出第一个数字
    console.log(start);

    // 定义一个变量来存储 setTimeout 返回的 id,以便之后取消定时器
    let timeoutId;

    // 修改为从 start + 1 开始,因为 start 已经被输出了
    for (let i = start + 1; i <= end; i++) {
        // 使用闭包来保持每次循环的 i 值
        // 注意这里使用了 let 来声明 timeoutId,确保每次循环都有自己的 timeoutId
        timeoutId = setTimeout((currentValue) => {
            console.log(currentValue);
        }, (i - start) * 100, i);
    }

    // 返回一个对象,包含一个 cancel 方法用于停止定时器
    return {
        cancel: function() {
            clearTimeout(timeoutId);
        }
    };
}

// 使用示例
const timer = count(2, 4);
// 调用 cancel 方法停止定时器
// timer.cancel();

clearTimeout 是 JavaScript 中的两个与时间控制相关的函数,它们使得开发者能够以异步方式延迟执行代码。下面是对它们的基本用法和一些细节的介绍。

1、setTimeout

setTimeout 函数用于在指定的毫秒数后执行一个函数或指定的代码片段。它的基本用法如下:

let timeoutId = setTimeout(functionToRun, delayInMilliseconds, ...args);
  • functionToRun 是延迟执行的函数。
  • delayInMilliseconds 指定了在执行函数之前等待的时间,单位是毫秒。
  • ...args 是可选的参数,这些参数会被传递给functionToRun

返回值 timeoutId 是一个数值ID,代表了这个特定的 setTimeout 调用的唯一标识符,可以用它来取消这个延时操作。

setTimeout(() => {
  console.log("This message is shown after 3 seconds.");
}, 3000);

2、clearTimeout

clearTimeout 函数用于取消一个之前通过 setTimeout 设置的延时执行。它的用法很简单:

clearTimeout(timeoutId);
  • timeoutId 是之前 setTimeout 调用的返回值。

如果 timeoutId 指向的延时执行已经发生或者没有找到对应的ID,则 clearTimeout 函数没有任何效果。

let timeoutId = setTimeout(() => {
  console.log("This message will never be shown.");
}, 5000);

clearTimeout(timeoutId); // 取消延时执行

3、使用场景和注意事项

  • 延迟执行setTimeout 常用于需要延迟执行某些操作的场景,如延迟显示提示信息、延迟执行动画等。
  • 防抖动(Debouncing):在事件处理中,如输入框的连续输入,可以使用 setTimeout 来实现防抖动功能,即在指定的延迟时间内没有再次触发事件才执行操作。
  • 取消延时执行:当你有一个延时执行的操作,但在执行之前你想取消它,clearTimeout 就非常有用。
  • 注意事项:如果你在组件或者应用卸载的过程中设置了 setTimeout,务必在组件或应用卸载时使用 clearTimeout 来取消未执行的延时操作,避免出现尝试更新已卸载组件的状态的错误。

JS20 流程控制

/**
 * 描述
 * 实现 fizzBuzz 函数,参数 num 与返回值的关系如下:
 * 1、如果 num 能同时被 3 和 5 整除,返回字符串 fizzbuzz
 * 2、如果 num 能被 3 整除,返回字符串 fizz
 * 3、如果 num 能被 5 整除,返回字符串 buzz
 * 4、如果参数为空或者不是 Number 类型,返回 false
 * 5、其余情况,返回参数 num
 * 示例1
 * 输入:
 * 15
 * 输出:
 * fizzbuzz
 * @param num
 */
function fizzBuzz(num) {
    // 检查num是否为数字
    if (typeof num !== 'number') {
        return false;
    }
    // 检查num是否同时能被3和5整除
    if (num % 3 === 0 && num % 5 === 0) {
        return 'fizzbuzz';
    }
    // 检查num是否能被3整除
    else if (num % 3 === 0) {
        return 'fizz';
    }
    // 检查num是否能被5整除
    else if (num % 5 === 0) {
        return 'buzz';
    }
    // 其他情况,返回num
    else {
        return num;
    }
}

const num = 15;
console.log(fizzBuzz(num)); // 应该输出 fizzbuzz

JS21 函数传参

/**
 * 描述
 * 将数组 arr 中的元素作为调用函数 fn 的参数
 * 示例1
 * 输入:
 *
 * function (greeting, name, punctuation) {return greeting + ', ' + name + (punctuation || '!');}, ['Hello', 'Ellie', '!']
 *
 * 输出:
 *
 * Hello, Ellie!
 */


function greet(greeting, name, punctuation) {
    return greeting + ', ' + name + (punctuation || '!');
}

const arr = ['Hello', 'Ellie', '!'];

function argsAsArray(fn, arr) {
    return fn(...arr);
}

console.log(argsAsArray(greet, arr));
// 输出: Hello, Ellie!

JS22 函数的上下文

前端面试题:call、apply、bind的基本概念_哔哩哔哩_bilibili

1、call() 方法

使用情况:

call()方法主要用于设置函数执行时的this上下文,并且立即执行该函数。它非常适合用于对象之间的方法借用,或者当你想要以某个对象为上下文环境执行函数时。

方法:
func.call(thisArg, arg1, arg2, ...);
  • thisArg:函数运行时使用的this值。注意,在非严格模式下,如果指定为nullundefined,它会自动替换为全局对象(浏览器环境下是window),而严格模式下会保持nullundefined
  • arg1, arg2, ...:传递给函数的参数。
示例:
function greet() {
  return `Hello, ${this.name}`;
}

const person = {name: 'Alice'};
console.log(greet.call(person)); // Hello, Alice

2、apply() 方法

使用情况:

apply()方法的作用与call()非常相似,区别在于apply()接受一个参数数组,而不是一系列的参数列表。这对于你不确定传递给函数多少参数,或者参数已经以数组形式存在时非常有用。

方法:
func.apply(thisArg, [argsArray]);
  • thisArg:在func函数运行时使用的this值。
  • [argsArray]:一个数组或类数组对象,其中的数组元素将作为单独的参数传递给func函数。
示例:
function greet(prefix, suffix) {
  return `${prefix} ${this.name} ${suffix}`;
}

const person = {name: 'Alice'};
console.log(greet.apply(person, ['Hello', '!'])); // Hello Alice !

3、bind() 方法

使用情况:

bind()方法创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。bind()是用于将函数体内的this绑定到某个对象,然后返回一个新函数。这对于异步回调中确保this上下文不改变特别有用。

方法:
let boundFunc = func.bind(thisArg[, arg1[, arg2[, ...]]]);
  • thisArg:调用绑定函数时作为this参数传递给目标函数的值。
  • arg1, arg2, ...:当目标函数被调用时,预先添加到绑定函数的参数列表中的参数。
示例:
function greet() {
  return `Hello, ${this.name}`;
}

const person = {name: 'Alice'};
const boundGreet = greet.bind(person);
console.log(boundGreet()); // Hello, Alice

4、总结

虽然call(), apply(), 和bind()都用于设置函数中的this上下文,但call()apply()会立即执行函数,而bind()则返回一个新的函数,可以稍后执行。call()适用于参数明确的情况,apply()适用于参数以数组形式存在的情况,而bind()适用于需要预设this值并稍后调用函数的情况。

/**
 * 描述
 * 将函数 fn 的执行上下文改为 obj 对象
 * 示例1
 * 输入:
 *
 * function () {return this.greeting + ', ' + this.name + '!!!';}, {greeting: 'Hello', name: 'Rebecca'}
 *
 * 输出:
 *
 * Hello, Rebecca!!!
 *
 * TOP
 * 是否格式化代码?
 */

function speak(fn, obj) {
    return fn.call(obj);
}
function fn(){
    return this.greeting + ', ' + this.name + '!!!';
}
const data={greeting: 'Hello', name: 'Rebecca'}
console.log(speak(fn,data));

JS23 返回函数

/**
 * 描述
 * 实现函数 functionFunction,调用之后满足如下条件:
 * 1、返回值为一个函数 f
 * 2、调用返回的函数 f,返回值为按照调用顺序的参数拼接,拼接字符为英文逗号加一个空格,即 ', '
 * 3、所有函数的参数数量为 1,且均为 String 类型
 * 示例1
 * 输入:
 * functionFunction('Hello')('world')
 * 输出:
 * Hello, world
 */
function functionFunction(str) {
    // 返回一个新的函数,这个新函数接受一个字符串参数
    return function(nextStr){
            // 将初始字符串和新字符串用', '连接起来,并返回
            return str + ', ' + nextStr;
        }
}
// 示例用法
const result = functionFunction('Hello')('world');
console.log(result); // 输出: Hello, world

这段代码实际上并不是直接传递两个参数给一个函数,而是利用了JavaScript中的函数柯里化(Currying)的概念。柯里化是一种将使用多个参数的函数转换成一系列使用一个参数的函数的技术。

在这个特定的例子中:

  1. functionFunction('Hello')首先被调用,并传递了一个参数'Hello'。根据之前提供的实现,functionFunction返回一个新的函数f
  2. 然后,立即在其返回的函数上调用('world'),即这个新的函数f接受'world'作为参数。

所以,const result = functionFunction('Hello')('world');这段代码实际上是两个步骤的操作:

  • 首先,functionFunction被调用并传入'Hello',它返回一个函数。
  • 然后,返回的函数立即被调用并传入'world'

这两步合起来看似传递了两个参数,但实际上是分别传递给两个函数的。第一个函数functionFunction接收第一个参数并返回第二个函数,第二个函数接收第二个参数并最终返回字符串'Hello, world'。这就是函数柯里化的一种应用实例。

JS25 二次封装函数

/**
 * 描述
 * 已知函数 fn 执行需要 3 个参数。请实现函数 partial,调用之后满足如下条件:
 * 1、返回一个函数 result,该函数接受一个参数
 * 2、执行 result(str3) ,返回的结果与 fn(str1, str2, str3) 一致
 * 示例1
 * 输入:
 *
 * var sayIt = function(greeting, name, punctuation) {     return greeting + ', ' + name + (punctuation || '!'); };  partial(sayIt, 'Hello', 'Ellie')('!!!');
 *
 * 输出:
 *
 * Hello, Ellie!!!
 */

function partial(fn, str1, str2) {

    return function result(str3){
        return fn(str1,str2,str3);
    }
}
const sayIt = function(greeting, name, punctuation) {
    return greeting + ', ' + name + (punctuation || '!');
};
const message=partial(sayIt, 'Hello', 'Ellie')('!!!');

console.log(message);

image-20240308202706749

在您提供的partial函数示例中,展示的是偏函数应用(Partial Function Application)的概念。这个过程允许你固定一个函数的一个或多个参数,创建一个新的函数,这个新函数等待剩余的参数。在这个特定的例子里,partial函数接受一个函数fn和两个参数str1str2,然后返回一个新的函数,这个新函数期待一个额外的参数str3。当这个新函数被调用时,它会将str1str2str3作为参数传给原始的fn函数,并返回fn的执行结果。

分解解释

  1. **第一个return**返回了一个新的函数result。这个返回的函数是一个闭包,因为它能够访问并使用partial函数调用时提供的参数str1str2,即使在partial函数的执行上下文已经结束之后。
  2. **第二个return**在返回的result函数内部。当result函数被调用并传入str3时,这个return语句执行,调用原始的fn函数,传入str1str2str3作为参数,并返回fn函数的返回值。

如何操作

  • 当你调用partial并传入fnstr1、和str2时,partial返回一个新的函数。
  • 这个新的函数等待一个参数str3
  • 当这个新函数被调用并给出str3时,它内部调用fn(str1, str2, str3)并返回结果。

JS26 使用 arguments

/**
 * 描述
 * 函数 useArguments 可以接收 1 个及以上的参数。请实现函数 useArguments,返回所有调用参数相加后的结果。本题的测试参数全部为 Number 类型,不需考虑参数转换。
 * 示例1
 * 输入:
 *
 * 1, 2, 3, 4
 *
 * 输出:
 *
 * 10
 */
function useArguments() {
    let sum=0;
    for (let i=0;i<arguments.length;i++){
        sum+=arguments[i];
    }
    return sum;
}
const res=useArguments(1, 2, 3, 4);
console.log(res);

JS27 使用 apply 调用函数

1Array.prototype.slice.call(arguments, 1)

这种方法利用了Function.prototype.call()方法来改变slice方法的调用上下文,使其从Array.prototype中借用到arguments对象上。这样做的结果是创建了一个从arguments第二个元素开始的新数组。这种方式在ES6之前是常见的将arguments对象转换为数组的手段,同时也可以用来从任意位置开始“切片”类数组对象。

2、使用[…arguments]后再slice

ES6引入的展开运算符(...)允许更简洁地将类数组对象转换为数组。[...arguments]arguments对象中的所有元素展开,然后包装在一个新的数组字面量中,从而创建了一个真正的数组。紧接着,使用这个数组的slice(1)方法,我们可以得到从第二个元素开始的所有元素的新数组。

3、对比

  • 可读性:使用展开运算符[...arguments]的方式通常被认为更现代且语法更清晰,特别是对于已经熟悉ES6及之后版本特性的开发者。
  • 性能:在大多数现代JavaScript引擎中,性能差异通常是微不足道的,尽管在某些情况下,直接使用slice.call可能会略微快一点,因为它避免了创建中间数组的步骤。但这种差异通常很小,很少是决定使用哪种方法的关键因素。
  • 功能性:两种方法都能达到相同的目的,即将arguments转换为数组,并从中取出一部分元素。选择哪种方法主要取决于代码的风格和可读性偏好。

总之,使用[...arguments].slice(1)的方法本质上确实是再次将数据放入数组中,这与使用Array.prototype.slice.call(arguments, 1)达到的最终结果相同。选择哪种方式主要基于个人偏好、代码一致性以及对现代JavaScript特性的使用。

/**
 * 描述
 * 实现函数 callIt,调用之后满足如下条件
 * 1、返回的结果为调用 fn 之后的结果
 * 2、fn 的调用参数为 callIt 的第一个参数之后的全部参数
 * 示例1
 * 输入:
 *
 * 无
 *
 * 输出:
 *
 * 无
 */
function callIt(fn) {
    // 将arguments对象转换成数组,去掉第一个参数(即fn函数)
    // const args = Array.prototype.slice.call(arguments, 1);
    const res=[...arguments]
    const args=res.slice(1);
    return fn(...args);

}

// 示例用法
var testFn = function(a, b, c) {
    return a + b + c;
};

var result = callIt(testFn, 1, 2, 3);
console.log(result); // 输出:6

JS28 二次封装函数

/**
 * 描述
 * 实现函数 partialUsingArguments,调用之后满足如下条件:
 * 1、返回一个函数 result
 * 2、调用 result 之后,返回的结果与调用函数 fn 的结果一致
 * 3、fn 的调用参数为 partialUsingArguments 的第一个参数之后的全部参数以及 result 的调用参数
 * 示例1
 * 输入:
 *
 * 无
 *
 * 输出:
 *
 * 无
 */

function partialUsingArguments(fn) {
    const arr=[...arguments].slice(1);
    return function result(){
        const result_arr=[...arguments];
        return fn(...arr,...result_arr);
    }

}

JS29 柯里化

/**
 * 描述
 * 已知 fn 为一个预定义函数,实现函数 curryIt,调用之后满足如下条件:
 * 1、返回一个函数 a,a 的 length 属性值为 1(即显式声明 a 接收一个参数)
 * 2、调用 a 之后,返回一个函数 b, b 的 length 属性值为 1
 * 3、调用 b 之后,返回一个函数 c, c 的 length 属性值为 1
 * 4、调用 c 之后,返回的结果与调用 fn 的返回值一致
 * 5、fn 的参数依次为函数 a, b, c 的调用参数
 * 示例1
 * 输入:
 *
 * var fn = function (a, b, c) {return a + b + c}; curryIt(fn)(1)(2)(3);
 *
 * 输出:
 *
 * 6
 */

function curryIt(fn) {
    return function a(a){
        return function b(b){
            return function c(c){
                return fn(a,b,c);
            }
        }
    }
}

var fn = function (a, b, c) {
    return a + b + c
};
const message=curryIt(fn)(1)(2)(3);
console.log(message);

JS30 或运算

/**
 * 描述
 * 返回参数 a 和 b 的逻辑或运算结果
 * 示例1
 * 输入:
 *
 * false, true
 *
 * 输出:
 *
 * true
 */
function or(a, b) {
    return a||b;
}
console.log(or(false,true));

JS31 且运算

/**
 * 描述
 * 返回参数 a 和 b 的逻辑且运算结果
 * 输入描述:
 * false, true
 * 输出描述:
 * false
 * 示例1
 * 输入:
 *
 * false, true
 *
 * 输出:
 *
 * false
 */

function and(a, b) {
    return a && b;
}
console.log(and(false,true));

JS32 模块

/**
 * 描述
 * 完成函数 createModule,调用之后满足如下要求:
 * 1、返回一个对象
 * 2、对象的 greeting 属性值等于 str1, name 属性值等于 str2
 * 3、对象存在一个 sayIt 方法,该方法返回的字符串为 greeting属性值 + ', ' + name属性值
 */
function createModule(str1, str2) {

    return obj = {
        greeting:str1,
        name:str2,
        sayIt(){
            return(this.greeting+ ', ' +this.name);
    }
    }
}
const objContent=createModule('hello','man');
console.log(objContent.sayIt());

JS33 二进制转换

/**
 * 描述
 * 获取数字 num 二进制形式第 bit 位的值。注意:
 * 1、bit 从 1 开始
 * 2、返回 0 或 1
 * 3、举例:2 的二进制为 10,第 1 位为 0,第 2 位为 1
 * 示例1
 * 输入:
 * 128, 8
 * 输出:
 * 1
 * @param num
 * @param bit
 */
function valueAtBit(num, bit) {
    const res = num.toString(2).split('');
    return res[res.length-bit]; //在二进制表示中,最右边的位是最低位(LSB),它实际上是第1位,而数组的索引是从左向右增加的。
}
console.log(valueAtBit(123,2))

1. toString()

用法

toString() 方法返回一个表示调用对象的字符串。对于数字类型,它可以转换数字为其字符串表示形式。此方法还可以接受一个可选的基数参数(进制),用于指定返回的数值字符串的基数。

参数
  • radix (可选):一个介于2到36之间的整数,表示基数(数值的进制)。如果不指定或者指定为undefined,默认为10(十进制)。
示例
javascript
let num = 15;

// 不指定基数,默认十进制
console.log(num.toString());  // 输出: "15"

// 指定基数为二进制
console.log(num.toString(2)); // 输出: "1111"

// 指定基数为十六进制
console.log(num.toString(16)); // 输出: "f"

2. split()

用法

split() 方法将一个字符串分割成子字符串数组,通过指定的分隔符来决定每个拆分点。返回一个包含结果子字符串的数组。

参数
  • separator (可选):指定用来分割字符串的字符(串)。如果省略或不指定,则返回包含原字符串的数组。
  • limit (可选):一个整数,限制返回的分割片段数量。超出这个数量的分割片段会被丢弃。
示例
let str = "apple,banana,cherry";

// 以逗号为分隔符
console.log(str.split(',')); // 输出: ["apple", "banana", "cherry"]

// 以逗号为分隔符,限制输出2个元素
console.log(str.split(',', 2)); // 输出: ["apple", "banana"]

3. parseInt()

用法

parseInt() 函数解析一个字符串参数,并返回指定基数的整数。如果给定的字符串在指定基数中的第一个字符不能被转换为数字类型,parseInt会返回NaN

参数
  • string:要被解析的字符串。如果参数不是一个字符串,则会先转换为字符串再解析。
  • radix (可选):一个介于2到36之间的整数,表示字符串的基数。对于十进制数,此参数不是必须的。如果忽略或为0,JavaScript会根据字符串来猜测具体的基数。
示例
// 解析十进制字符串
console.log(parseInt("10")); // 输出: 10

// 解析十六进制字符串
console.log(parseInt("0xF", 16)); // 输出: 15

// 解析二进制字符串
console.log(parseInt("1010", 2)); // 输出: 10

// 字符串以"0x"开头时,解析为十六进制
console.log(parseInt("0x10")); // 输出: 16

// 字符串以"0"开头,radix为10时,解析为十进制
console.log(parseInt("010", 10));  // 输出: 10

toString(), split(), 和 parseInt() 是处理字符串和数字时非常实用的JavaScript内置函数,它们各自有不同的用途和参数,了解这些方法的工作原理可以帮助您更有效地编写JavaScript代码。

JS34 二进制转换

/**
 * 描述
 * 给定二进制字符串,将其换算成对应的十进制数字
 * 示例1
 * 输入:
 *
 * '11000000'
 *
 * 输出:
 *
 * 192
 *
 * TOP
 * 是否格式化代码?
 * 1
 */
function changeNun(num){
    const res = parseInt(num,2);
    return res;
}
console.log(changeNun(11000000));

JS35 二进制转换

1、join

在JavaScript中,join方法是一个数组的实例方法,它将数组中的所有元素连接成一个字符串。你可以指定一个字符串作为连接符(分隔符),来分隔数组中的元素。如果不指定分隔符,默认使用逗号,作为分隔符。

语法

array.join([separator])

separator (可选):指定一个字符串来分隔数组中的各个元素。如果省略这个参数,数组元素之间将默认用逗号,分隔。如果分隔符是空字符串'',则所有元素之间将没有任何字符。

返回值

返回一个所有数组元素连接成的字符串。

使用默认分隔符

const fruits = ["Banana", "Orange", "Apple", "Mango"];
const str = fruits.join(); // 默认使用逗号作为分隔符
console.log(str); // 输出:"Banana,Orange,Apple,Mango"

使用不同的分隔符

const fruits = ["Banana", "Orange", "Apple", "Mango"];
const str = fruits.join(" - "); // 使用" - "作为分隔符
console.log(str); // 输出:"Banana - Orange - Apple - Mango"

不使用分隔符

const numbers = [1, 2, 3, 4, 5];
const str = numbers.join(''); // 不使用分隔符
console.log(str); // 输出:"12345"

2、注意事项

  • 如果数组中的任何元素是undefinednull,它们会被转换为空字符串。
  • join方法不改变原数组,而是返回一个新字符串。
  • join方法经常用于将数组转换为更易于阅读和展示的字符串格式。

通过这些示例和说明,你可以看到join方法的灵活性和用途,特别是在处理数组元素并需要将它们以特定方式展示为字符串时。

JS35 二进制转换

/**
 * 描述
 * 将给定数字转换成二进制字符串。如果字符串长度不足 8 位,则在前面补 0 到满8位。
 * 示例1
 * 输入:
 *
 * 65
 *
 * 输出:
 *
 * 01000001
 */

/**
 * Function One
 * @param num
 * @returns {number|string}
 */
// function convertToBinary(num) {
//     const arr=num.toString(2);
//     const res=arr.split('');
//
//     if(res.length>=8){
//         return Number(arr);
//     }else {
//         let addLength=8-res.length;
//         const res_arr=[];
//         for(let i=0;i<addLength;i++){
//             res_arr.push(0);
//         }
//         res.splice(0,0,...res_arr);
//         return res.join('');
//     }
// }
// console.log(convertToBinary(65));


/**
 * Function Two
 * @param num
 * @returns {string}
 */
function convertToBinary(num) {
    return num.toString(2).padStart(8, '0');
}

console.log(convertToBinary(65)); // 输出: 01000001

padStart是一个非常实用的字符串方法,用于在当前字符串的开始处填充一些字符,直到字符串达到指定的长度。如果原始字符串的长度已经达到或超过了指定的最小长度,则返回原始字符串。这个方法常用于格式化数字或代码,使其符合特定的显示要求。

1、语法

str.padStart(targetLength [, padString])
  • targetLength:目标字符串的长度。如果这个值小于原始字符串的长度,则返回原始字符串。
  • padString(可选):填充字符串。如果这个字符串太长,会被截断以确保填充后的字符串长度等于targetLength。默认值是空格(" ")。

2、示例

假设我们需要确保一个字符串表示的数字总是至少8位长,不足的部分在前面补0:

let numStr = "65";
let paddedStr = numStr.padStart(8, "0");
console.log(paddedStr); // 输出: "00000065"

这个方法非常适合于需要将数字转换为二进制形式,并且需要保证结果字符串达到特定长度的情况。例如,保证一个二进制数表示为8位,不足的部分用0填充在前。

JS36 乘法

split() 只针对字符串有效,目的就是把字符串内容按照规定分割

/**
 * 描述
 * 求 a 和 b 相乘的值,a 和 b 可能是小数,需要注意结果的精度问题
 * 示例1
 * 输入:
 *
 * 3, 0.0001
 *
 * 输出:
 *
 * 0.0003
 */

function multiply(a, b) {
    // 将a、b转换为字符串,便于计算小数位数
    const aStr = a.toString();
    const bStr = b.toString();
    // 计算小数位数
    const aDecimalPlaces = aStr.includes('.') ? aStr.split('.')[1].length : 0;
    const bDecimalPlaces = bStr.includes('.') ? bStr.split('.')[1].length : 0;
    // 计算因转换成整数而需要放大的倍数
    const factor = Math.pow(10, aDecimalPlaces + bDecimalPlaces);
    // 将a和b转换为整数并相乘,然后除以放大的倍数,调整结果的小数位数
    return (a * Math.pow(10, aDecimalPlaces) * (b * Math.pow(10, bDecimalPlaces))) / factor;
}

console.log(multiply(3, 0.00001));

处理JavaScript中小数乘法的精度问题可以通过一些策略来实现,一种常见的方法是将小数转换成整数进行运算,从而避免直接乘小数带来的精度问题。下面是一个解决方案,它涉及到扩大数值到整数进行计算后,再缩小回原来的数值范围。

1、解决方案

  1. 分别计算两个数小数点后的位数
  2. 根据小数位数,将两个数转换成整数(即乘以相应的10的幂使之成为整数)
  3. 进行整数乘法运算
  4. 根据小数位数调整最终结果,使其回到正确的小数位

Math.pow函数是JavaScript中的一个基本数学函数,用于计算一个数的指数幂,即求xy次幂。其语法如下:

Math.pow(x, y)

其中,x是底数(base),y是指数(exponent)。函数返回值是xy次幂的结果。

2、示例

let result = Math.pow(2, 3); // 计算2的3次幂
console.log(result); // 输出:8

在这个例子中,Math.pow(2, 3)计算的是2的3次幂,即2 * 2 * 2,结果为8。

3、特性

  • 通用性:可以用于计算任意实数的任意实数次幂。
  • 处理负数和分数
  • 指数y可以是负数或分数,允许计算倒数和根号。
    • 负数次幂表示倒数,如Math.pow(2, -2)等于1/4。
    • 分数次幂表示根号,如Math.pow(9, 0.5)Math.pow(9, 1/2)等于3,因为3是9的平方根。

4、注意事项

  • Math.pow处理大数或非常小的数时可能会遇到精度问题。
  • 对于特殊情况,如任何数的0次幂等于1(除了0^0,在JavaScript中这也返回1,尽管在数学上这是未定义的),Math.pow遵循这些数学规则。
  • 在ES2016及以后的版本中,你也可以使用指数运算符**作为Math.pow的简写。例如,2 ** 3Math.pow(2, 3)等效,都是计算2的3次幂。

使用指数运算符

指数运算符**提供了一种更为简洁的方式来执行幂运算,与Math.pow功能相同:

let result = 2 ** 3; // 同Math.pow(2, 3)
console.log(result); // 输出:8

使用指数运算符可以使代码更简洁易读,尤其是在进行复杂数学计算时

JS37 改变上下文

/**
 * 描述
 * 将函数 fn 的执行上下文改为 obj,返回 fn 执行后的值
 * 示例1
 * 输入:
 *
 * alterContext(function() {return this.greeting + ', ' + this.name + '!'; }, {name: 'Rebecca', greeting: 'Yo' })
 *
 * 输出:
 *
 * Yo, Rebecca!
 */
function alterContext(fn, obj) {
    return fn.call(obj);
}
const message=alterContext(function() {return this.greeting + ', ' + this.name + '!'; }, {name: 'Rebecca', greeting: 'Yo' })
console.log(message)

JS38 批量改变对象的属性

/**
 * 描述
 * 给定一个构造函数 constructor,请完成 alterObjects 方法,将 constructor 的所有实例的 greeting 属性指向给定的 greeting 变量。
 * 示例1
 * 输入:
 *
 * var C = function(name) {this.name = name; return this;};
 * var obj1 = new C('Rebecca');
 * alterObjects(C, 'What\'s up'); obj1.greeting;
 *
 * 输出:
 *
 * What's up
 */
function alterObjects(constructor, greeting) {
    constructor.prototype.greeting=greeting;
}
// 给定的构造函数
var C = function(name) {
    this.name = name; // 每个实例都有一个名字
    // 不需要在这里初始化 greeting 属性,因为我们会通过原型来修改它
};
alterObjects(C,'What\'s up')

var obj1 = new C('Rebecca');
console.log(obj1.greeting);


https://blog.csdn.net/a_student_2020/article/details/136422045?spm=1001.2014.3001.5501

image-20240308124109880

image-20240308124125016

JS39 属性遍历

/**
 * 描述
 * 找出对象 obj 不在原型链上的属性(注意这题测试例子的冒号后面也有一个空格~)
 * 1、返回数组,格式为 key: value
 * 2、结果数组不要求顺序
 * 示例1
 * 输入:
 *
 * var C = function() {this.foo = 'bar'; this.baz = 'bim';};
 * C.prototype.bop = 'bip';
 * iterate(new C());
 *
 * 输出:
 *
 * ["foo: bar", "baz: bim"]
 */
function iterate(obj) {
    const res=[];
    for (let i in obj){
        if(obj.hasOwnProperty(i)){
            let text=`${i}: ${obj[i]}`;
            res.push(text);
        }
    }return res;
}

var C = function() {
    this.foo = 'bar';
    this.baz = 'bim';
};
const obj=new C();

C.prototype.bop = 'bip';
console.log(iterate(new C()));

1、hasOwnProperty

hasOwnProperty 方法

hasOwnProperty 是 JavaScript 中的一个非常重要的方法,它用于检测一个属性是否为对象自身的属性,而不是继承自其原型链的属性。如果属性是对象自身的属性,hasOwnProperty 会返回 true;如果属性是从原型链上继承的,或者根本不存在,它会返回 false

因此,hasOwnProperty 的功能是用来检测一个属性是否为对象本身的属性,而不是原型链上的属性。

2、对象中的for…in

for...in 循环中,变量 i(或者您选择的任何变量名)代表的是对象属性的名称(键),而不是索引。对象属性的键通常是字符串(或者在 ES6 中,也可以是 Symbol 类型)。因此,当您使用 obj.hasOwnProperty(i) 时,i 是属性的名称(键),您是在检查对象 obj 是否有一个名为 i 的自有属性。

3、模板字符串

在代码中,${i}: ${obj[i]} 使用了 ES6 引入的模板字符串(Template Strings)功能。模板字符串是一种允许嵌入表达式的字符串字面量,可以包含多行文本和字符串插值功能。使用反引号(```)包围,而不是单引号(')或双引号(")。

在模板字符串中,${expression} 是一个占位符,用于嵌入表达式的结果。在这个特定的例子中,iobj[i] 都是表达式:

  • i 是当前迭代到的对象属性名(键)。
  • obj[i] 是通过属性名(键)i 获取的对应属性值。

因此,${i}: ${obj[i]} 会根据当前迭代到的属性名和属性值,生成一个格式为 "key: value" 的字符串,并将这个字符串添加到结果数组 res 中。

JS40 判断是否包含数字

/**
 * 描述
 * 给定字符串 str,检查其是否包含数字,包含返回 true,否则返回 false
 * 示例1
 * 输入:
 *
 * 'abc123'
 *
 * 输出:
 *
 * true
 */

function containsNumber(str) {
    const res = str.split('');
    // for (let i of res) {
    //     // 检查每个字符是否是数字
    //     if (i >= '0' && i <= '9') {
    //         return true;
    //     }
    // }
    // return false;

    for (let i of res) {
        // 检查每个字符是否是数字
        if (!isNaN(Number(i))) {
            return true;
        }
    }
    return false;
}

console.log(containsNumber('abc123')); // 应该输出 true
  1. if (!isNaN(Number(i))) 的用法解释:

    • Number(i) 尝试将字符 i 转换成数字。如果 i 是一个数字字符(例如 '1'),Number(i) 将会是数字 1。如果 i 不是数字字符(例如 'a'),Number(i) 将会是 NaN(代表“不是一个数字”)。
    • isNaN() 函数用于检查其参数是否是非数字值 NaN。如果是 NaN,则返回 true;如果不是,返回 false
    • !isNaN(Number(i)) 的意思是“如果 i 转换成数字后不是 NaN”,即 i 是一个数字字符。
  2. 关于 return 的问题:

    • 函数中的 return 语句会立即结束当前函数的执行,并返回指定的值给函数调用者。如果有一个 return 语句在函数的内部循环中被执行(比如在 for 循环中),它会立即结束整个函数的执行,而不仅仅是循环。
    • 如果循环内的 return true; 被执行,它会立即结束 containsNumber 函数并返回 true。循环外的 return false; 只有在遍历完整个字符串且未找到任何数字字符时才会执行。
    • 因此,内层的 return(在循环中)不会被外层的 return 覆盖。相反,它会终止函数并返回结果,外层的 return 只在没有任何内层 return 被执行时才会执行。

JS41 检查重复字符串

/**
 * 描述
 * 给定字符串 str,检查其是否包含连续重复的字母(a-zA-Z),包含返回 true,否则返回 false
 * 示例1
 * 输入:
 *
 * 'rattler'
 *
 * 输出:
 *
 * true
 */

function containsRepeatingLetter(str) {
    const res=str.split('');
    for(i=0;i<res.length-1;i++){
        if(res[i]===res[i+1] &&(isNaN(Number(res[i])))){ //注意相同数值的情况
            return true
        }
    }return false;
}
console.log(containsRepeatingLetter('I33t'));

JS42 判断是否以元音字母结尾

/**
 * 描述
 * 给定字符串 str,检查其是否以元音字母结尾
 * 1、元音字母包括 a,e,i,o,u,以及对应的大写
 * 2、包含返回 true,否则返回 false
 * 示例1
 * 输入:
 *
 * 'gorilla'
 *
 * 输出:
 *
 * true
 */

function endsWithVowel(str) {
    const set=new Set("a,e,i,o,u,A,E,I,O,U")
    const res=str.split('');
    return set.has(res[res.length-1]);
}
console.log(endsWithVowel('gorilla'));

JS43 获取指定字符串

1、正则表达式(Regular Expression)

正则表达式(Regular Expression)是一种强大的文本匹配和处理工具,常用于字符串的搜索、替换和验证。以下是一些常见的正则表达式及其用途:

  1. 匹配数字:
    • \d:匹配任意数字。
    • \d+:匹配一个或多个数字。
    • \d{2}:匹配恰好两个数字。
  2. 匹配字母:
    • \w:匹配任意字母、数字和下划线。
    • \w+:匹配一个或多个字母、数字和下划线。
    • [a-zA-Z]:匹配任意一个字母。
  3. 匹配空白字符:
    • \s:匹配空格、制表符、换行符等空白字符。
  4. 匹配特殊字符:
    • \.:匹配点号。
    • \\:匹配反斜杠。
    • \+:匹配加号。
  5. 匹配数量:
    • *:匹配零个或多个。
    • +:匹配一个或多个。
    • ?:匹配零个或一个。
    • {n}:匹配恰好 n 个。
    • {n,}:匹配至少 n 个。
    • {n,m}:匹配至少 n 个,但不超过 m 个。
  6. 位置和边界:
    • ^:匹配字符串的开头。
    • $:匹配字符串的结尾。
    • \b:匹配单词边界。
    • \B:匹配非单词边界。
  7. 分组和引用:
    • ():将其中的表达式分组。
    • \1\2:引用分组匹配的内容。
  8. 字符类:
    • [abc]:匹配 a、b 或 c 中的任意一个字符。
    • [^abc]:匹配除了 a、b、c 之外的任意字符。
  9. 转义字符:
    • \:转义特殊字符,使其失去特殊意义。
  10. 模式修饰符:
    • i:忽略大小写。
    • g:全局匹配。
    • m:多行匹配。
/**
 * 描述
 *
 * 给定字符串 str,检查其是否包含 连续3个数字,请使用正则表达式实现。
 * 1、如果包含,返回最先出现的 3 个数字的字符串
 * 2、如果不包含,返回 false
 * 示例1
 * 输入:
 *
 * '9876543'
 *
 * 输出:
 *
 * 987
 */
function captureThreeNumbers(str) {
    const  match=str.match(/\d{3}/);
    return match?match[0]:false;
}
console.log(captureThreeNumbers('9876543'))
  • / 开始和结束的斜杠之间是正则表达式的模式。
  • \d 表示匹配任意一个数字。
  • {3} 表示匹配前面的模式(即 \d)恰好三次,即匹配连续的三个数字。
  • match() 方法用于在字符串中检索指定模式的第一个匹配项。
  • match[0] 是匹配到的第一个结果,即连续的三个数字的字符串形式。
  • 最后,通过三元运算符,如果匹配成功则返回匹配的结果,否则返回 false

这样,当你调用 captureThreeNumbers('9876543') 时,会返回 '987',符合要求。

JS44 判断是否符合指定格式

/**
 * 描述
 * 给定字符串 str,检查其是否符合如下格式
 * 1、XXX-XXX-XXXX
 * 2、其中 X 为 Number 类型
 * 示例1
 * 输入:
 *
 * '800-555-1212'
 *
 * 输出:
 *
 * true
 */
function matchesPattern(str) {
    var pattern = /^\d{3}-\d{3}-\d{4}$/;
    return pattern.test(str);
}
console.log(matchesPattern('800-555-1212'))
  • 使用var pattern = /^\d{3}-\d{3}-\d{4}$/;定义了一个正则表达式,它匹配以三个数字开始,接着是一个短横线,然后是三个数字,又是一个短横线,最后是四个数字的字符串。
  • pattern.test(str)方法用于测试字符串str是否符合模式pattern。如果str符合该正则表达式定义的模式,则返回true;否则,返回false

这样,matchesPattern函数就能根据给定的规则检查字符串格式了。

在JavaScript中,使用.match()方法和.test()方法都可以用来检查一个字符串是否符合某个正则表达式的模式,但它们之间存在一些关键的区别,这些区别影响着在特定场景下哪个方法更适合使用:

  1. 返回值不同
    • .match()方法返回的是一个数组,该数组包含了匹配的结果。如果没有找到任何匹配,它会返回null。当使用全局标志(g)时,.match()返回的数组会包含字符串中所有的匹配项。而不使用全局标志时,返回的数组还会包含关于匹配位置的额外信息。
    • .test()方法返回的是一个布尔值,如果字符串中含有与正则表达式匹配的文本,则返回true,否则返回false
  2. 性能差异
    • 由于.test()方法只需要确定字符串中是否存在至少一个匹配项,并返回一个简单的布尔值,它通常在性能上比.match()方法更优,尤其是在只关心是否匹配,而不关心匹配的具体内容时。
    • .match()方法提供了更多的信息,但这也意味着当你不需要这些额外信息时,使用它可能会导致不必要的性能开销。
  3. 用例
    • 当你需要知道字符串是否符合某个模式,而不关心具体的匹配内容时,.test()是更好的选择。
    • 当你需要获取一个或多个匹配结果,或者需要更多关于匹配(如具体匹配的文本或匹配发生的位置)的信息时,.match()是更合适的选择。

因此,在你的场景中(检查字符串是否符合XXX-XXX-XXXX格式),使用.test()是更合适的,因为你只需要一个简单的是/否答案,不需要获取匹配的具体内容。这使得.test()在这种用例下既高效又足够。

JS45 判断是否符合 USD 格式

/**
 * 描述
 * 给定字符串 str,检查其是否符合美元书写格式
 * 1、以 $ 开始
 * 2、整数部分,从个位起,满 3 个数字用 , 分隔
 * 3、如果为小数,则小数部分长度为 2
 * 4、正确的格式如:$1,023,032.03 或者 $2.03,错误的格式如:$3,432,12.12 或者 $34,344.3
 */

function isUSD(str) {
    const pattern = /^\$(\d{1,3}(,\d{3})*|0)(\.\d{2})?$/;
    return pattern.test(str);
}
// 测试示例
console.log(isUSD('$1,023,032.03')); // 应输出 true
  • ^\$:匹配字符串开头的 $ 符号。
  • (\d{1,3}(,\d{3})*|0):匹配 1 到 3 位数字,后面可以跟着零个或多个由逗号和三个数字组成的组合,或者一个单独的 0
  • (\.\d{2})?:匹配小数点后恰好有两位数字的可选部分。
  • $:确保正则表达式匹配整个字符串的末尾。

1、*|0 的含义

在正则表达式中,*| 是特殊字符,具有特定的含义:

  • *(星号)表示前面的元素可以出现零次或多次。换言之,它允许前面的模式重复任意次数,包括零次。
  • |(管道符)表示逻辑“或”操作。它用于分隔两个可选的模式,匹配这两个模式中的任意一个。

在表达式 (\d{1,3}(,\d{3})*|0) 中:

  • \d{1,3}(,\d{3})* 匹配一个数字序列,该序列以1到3位数字开始,后面可以跟随零个或多个由逗号和三个数字组成的序列。这部分用来匹配类似 1,000123,456 的格式。
  • |0 表示上述复杂模式的逻辑“或”一个单独的 0。这意味着,整个表达式可以匹配上述的数字序列模式,或者仅仅是一个 0

综上所述,*|0 在这里是为了确保模式可以匹配数字序列(符合千位分隔的格式)或单独的 0(表示金额为零)。

2、 转义

在正则表达式中,.(点)是一个特殊字符,它匹配除换行符之外的任何单个字符。如果你想匹配字面上的点(.),而不是它作为通配符的含义,你需要对它进行转义。

转义一个字符的方法是在它前面加上反斜杠 \。所以,\. 表示字面上的点字符,而不是任意字符的通配符。

在表达式 (\.\d{2})? 中:

  • \. 匹配一个字面上的点。
  • \d{2} 匹配两位数字。
  • ? 表示前面的模式是可选的,即小数部分可以出现一次或不出现。

因此,\. 是必要的,以确保只匹配实际的小数点而不是任意字符。

  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值