JS能力測評經典題--來自牛客網

每道題都寫多種解法,開闊思路。一共45道題。
因為牛客網oj不支持ES6語法,所以大多數寫法只給出傳統寫法。

題目描述
找出元素 item 在給定數組 arr 中的位置
輸出描述:
如果數組中存在 item,則返回元素在數組中的位置,否則返回 -1
輸入例子:
indexOf([ 1, 2, 3, 4 ], 3)
輸出例子:
2

// 一般寫法
function indexOf(arr, item) {
    for (var i = 0; i < arr.length; ++i) {
        if (item === arr[i]) {
            return i;
        }
    }
    return -1;
}

// 簡單粗暴版(因為js自帶的indexOf函數功能已經實現了)
function indexOf(arr, item) {
    return arr.indexOf(item);
}

// 每次比較后都將數組首元素去掉,同時計數器加1,直到找到元素item
function indexOf(arr, item) {
    var count = 0, len = arr.length;
    while (arr.length !== 0 && arr[0] !== item) {
        arr.shift();
        count++;
    }

    return count === len ? -1 : count;
}

// 較復雜的寫法(不建議使用)
function indexOf(arr, item) {
    var pos = arr.map(function(e, index) {
        return e === item ? index: -1;
    }).filter(function(e) {
        return e !== -1;
    });

    return pos.length === 0 ? -1 : pos[0];
}

// forEach加try-catch語句(不建議使用)
function indexOf(arr, item) {
    try {
        arr.forEach((e, i) => {
            if (e === item) {
                throw new Error(i);
            }
        } catch (err) {
            return err.message;
        }
        return -1;
    }
}

題目描述
計算給定數組 arr 中所有元素的總和
輸入描述:
數組中的元素均為 Number 類型
輸入例子:
sum([ 1, 2, 3, 4 ])
輸出例子:
10

// 一般寫法
function sum(arr) {
    var sum = 0;
    for (var i = 0; i < arr.length; ++i) {
        sum += arr[i];
    }
    return sum;
}

// 使用reduce,且將sum初始化為0
function sum(arr) {
    return arr.reduce(function(sum, e) {
        return sum + e;
    }, 0);
}
// ES6寫法,且將reduce中傳的0省略,則sum在這里相當于初始化為arr的首元素
//【第一個sum為函數名,其余為函數內變量名】
const sum = (arr) => arr.reduce((sum, e) => sum + e);

題目描述
移除數組 arr 中的所有值與 item 相等的元素。不要直接修改數組 arr,結果返回新的數組
輸入例子:
remove([1, 2, 3, 4, 2], 2)
輸出例子:
[1, 3, 4]

// 一般寫法
function remove(arr, item) {
    var tempArr = [];
    for (var i = 0; i < arr.length; ++i) {
        tempArr.push(arr[i]);
    }
    /* 對arr的拷貝可使用一個技巧,即 tempArr = arr.slice(0);
       以后均采用這種寫法*/

    var index = tempArr.indexOf(item);
    while (index !== -1) {
        tempArr.splice(index, 1); // 每次刪一個元素
        index = tempArr.indexOf(item);
    }

    return tempArr;
}


// 使用filter
function remove(arr, item) {
    return arr.filter(function(e) {
        return e !== item;
    });
};
// ES6寫法
const remove = (arr, item) => arr.filter((e) => e !== item);

移除數組 arr 中的所有值與 item 相等的元素,直接在給定的 arr 數組上進行操作,并將結果返回
輸入例子:
removeWithoutCopy([1, 2, 2, 3, 4, 2, 2], 2)
輸出例子:
[1, 3, 4]

// 一般寫法
function removeWithoutCopy(arr, item) {
    for (var i = 0; i < arr.length; ++i) {
        if (arr[i] === item) {
            arr.splice(i, 1);
            i--;
        };
    };

    return arr;
}

// 類似地
function removeWithoutCopy(arr, item) {
    var index = arr.indexOf(item);
    while (index !== -1) {
        arr.splice(index, 1);
        index = arr.indexOf(item);
    }

    return arr;
}

/* 因為filter會改變不修改數組本身,會返回一個新數組,所以此處不采用 */

題目描述
在數組 arr 末尾添加元素 item。不要直接修改數組 arr,結果返回新的數組
輸入例子:
append([1, 2, 3, 4], 10)
輸出例子:
[1, 2, 3, 4, 10]

function append(arr, item) {
    var tempArr = arr.slice(0);
    tempArr.push(item);

    return tempArr;
}

題目描述
刪除數組 arr 最后一個元素。不要直接修改數組 arr,結果返回新的數組
輸入例子:
truncate([1, 2, 3, 4])
輸出例子:
[1, 2, 3]

// 一般寫法
function truncate(arr) {
    var tempArr = arr.slice(0);
    tempArr.splice(arr.length - 1);

    return tempArr;
}

// 使用pop
function truncate(arr) {
    var tempArr = arr.slice(0);
    tempArr.pop();

    return tempArr;
}

題目描述
在數組 arr 開頭添加元素 item。不要直接修改數組 arr,結果返回新的數組
輸入例子:
prepend([1, 2, 3, 4], 10)
輸出例子:
[10, 1, 2, 3, 4]

// 使用splice添加元素
function prepend(arr, item) {
    var tempArr = arr.slice(0);
    tempArr.splice(0, 0, item);

    return tempArr;
}

// 直接使用unshift添加首元素方法
function prepend(arr, item) {
    var tempArr = arr.slice(0);
    tempArr.unshift(item);

    return tempArr;
}

題目描述
刪除數組 arr 第一個元素。不要直接修改數組 arr,結果返回新的數組
輸入例子:
curtail([1, 2, 3, 4])
輸出例子:
[2, 3, 4]

// 使用splice刪除元素
function curtail(arr) {
    var tempArr = arr.slice();
    tempArr.splice(0, 1);

    return tempArr;
}

// 直接使用shift刪除首元素方法
function curtail(arr) {
    var tempArr = arr.slice();
    tempArr.shift();

    return tempArr;
}

題目描述
合并數組 arr1 和數組 arr2。不要直接修改數組 arr,結果返回新的數組
輸入例子:
concat([1, 2, 3, 4], ['a', 'b', 'c', 1])
輸出例子:
[1, 2, 3, 4, 'a', 'b', 'c', 1]

// 簡單粗暴版,js已實現此函數功能
function concat(arr1, arr2) {
    var tempArr = arr1.slice(0);
    return tempArr.concat(arr2);
}

// 直接將arr2中的元素一個個push
function concat(arr1, arr2) {
    var tempArr = arr1.slice(0);
    arr2.forEach(function(e) {
        tempArr.push(e);
    });

    return tempArr;
}

// 簡化版,使用apply
function concat(arr1, arr2) {
    var tempArr = arr1.slice(0);
    [].push.apply(tempArr, arr2); // 簡寫版,可替換成下面一行,數組原型方法
    /* Array.prototype.push.apply(tempArr, arr2); */

    return tempArr;
}

題目描述
在數組 arr 的 index 處添加元素 item。不要直接修改數組 arr,結果返回新的數組
輸入例子:
insert([1, 2, 3, 4], 'z', 2)
輸出例子:
[1, 2, 'z', 3, 4]

// 使用splice添加元素
function insert(arr, item, index) {
    var tempArr = arr.slice(0);
    tempArr.splice(index, 0, item);

    return tempArr;
}

題目描述
統計數組 arr 中值等于 item 的元素出現的次數
輸入例子:
count([1, 2, 4, 4, 3, 4, 3], 4)
輸出例子:
3

// 一般寫法
function count(arr, item) {
    var count = 0;
    arr.forEach(function(e) {
        e === item ? count++ : 0;
    });

    return count;
}

// 使用ES6的for...of循環
function count(arr, item) {
    let cnt = 0;
    for (let e of arr) {
        e === item ? cnt++ : 0;
    }
    return cnt;
}

// 使用reduce,歸約寫法
function count(arr, item) {
   return arr.reduce(function(cnt, e) {
       return cnt + (e === item ? 1 : 0);
   }, 0);
}
// 使用ES6箭頭函數
const count = (arr, item) =>
    arr.reduce((cnt, e) =>
        cnt + (e === item ? 1 : 0), 0);

// 使用filter,返回一個新數組,其長度即為所求
const count = (arr, item) => arr.filter(e => e === item).length;

題目描述
找出數組 arr 中重復出現過的元素
輸入例子:
duplicates([1, 2, 4, 4, 3, 3, 1, 5, 3]).sort()
輸出例子:
[1, 3, 4]

關于indexOf與lastIndexOf的文檔

// 一般寫法,排序之后判斷相鄰元素是否相等
function duplicates(arr) {
    var sortedArr = arr.sort();
    var tempArr = [];
    for (var i = 0; i < sortedArr.length; ++i) {
        if (sortedArr[i] === sortedArr[i + 1] &&
             tempArr.indexOf(sortedArr[i]) === -1) {
            tempArr.push(sortedArr[i]);
        }
    }

    return tempArr;
}

// indexOf和lastIndexOf的妙用
function duplicates(arr) {
    var tempArr = [];
    arr.forEach(function(e) {
       if (arr.indexOf(e) !== arr.lastIndexOf(e) &&
            tempArr.indexOf(e) === -1) {
           tempArr.push(e);
       }
    });

    return tempArr;
}

// 使用reduce
function duplicates(arr) {
    var tempArr = [];
    arr.forEach(function(e) {
        tempArr[e] = (tempArr[e] === undefined ? 1 : tempArr[e] + 1);
        /* 計算每個元素出現次數 */
    });

    return tempArr.reduce(function(ret, e, item) {
        if (e && e !== 1 && ret.indexOf(item) === -1) {
            ret.push(item);
            /* 當元素存在,且出現次數不為1,且未push進ret中時... */
        }
        return ret;
    }, []);
}

題目描述
為數組 arr 中的每個元素求二次方。不要直接修改數組 arr,結果返回新的數組
輸入例子:
square([1, 2, 3, 4])
輸出例子:
[1, 4, 9, 16]

// 一般寫法
function square(arr) {
    var tempArr = [];
    arr.forEach(function(e) {
        tempArr.push(e * e);
    });

    return tempArr;
}

// 使用map
function square(arr) {
    return arr.map(function(e) {
        return e * e;
    })
}
// ES6箭頭函數版
const square = arr => arr.map(e => e * e);

題目描述
在數組 arr 中,查找值與 item 相等的元素出現的所有位置
輸入例子:
findAllOccurrences('abcdefabc'.split(''), 'a').sort()
輸出例子:
[0, 6]

// 一般寫法(類C,不推薦)
function findAllOccurrences(arr, item) {
    var tempArr = [];
    for (var i = 0; i < arr.length; i++){
        if (arr[i] === item) {
            tempArr.push(i);
        }
    }

    return tempArr;
}

// 改進版
function findAllOccurrences(arr, item) {
    var tempArr = [];
    arr.forEach(function(e, index) {
        e !== item || tempArr.push(index);
    });

    return tempArr;
}

// 使用map和filter
function findAllOccurrences(arr, item) {
    return arr.map(function(ele, index) {
        return ele === item ? index : -1;
        /* 樣例返回結果為[ -1, -1, -1, -1, -1, -1, -1, 0, 6 ] */
    }).filter(function(e) {
        return e !== -1;
        /* 過濾掉 e === -1 的情況 */
    })
}
// ES6箭頭函數版
const findAllOccurrences = (arr, item) =>
    arr.map((ele, index) => ele === item ? index : -1).filter(e => e !== -1);

題目描述
給定的 js 代碼中存在全局變量,請修復

function globals() {
    myObject = {
      name : 'Jory'
    };
    return myObject;
}
/* 在JavaScript中,如果不使用var聲明變量,則該變量被視為全局變量。*/

// 直接加var (在ES6標準中,最好替換成const或let)
function globals() {
    var myObject = {
      name : 'Jory'
    };

    return myObject;
}

// 把對象歸屬改為原型
function globals() {
    globals.prototype.myObject = {
        name : 'Jory'
    };

    return globals;
}

// 把對象改為匿名的
function globals() {
    return { name : 'Jory' };
}

題目描述
請修復給定的 js 代碼中,函數定義存在的問題

function functions(flag) {
    if (flag) {
      function getValue() { return 'a'; }
    } else {
      function getValue() { return 'b'; }
    }
    return getValue();
}

輸入例子:
functions(true)
輸出例子:
a

/* else中的語句相當于將if中的function重寫,因此無論flag為何值,返回的方法始終為重寫后的方
   法。將方法賦值給一個變量,方法就不會被重寫 */

// 將方法賦值給一個變量
function functions(flag) {
    var getValue = null;
    if (flag) {
        getValue = function () { return 'a'; }
    } else {
        getValue = function () { return 'b'; }
    }

    return getValue();
}

// 修改為閉包形式
function functions(flag) {
    if (flag) {
        return (function() { return 'a'; })();
    } else {
        return (function() { return 'b'; })();
    }
}

// 使用 ?: 判斷語句整合版
function functions(flag) {
    function getValue() {
        return flag ? 'a' : 'b';
    }

    return getValue();
}
// ES6箭頭函數形式(精簡版)
const functions = flag => flag ? 'a' : 'b';

題目描述
修改 js 代碼中 parseInt 的調用方式,使之通過全部測試用例

function parse2Int(num) {
    return parseInt(num);
}

輸入例子:
parse2Int('12'); parse2Int('12px'); parse2Int('0x12')
輸出例子:
12; 12; 0

/* parseInt(string, radix);
   當參數radix的值為0,或沒有設置該參數時,parseInt()會根據 string 來判斷數字的基數。
   如果string以"0x"開頭,parseInt()會把string的其余部分解析為十六進制的整數。
   如果string以0開頭,那么ECMAScript3允許parseInt()的一個實現把其后的字符解析為八進制
   或十六進制的數字。如果string以1~9的數字開頭,parseInt()將把它解析為十進制的整數 */

// 將所有輸入轉換成十進制輸出
function parse2Int(num) {
    return parseInt(num, 10);
}

題目描述
判斷 val1 和 val2 是否完全等同

function identity(val1, val2) {
    return val1 === val2;
}

題目描述
實現一個打點計時器,要求
1、從 start 到 end(包含 start 和 end),每隔 100 毫秒 console.log 一個數字,每次數字增幅為 1
2、返回的對象中需要包含一個 cancel 方法,用于停止定時操作
3、第一個數需要立即輸出

setTimeout和setInterval的語法相同。它們都有兩個參數,一個是將要執行的代碼字符串,還有一個
是以毫秒為單位的時間間隔,當過了那個時間段之后就將執行那段代碼。
不過這兩個函數還是有區別的,setInterval在執行完一次代碼之后,經過了那個固定的時間間隔,它還
會自動重復執行代碼,而setTimeout只執行一次那段代碼。
window.setInterval("function", time); //設置一個超時對象,周期="交互時間"
window.setTimeout("function", time);  //設置一個超時對象,只執行一次,無周期
/* 停止定時 */
window.clearInterval(對象);   // 清除已設置的setInterval對象
window.clearTimeout(對象);    // 清除已設置的setTimeout對象

setInterval setTimeout clearInterval() clearTimeout()

// 一般寫法,使用setInterval
function count(start, end) {
    console.log(start++);

    var timer = setInterval(function() {
        if (start <= end) {
            console.log(start++);
        } else {
            clearInterval(timer);
        }
    }, 100);

    return {
        cancel: function() {
            clearInterval(timer);
        }
    }
}

// 用setTimeout模擬setInterval,遞歸寫法,表述較簡潔
function count(start, end) {
    if (start <= end) {
        console.log(start++);

        st = setTimeout(function() {
            count(start, end);
        }, 100);
    }

    return {
        cancel: function() {
            clearTimeout(st);
        }
    }
}

// 這種寫法不符合函數式編程的特點,不推薦
function count(start, end) {
    var timer = null;
    console.log(start);

    var obj = {
        timer: setInterval(function() {
            if (start <= end) {
                start++;
                console.log(start);
            } else {
                clearInterval(this.timer);
            }
        }, 100),
        cancel: function() {
          clearInterval(this.timer);
        }
    }

    return obj;
}

題目描述
實現 fizzBuzz 函數,參數 num 與返回值的關系如下:
1、如果 num 能同時被 3 和 5 整除,返回字符串 fizzbuzz
2、如果 num 能被 3 整除,返回字符串 fizz
3、如果 num 能被 5 整除,返回字符串 buzz
4、如果參數為空或者不是 Number 類型,返回 false
5、其余情況,返回參數 num
輸入例子:
fizzBuzz(15)
輸出例子:
fizzbuzz

// 一般寫法
function fizzBuzz(num) {
    if (num % 3 === 0 && num % 5 === 0) {
    /* 特殊地,可換為 if (num % 15 === 0) */
        return 'fizzbuzz';
    } else if (num % 3 === 0) {
        return 'fizz';
    } else if (num % 5 === 0) {
        return 'buzz';
    }

    if (num === null || num === '' || typeof num !== 'number') {
        return false;
    }

    return num;
}

// 改進寫法
function fizzBuzz(num) {
    if (num === null || num === '' || typeof num !== 'number') {
        return false;
    }

    var result = '';
    if (num % 3 === 0) {
        result += 'fizz';
    }
    if (num % 5 === 0) {
        result += 'buzz';
    }

    return result ? result : num;
}

題目描述
將數組 arr 中的元素作為調用函數 fn 的參數
輸入例子:
argsAsArray(function (greeting, name, punctuation) {return greeting + ', ' + name + (punctuation || '!');}, ['Hello', 'Ellie', '!'])
輸出例子:
Hello, Ellie!

// 直接法
function argsAsArray(fn, arr) {
    return fn(arr[0],arr[1],arr[2]);
}

/* 調用函數可以使用call或者apply這兩個方法,區別在于call需要將傳給函數的參數明確寫出來,是
   多少參數就需要寫多少參數。而apply則將傳遞給函數的參數放入一個數組中,傳入參數數組即可 */
function argsAsArray(fn, arr) {
    return fn.apply(this, arr);
}

題目描述
將函數 fn 的執行上下文改為 obj 對象
輸入例子:
speak(function () {return this.greeting + ', ' + this.name + '!!!';}, {greeting: 'Hello', name: 'Rebecca'})
輸出例子:
Hello, Rebecca!!!

// apply
function speak(fn, obj) {
    return fn.apply(obj, []); // 第二個參數可省略
}

// call
function speak(fn, obj) {
    return fn.call(obj);
}

// bind
function speak(fn, obj) {
    return fn.bind(obj)();
}

題目描述
實現函數 functionFunction,調用之后滿足如下條件:
1、返回值為一個函數 f
2、調用返回的函數 f,返回值為按照調用順序的參數拼接,拼接字符為英文逗號加一個空格,即 ', '
3、所有函數的參數數量為 1,且均為 String 類型
輸入例子:
functionFunction('Hello')('world')
輸出例子:
Hello, world

// 一般寫法
function functionFunction(str) {
    function f(s) {
        return str + ', ' + s; // 可替換為 return [str, s].join(', '); 
    }

    return f;
}

// 精簡版,省略內部命名函數
const functionFunction = str => s => [str, s].join(', ');

題目描述
實現函數 makeClosures,調用之后滿足如下條件:
1、返回一個函數數組 result,長度與 arr 相同
2、運行 result 中第 i 個函數,即 result[i](),結果與 fn(arr[i]) 相同
輸入例子:

var arr = [1, 2, 3]; 
var square = function (x) { 
    return x * x; 
}; 
var funcs = makeClosures(arr, square); 
funcs[1]();

輸出例子:
4

理解閉包

// 這種寫法比較繞(多層嵌套)
function makeClosures(arr, fn) {
    var result = [];
    arr.forEach(function(e) {
        result.push(function(num) {
            return function() {
                return fn(num);
            };
        }(e));
    });

    return result;
}

// 較簡潔版(一般按這種方式)
function makeClosures(arr, fn) {
    var result = [];
    arr.forEach(function(e) {
        result.push(function() {
            return fn(e);
        });
    });

    return result;
}

// bind的妙用(推薦寫法)
function makeClosures(arr, fn) {
    var result = [];
    arr.forEach(function(e) {
        result.push(fn.bind(this, e));
    });

    return result;
}

/* 為了更清晰地作對比,對這幾種寫法分別用箭頭函數表示 */
// 1
function makeClosures(arr, fn) {
    let result = [];
    arr.forEach(e => result.push(num => () => fn(num))(e));
    return result;
}
// 2
function makeClosures(arr, fn) {
    let result = [];
    arr.forEach(e => result.push((e => () => fn(e))(e)));
    return result;
} 
// 3
function makeClosures(arr, fn) {
    let result = [];
    arr.forEach(e => result.push(fn.bind(null, e))); 
    return result;
}

題目描述
已知函數 fn 執行需要 3 個參數。請實現函數 partial,調用之后滿足如下條件:
1、返回一個函數 result,該函數接受一個參數
2、執行 result(str3) ,返回的結果與 fn(str1, str2, str3) 一致
輸入例子:
var sayIt = function(greeting, name, punctuation) { return greeting + ', ' + name + (punctuation || '!'); }; partial(sayIt, 'Hello', 'Ellie')('!!!');
輸出例子:
Hello, Ellie!!!

關于js中 call、apply、bind 的用法可參考這篇博客
// call和apply必須顯式地調用str3,立即執行
// bind不是立即執行,未傳入str3時,并未執行,只是返回一個函數,等待參數傳入
?// this用于上下文不確定的情況

// call
function partial(fn, str1, str2) {
    function result(str3) {
        return fn.call(this, str1, str2, str3);
    }

     return result;
}

// apply(這里只是為了對照)
function partial(fn, str1, str2) {
    function result(str3) {
        return fn.apply(this, [str1, str2, str3]);
    }

    return result;
}

// 這個bind會生成一個新函數(對象), 它的str1, str2參數都定死了, str3未傳入, 一旦傳入就會執行
function partial(fn, str1, str2) {
    return fn.bind(this, str1, str2); // 或 return fn.bind(null, str1, str2);
}

// bind同上, 多了一步, 把str3傳入的過程寫在另一個函數里面, 
// 而另一個函數也有str1, str2參數
// 此法有種多次一舉的感覺,但是表示出了后續的調用。
function partial(fn, str1, str2) {
    function result(str3) {
        return fn.bind(this, str1, str2)(str3);
    }

    return result;
}

// 匿名函數,默認this綁定global,與bind的第一個參數為this時效果一樣。
function partial(fn, str1, str2) {
    return function(str3) {
        return fn(str1, str2, str3);
    }
}

// ES6。this指向undefined.
const partial = (fn, str1, str2) => str3 => fn(str1, str2, str3);

題目描述
函數 useArguments 可以接收 1 個及以上的參數。請實現函數 useArguments,返回所有調用參數相加后的結果。本題的測試參數全部為 Number 類型,不需考慮參數轉換。
輸入例子:
useArguments(1, 2, 3, 4)
輸出例子:
10

關于arguments的介紹

// 一般寫法,直接利用arguments只有length屬性的特點
function useArguments() {
    var sum = 0;
    for (var i = 0; i < arguments.length; ++i) {
        sum += arguments[i];
    }

    return sum;
}

// call
function useArguments() {
    var arr = [].slice.call(arguments); // 轉成數組 ([]為Array.prototype的語法糖)
    return arr.reduce(function(a, b) {
        return a + b;
    });
}
/* call, bind, apply會改變生成函數對象的this, 使得arguments可以直接使用數組的方法,
   所以也可不轉換成數組而直接使用reduce */

// call同上, 精簡版
function useArguments() {
    return [].reduce.call(arguments, function(a, b) {
        return a + b;
    });
}

// bind
function useArguments() {
    return [].reduce.bind(arguments, function(a, b) {
        return a + b;
    })();
}

// apply
function useArguments() {
    return [].reduce.apply(arguments, [function(a, b) {
        return a + b;
    }]);
}

// eval的妙用,但不推薦使用eval
function useArguments() {
    var arr = Array.prototype.slice.call(arguments);
    return eval(arr.join('+'));
}

題目描述
實現函數 callIt,調用之后滿足如下條件
1、返回的結果為調用 fn 之后的結果
2、fn 的調用參數為 callIt 的第一個參數之后的全部參數
輸入例子:
var a = 1; var b = 2; var test = function (first, second) { return first === a && second === b;}; callIt(test, a, b);
輸出例子:
true

箭頭函數的arguments 剩余參數(rest parameter)

// slice
function callIt(fn) {
    var arr = [].slice.call(arguments, 1); 
    /* 將arguments轉成數組并只截取從1開始之后的所有元素 */
    return fn.apply(this, arr);
}

// shift
function callIt(fn) {
    return [].shift.call(arguments).apply(null, arguments);
    /* [].shift.call(arguments)返回了第一個參數fn, 因為這里fn等價于arguments[0] */
}

// ES6語法糖
const callIt = (fn, ...args) => fn(...args);

題目描述
實現函數 partialUsingArguments,調用之后滿足如下條件:
1、返回一個函數 result
2、調用 result 之后,返回的結果與調用函數 fn 的結果一致
3、fn 的調用參數為 partialUsingArguments 的第一個參數之后的全部參數以及 result 的調用參數
輸入例子:
var a = 1; var b = 2; var c = 3; var d = 4;var test = function (first, second, third, forth) {return first + second + third + forth;};partialUsingArguments(test, a, b)(c, d);
輸出例子:
10

function partialUsingArguments(fn) {
    var arr = [].slice.call(arguments, 1); 
    /* arr是由partialUsingArguments傳的arguments轉換而來 */
    
    function result() {
        var arrFromResult = [].slice.call(arguments);
        /* arrFromResult則從函數result中傳的arguments轉換, 所以不能在result外獲取 */
        return fn.apply(null, arr.concat(arrFromResult));
    }

    return result;
}

// 稍微簡化,跟上面差不多
function partialUsingArguments(fn) {
    var arr = [].slice.call(arguments, 1);
    
    return function() {
        return fn.apply(this, arr.concat([].slice.call(arguments)));
    }
}

題目描述
已知 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 的調用參數
輸入例子:
var fn = function (a, b, c) {return a + b + c}; curryIt(fn)(1)(2)(3);
輸出例子:
6

JS中的柯里化(currying)

// apply
function curryIt(fn) {
    var arr = [];
    return function(a) {
        arr.push(a);
        return function(b) {
            arr.push(b);
            return function(c) {
                arr.push(c);
                return fn.apply(this, arr);
            }
        }
    }
}

// 推薦寫法
function curryIt(fn) {
    return function(e1) {
        return function(e2) {
            return function(e3) {
                return fn(e1, e2, e3); // return fn.call(this, e1, e2, e3); 與之等價
            }
        }
    }
}
// 箭頭函數
const curryIt = fn => e1 => e2 => e3 => fn(e1, e2, e3);

題目描述
返回參數 a 和 b 的邏輯或運算結果
輸入例子:
or(false, true)
輸出例子:
true

function or(a, b) {
    return a || b;
}

題目描述
返回參數 a 和 b 的邏輯且運算結果
輸入例子:
and(false, true)
輸出例子:
false

function and(a, b) {
    return a && b;
}

題目描述
完成函數 createModule,調用之后滿足如下要求:
1、返回一個對象
2、對象的 greeting 屬性值等于 str1, name 屬性值等于 str2
3、對象存在一個 sayIt 方法,該方法返回的字符串為 greeting屬性值 + ', ' + name屬性值

// 推薦寫法
function createModule(str1, str2) {
    return {
        greeting: str1,
        name: str2,
        sayIt: function() {
            return [this.greeting, this.name].join(', ');
        }
    }
}

// Constructor grammer
function createModule(str1, str2) {
    function Obj() {
        this.greeting = str1;
        this.name = str2;
        this.sayIt = function() {
            return this.greeting + ', ' + this.name;
        }
    }
    
    return new Obj();   
}

題目描述
獲取數字 num 二進制形式第 bit 位的值。注意:
1、bit 從 1 開始
2、返回 0 或 1
3、舉例:2 的二進制為 10,第 1 位為 0,第 2 位為 1
輸入例子:
valueAtBit(128, 8)
輸出例子:
1

// 推薦寫法 (時間復雜度是O(log bit))
function valueAtBit(num, bit) {
    return (num & Math.pow(2, bit - 1)) === 0 ? 0 : 1;
}

// 利用運算符特性,簡潔寫法 (時間復雜度O(n))
function valueAtBit(num, bit) {
    return num >> (bit - 1) & 1;
}

// 轉成字符串 (不推薦)
function valueAtBit(num, bit) {
    var s = num.toString(2);
    var len = s.length;
    return parseInt(s[len - bit]);
}

題目描述
給定二進制字符串,將其換算成對應的十進制數字
輸入例子:
base10('11000000')
輸出例子:
192

// parseInt(string, radix) 的可選參數是操作數的進制說明,不是目標的進制。
function base10(str) {
    return parseInt(str, 2);
}

題目描述
將給定數字轉換成二進制字符串。如果字符串長度不足 8 位,則在前面補 0 到滿8位。
輸入例子:
convertToBinary(65)
輸出例子:
01000001

// 前三種都是采用拼接方法
function convertToBinary(num) {
    var s = num.toString(2);
    var len = s.length;
    if (len < 8) {
        s = '0000000'.slice(0, 8 - len) + s;
    }
    
    return s;
}

function convertToBinary(num) {
    var s = num.toString(2);
    while (s.length < 8) {
        s = '0' + s;
    }
     
    return s;
}

function convertToBinary(num) {
    var s = num.toString(2);
    return s.length < 8 ? new Array(8 - s.length + 1).join('0').concat(s) : s;
}

// 推薦寫法, 如果長度過長, 則截取后8位(倒數第8位開始到最后1位)
function convertToBinary(num) {
    var s = num.toString(2);
    return s.length < 8 ? ('00000000' + s).slice(-8) : s;
}

題目描述
求 a 和 b 相乘的值,a 和 b 可能是小數,需要注意結果的精度問題
輸入例子:
multiply(3, 0.0001)
輸出例子:
0.0003

String.prototype.substring()

Number.prototype.toFixed()

// 推薦寫法
function multiply(a, b) {
    a = a.toString();
    b = b.toString();
    var aLen = a.substring(a.indexOf('.') + 1).length;
    var bLen = b.substring(b.indexOf('.') + 1).length; 
    
    return (a * b).toFixed(Math.max(aLen, bLen));
    /* 本題未說明保留小數位數, 這里假定得出的結果不含多余的0, 即0.0003000...需轉成0.0003 */
}

題目描述
將函數 fn 的執行上下文改為 obj,返回 fn 執行后的值
輸入例子:
alterContext(function() {return this.greeting + ', ' + this.name + '!'; }, {name: 'Rebecca', greeting: 'Yo' })
輸出例子:
Yo, Rebecca!

/* 將函數fn的執行上下文改為obj對象, 只需要將obj作為call或apply的第一個參數傳入即可 */
// call
function alterContext(fn, obj) {
    return fn.call(obj); // 這里的第二個參數可有可無, 如有, 則作為fn的參數(下同)
}

// apply
function alterContext(fn, obj) {
  return fn.apply(obj); 
}

// bind
function alterContext(fn, obj) {
  return fn.bind(obj)(); // .bind()返回的是一個函數, 需要立即執行 
}

題目描述
給定一個構造函數 constructor,請完成 alterObjects 方法,將 constructor 的所有實例的 greeting 屬性指向給定的 greeting 變量。
輸入例子:
var C = function(name) {this.name = name; return this;}; var obj1 = new C('Rebecca'); alterObjects(C, 'What's up'); obj1.greeting;
輸出例子:
What's up

繼承與原型鏈(prototype chain)

/* 每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個
   指向原型對象的內部指針 */

function alterObjects(constructor, greeting) {
    constructor.prototype.greeting = greeting;
}

題目描述
找出對象 obj 不在原型鏈上的屬性(注意這題測試例子的冒號后面也有一個空格~)
1、返回數組,格式為 key: value
2、結果數組不要求順序
輸入例子:
var C = function() {this.foo = 'bar'; this.baz = 'bim';}; C.prototype.bop = 'bip'; iterate(new C());
輸出例子:
["foo: bar", "baz: bim"]

Object.getOwnPropertyNames()
Object.keys()
Object.prototype.hasOwnProperty()
// 前兩種均使用map (推薦寫法)
function iterate(obj) {
    return Object.getOwnPropertyNames(obj).map(function(key) {
        return key + ': ' + obj[key];
    });
}

function iterate(obj) {
    return Object.keys(obj).map(function(key) {
        return key + ': ' + obj[key];
    });
}

/*用for-in遍歷可枚舉的屬性, 用hasOwnProperty()方法判斷是否是自有屬性(即不在原型鏈上)*/
function iterate(obj) {
    var arr = [];
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            arr.push(key + ': ' + obj[key]);
        }
    }
    
    return arr;
}

</br>

以下為正則表達式相關題目

可以通過下面兩種方法創建一個正則表達式:

  1. 使用一個正則表達式字面量,如下所示:
    var re = /ab+c/;
    正則表達式字面量在腳本加載后編譯。若你的正則表達式是常量,使用這種方式可以獲得更好的性能。
  2. 調用RegExp對象的構造函數,如下所示:
    var re = new RegExp("ab+c");
    使用構造函數,提供了對正則表達式運行時的編譯。當你知道正則表達式的模式會發生改變, 或者你事先并不了解它的模式或者是從其他地方(比如用戶的輸入),得到的代碼這時比較適合用構造函數的方式。
///

題目描述
給定字符串 str,檢查其是否包含數字,包含返回 true,否則返回 false
輸入例子:
containsNumber('abc123')
輸出例子:
true

\d 匹配一個數字, 等價于[0-9]。
例如,  /\d/ 或者 /[0-9]/ 匹配 "B2 is the suite number." 中的'2'。
test()方法執行一個檢索,用來查看正則表達式與指定的字符串是否匹配。返回true或false。
function containsNumber(str) {
    return /\d/.test(str);
}

題目描述
給定字符串 str,檢查其是否包含連續重復的字母(a-zA-Z),包含返回 true,否則返回 false
輸入例子:

containsRepeatingLetter('rattler')
輸出例子:
true

[xyz] 一個字符集合, 匹配方括號的中任意字符。可以使用破折號(-)來指定一個字符范圍。
對于點(.)和星號(*)這樣的特殊符號在一個字符集中沒有特殊的意義。他們不必進行轉義,不過轉義也是起作用的。
例如,[abcd] 和[a-d]是一樣的。他們都匹配"brisket"中的‘b’,也都匹配“city”中的‘c’。
/[a-z.]+/ 和/[\w.]+/都匹配“test.i.ng”中的所有字符。

(x) 匹配 'x' 并且記住匹配項,就像下面的例子展示的那樣。括號被稱為"捕獲括號"。
模式/(foo) (bar) \1 \2/中的'(foo)'和'(bar)'匹配并記住字符串'foo bar foo bar'中前兩個單詞。
模式中的 \1 和 \2 匹配字符串的后兩個單詞。注意 \1、\2、\n是用在正則表達式的匹配環節。
在正則表達式的替換環節,則要使用像 $1、$2、$n 這樣的語法,例如,
'bar foo'.replace( /(...) (...)/, '$2 $1' )。 
function containsRepeatingLetter(str) {
    return /([a-zA-Z])\1/.test(str);
}

題目描述
給定字符串 str,檢查其是否以元音字母結尾
1、元音字母包括 a,e,i,o,u,以及對應的大寫
2、包含返回 true,否則返回 false
輸入例子:
endsWithVowel('gorilla')
輸出例子:
true

$ 匹配輸入的結束。如果多行標示被設置為true,那么也匹配換行符前的位置。
例如,/t$/ 并不會匹配 "eater" 中的 't',但是會匹配 "eat" 中的 't'。

通過標志進行高級搜索
正則表達式有四個可選參數進行全局和不分大小寫搜索。
這些參數既可以單獨使用也可以一起使用在任何順序和包含正則表達式的部分中。

正則表達式標志
標志  描述
g     全局搜索。
i     不區分大小寫搜索。
m     多行搜索。
y     執行“粘性”搜索,匹配從目標字符串的當前位置開始,可以使用y標志。
function endsWithVowel(str) {
    return /[aeiou]$/i.test(str);
}

題目描述
給定字符串 str,檢查其是否包含連續3個數字
1、如果包含,返回最新出現的3個數字的字符串
2、如果不包含,返回 false
輸入例子:
captureThreeNumbers('9876543')
輸出例子:
987

String.prototype.match()

function captureThreeNumbers(str) {
    var arr = str.match(/\d{3}/); // 尋找三個數字字符 \d{3}
    return arr ? arr[0] : false;
}

題目描述
給定字符串 str,檢查其是否符合如下格式
1、XXX-XXX-XXXX
2、其中 X 為 Number 類型
輸入例子:
matchesPattern('800-555-1212')
輸出例子:
true

^ 匹配輸入的開始。如果多行標志被設置為true,那么也匹配換行符后緊跟的位置。
例如,/^A/ 并不會匹配 "an A" 中的 'A',但是會匹配 "An E" 中的 'A'。
當 '^' 作為第一個字符出現在一個字符集合模式時,它將會有不同的含義。
function matchesPattern(str) {
    return /^(\d{3}-){2}\d{4}$/.test(str);
}

題目描述
給定字符串 str,檢查其是否符合美元書寫格式
1、以 $ 開始
2、整數部分,從個位起,滿 3 個數字用 , 分隔
3、如果為小數,則小數部分長度為 2
4、正確的格式如:$1,023,032.03 或者 $2.03,錯誤的格式如:$3,432,12.12 或者 $34,344.3
輸入例子:
isUSD('$20,933,209.93')
輸出例子:
true

? 匹配前面一個表達式0次或者1次。等價于 {0,1}。
例如,/e?le?/ 匹配 'angel' 中的 'el',和 'angle' 中的 'le' 
(注意第二個 ? 前面的匹配表達式是 e 而不是 le) 以及 'oslo' 中的'l'。

如果緊跟在任何量詞 *、 +、? 或 {} 的后面,將會使量詞變為非貪婪的(匹配盡量少的字符),和缺省
使用的貪婪模式(匹配盡可能多的字符)正好相反。
例如,對 "123abc" 應用 /\d+/ 將會返回 "123",如果使用 /\d+?/,那么就只會匹配到 "1"。
還可以運用于向前斷言 正向肯定查找x(?=y) 和 正向否定查找x(?!y) 。

* 匹配前一個表達式0次或多次。等價于 {0,}。
例如,/bo*/會匹配 "A ghost boooooed" 中的 'booooo' 和 "A bird warbled" 中的 'b', 
但是在 "A goat grunted" 中將不會匹配任何東西。

. (小數點)匹配  除了換行符(\n)之外的任何單個字符。
例如, /.n/將會匹配"nay, an apple is on the tree"中的'an'和'on', 但是不會匹配 'nay'。
function isUSD(str) {
    return /^\$\d{1,3}(,\d{3})*(\.\d{2})?$/.test(str);
}

如有錯誤,懇請指正,也歡迎各位大佬貢獻出更好的解法

--                                  The End                                    --
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容