柯里化
已知 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
function curryIt(fn) {
let args = [];
return function curried(arg){
args.push(arg)
if(args.length >= fn.length){
return fn.apply(this,args)
}
else{
return function(arg2){
return curried.call(this,arg2)
}
}
}
}
二次封装函数
实现函数 partialUsingArguments,调用之后满足如下条件:
1、返回一个函数 result
2、调用 result 之后,返回的结果与调用函数 fn 的结果一致
3、fn 的调用参数为 partialUsingArguments 的第一个参数之后的全部参数以及 result 的调用参数
function partialUsingArguments(fn) {
let args1 = Array.prototype.slice.call(arguments, 1)
return function() {
let args2 = Array.prototype.slice.call(arguments, 0)
return fn.apply(this, args1.concat(args2))
}
}
已知函数 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!!!
function partial(fn, str1, str2) {
//返回一个函数 result,该函数接受一个参数
return function(str3){
//执行 result(str3) ,返回的结果与 fn(str1, str2, str3) 一致
return fn(str1,str2,str3);
}
}
使用apply函数调用
实现函数 callIt,调用之后满足如下条件
1、返回的结果为调用 fn 之后的结果
2、fn 的调用参数为 callIt 的第一个参数之后的全部参数
function callIt(fn) {
let args = Array.prototype.slice.call(arguments, 1)
return fn.apply(this, args)
}
-
apply() 和 call()方法
person 的 fullName 方法被应用到 personvar person = { fullName: function() { return this.firstName + " " + this.lastName; } } var person1 = { firstName: "Bill", lastName: "Gates", } person.fullName.apply(person1); // 将返回 "Bill Gates"
-
call() 和 apply() 之间的区别
不同之处是:
call() 方法分别接受参数。
apply() 方法接受数组形式的参数。
如果要使用数组而不是参数列表,则 apply() 方法非常方便。 -
apply方法能劫持另外一个对象的方法,继承另外一个对象的属性
Function.apply(obj,args)方法能接收两个参数
obj:这个对象将代替Function类里this对象
args:这个是数组,它将作为参数传给
闭包:
如果在函数func内部声明函数inner,然后在函数外部调用inner,这个过程即产生了一个闭包。
闭包最大的用处有两个:可以读取到函数内部的变量,让这些变量的值始终保持在内存中
使用arguments
函数useArguments可以接收1个及以上的参数。请实现函数useArguments,返回所有调用参数相加后的结果。
function useArguments() {
let sum = 0;
for(let i = 0; i < arguments.length; i++){
sum += arguments[i];
}
return sum;
}
-
Arguments对象
arguments 是一个对应于传递给函数的参数的类数组对象arguments对象是所有(非箭头)函数中都可用的局部变量。你可以使用arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,第一个参数在索引0处。例如,如果一个函数传递了三个参数,你可以以如下方式引用他们:
arguments[0] arguments[1] arguments[2]
arguments对象不是一个 Array 。它类似于Array,但除了length属性和索引元素之外没有任何Array属性。例如,它没有 pop 方法。但是它可以被转换为一个真正的Array
使用闭包
实现函数 makeClosures,调用之后满足如下条件:
1、返回一个函数数组 result,长度与 arr 相同
2、运行 result 中第 i 个函数,即 resulti,结果与 fn(arr[i]) 相同
输入
[1, 2, 3], function (x) {
return x * x;
}
输入
4
function makeClosures(arr, fn){
let result = [];
for(let i=0;i<arr.length;i++){
result[i] = fn.bind(this.arr[i]);
}
return result;
}
ES6 新增了let命令,用来声明局部变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效,而且有暂时性死区的约束
返回函数
实现函数 functionFunction,调用之后满足如下条件:
1、返回值为一个函数 f
2、调用返回的函数 f,返回值为按照调用顺序的参数拼接,拼接字符为英文逗号加一个空格,即 ', ’
3、所有函数的参数数量为 1,且均为 String 类型
function functionFunction(str) {
return f = function(arr){
return str+", "+arr;
};
}
函数的上下文
将函数 fn 的执行上下文改为 obj 对象
输入
function () {return this.greeting + ', ’ + this.name + ‘!!!’;}, {greeting: ‘Hello’, name: ‘Rebecca’}
输出
Hello, Rebecca!!!
function speak(fn, obj) {
obj.fn = fn
return obj.fn()
}
函数传参
将数组 arr 中的元素作为调用函数 fn 的参数
function argsAsArray(fn, arr) {
return fn.apply(this,arr)
}
计时器
实现一个打点计时器,要求
1、从 start 到 end(包含 start 和 end),每隔 100 毫秒 console.log 一个数字,每次数字增幅为 1
2、返回的对象中需要包含一个 cancel 方法,用于停止定时操作
3、第一个数需要立即输出
function count(start, end) {
console.log(start); //立即输出第一个数
var timer = setInterval(()=>{
if(start<end){//从start到end
console.log(++start);//每次数字增幅为1
}
else{
clearInterval(timer);
}
},100) //100毫秒执行一次
return{//返回一个包含cancel的方法
cancel(){
clearInterval(timer);
}
}
}
正确的使用parseInt
修改 js 代码中 parseInt 的调用方式,使之通过全部测试用例
function parse2Int(num) {
var reg = /[a-zA-Z]/g;
var res = new RegExp(/^(?![^a-zA-Z]+$)/);
if(res.test(num)){
return parseInt(num.split(reg)[0]);
}
return parseInt(num);
}
正确的函数定义
请修复给定的 js 代码中,函数定义存在的问题
思路:else中的语句相当于将if中的function重写,因此无论flag为何值,返回的方法始终为重写后的方法。将方法赋值给一个变量,方法就不会被重写,因此才能得到正确的结果。
function functions(flag) {
/*if (flag) {
function getValue() { return 'a'; }
} else {
function getValue() { return 'b'; }
}*/
if(flag){
var getValue = function(){
return 'a';
}
}else{
var getValue = function(){
return 'b';
}
}
return getValue();
}
避免全局变量
给定的 js 代码中存在全局变量,请修复
思路:在Javascript语言中,声明变量使用的都是关键字var,如果不使用var而直接声明变量,则该变量为全局变量。
function globals() {
var myObject = {
name : 'Jory'
};
return myObject;
}
查找元素位置
在数组 arr 中,查找值与 item 相等的元素出现的所有位置
输入
[‘a’,‘b’,‘c’,‘d’,‘e’,‘f’,‘a’,‘b’,‘c’] ‘a’
输出
[0, 6]
function findAllOccurrences(arr, target) {
let newArr = [];
arr.forEach((el,index)=>{
if(el == target){
newArr.push(index);
}
})
return newArr;
}
求二次方
为数组 arr 中的每个元素求二次方。不要直接修改数组 arr,结果返回新的数组
function square(arr) {
return arr.map(el => Math.pow(el,2))
}
查找重复元素
找出数组 arr 中重复出现过的元素
function duplicates(arr) {
var temp = [];
arr.forEach(function(elem){
if(arr.indexOf(elem) != arr.lastIndexOf(elem)&& temp.indexOf(elem) == -1){
temp.push(elem);
}
});
return temp;
}
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。
indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
stringObject.indexOf(searchvalue,fromindex)
lastIndexOf() 方法可返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。
stringObject.lastIndexOf(searchvalue,fromindex)
计数
统计数组 arr 中值等于 item 的元素出现的次数
// 相同的加一
function count(arr, item) {
var j = 0;
arr.forEach(function (ele) {
if (ele == item)
j++;
});
return j;
}
//去除不同的,返回长度
function count(arr, item) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] != item) {
arr.splice(i, 1);
i--;
}
}
return arr.length;
}
//遇到不同的往前移动,然后返回长度
function count(arr, item) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] != item) {
for (var j = i; j < arr.length; j++) {
arr[j] = arr[j + 1];
}
arr.pop();
i--;
}
}
return arr.length;
添加元素
在数组 arr 的 index 处添加元素 item。不要直接修改数组 arr,结果返回新的数组
function insert(arr, item, index) {
var temp = arr.slice(0);//从某个已有的数组返回选定的元素
temp.splice(index,0,item);//删除元素并向数组添加新元素
return temp;
}
-
Array对象方法
方法 描述 concat() 连接两个或更多的数组,并返回结果 join() 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔 pop() 删除并返回数组的最后一个元素 push() 向数组的末尾添加一个或更多元素,并返回新的长度 reverse() 颠倒数组中元素的顺序 shift() 删除并返回数组的第一个元素 slice() 从某个已有的数组返回选定的元素 sort() 对数组的元素进行排序 splice() 删除元素,并向数组添加新元素 toSource() 返回该对象的源代码 toString() 把数组转换为字符串,并返回结果。 toLocaleString() 把数组转换为本地数组,并返回结果. unshift() 向数组的开头添加一个或更多元素,并返回新的长度 valueOf() 返回数组对象的原始值
数组合并
合并数组 arr1 和数组 arr2。不要直接修改数组 arr,结果返回新的数组
//第一种:字符串方式
function concat(arr1, arr2) {
return (arr1 + "," + arr2).split(",");
}
//第二种:原生concat方式
function concat(arr1, arr2) {
return arr1.concat(arr2);
}
//第三种:扩展运算符...
function concat(arr1, arr2) {
return [...arr1, ...arr2];
}
删除数组第一个元素
//使用shift方法
function cuttail(arr) {
var newArr = arr.slice(0);
newArr.shift();
return newArr;
}
//使用splice切除第一个元素
function cuttail(arr) {
var newArr = arr.slice(0);
return newArr.splice(0,1);
}
//使用slice赋值元素,其实可以直接return arr.slice(0);
function cuttail(arr) {
var newArr = arr.slice(0);
console.log(newArr);
return newArr.slice(1);
}
//每个元素往前移动,最后再pop一下
function cuttail(arr) {
var newArr = arr.slice(0);
for (var i = 0; i < newArr.length - 1; i++) {
newArr[i] = newArr[i + 1];
}
newArr.pop();
return newArr;
}
//使用filter函数过滤
function cuttail(arr) {
return arr.filter(function (ele,i) {
return i != 0;
});
}
添加元素
在数组 arr 开头添加元素 item。不要直接修改数组 arr,结果返回新的数组
function prepend(arr, item) {
let arr1 = [];
for(let i=0;i<arr.length;i++){
arr1[i] = arr[i]
}
arr1.unshift(item)
return arr1
}
在数组 arr 末尾添加元素 item。不要直接修改数组 arr,结果返回新的数组
function append(arr, item) {
var arr1 = arr.slice(0);
arr1.push(item);
return arr1;
}
删除数组最后一个元素
删除数组 arr 最后一个元素。不要直接修改数组 arr,结果返回新的数组
function truncate(arr) {
var newArray = arr.slice(0, arr.length - 1);
return newArray;
}
移除数组中的元素
移除数组 arr 中的所有值与 item 相等的元素,直接在给定的 arr 数组上进行操作,并将结果返回
indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
function removeWithoutCopy(arr, item) {
while(arr.indexOf(item)!==-1){
arr.splice(arr.indexOf(item),1);
}
return arr;
}
移除数组 arr 中的所有值与 item 相等的元素。不要直接修改数组 arr,结果返回新的数组
function remove(arr, item) {
var arr2=arr.filter(value=>{return value!=item})
return arr2
}
数组求和
计算给定数组arr中所有元素的总和
function sum(arr) {
var suma = 0;
for(var i = 0; i < arr.length; i++){
suma += arr[i];
}
return suma;
}
//函数式编程
function sum(arr) {
return arr.reduce(function(prev, curr, idx, arr){
return prev + curr;
});
}
//foreach方法
function sum(arr) {
var s = 0;
arr.forEach(function(val, idx, arr) {
s += val;
}, 0);
return s;
};
//eval
function sum(arr) {
return eval(arr.join("+"));
};
查找数组元素位置
找出元素 item 在给定数组 arr 中的位置
function indexOf(arr, item) {
while(arr.length > 0) {
if (arr.pop() == item)
return arr.length;
}
return -1;
}
设置文字颜色
请使用嵌入样式将所有p标签设置为红色文字
<p>欢迎来到牛客网</p>
<p>在这里,我们为你提供了IT名企的笔试面试题库</p>
<p>在这里,我们以题会友</p>
<p>QQ群号:272820159</p>
var aP = document.getElementsByTagName('p');
for(var i=0;i<aP.length;i++)
aP[i].style.color = 'red';
段落标识
请将下面这句话以段落的形式展示在浏览器中——“牛客网是一个专注于程序员的学习和成长的专业平台。”
加粗文字
使用一个标签将“牛客网”三个字加粗显示
<p>牛客网,程序员必备求职神器</p>
let p = document.querySelector('p');
let t= p.innerText;
t = t.splice('牛客网').join('<strong>牛客网</strong>');
p.innerHTML = t;
方法2
let p = document.getElementsByTagName("p")[0]
p.innerHTML = p.innerText.replace("牛客网","<strong style='font-weight:999'>牛客网</strong>")
字符串字符统计
统计字符串中每个字符的出现频率,返回一个 Object,key 为统计字符,value 为出现频率
- 不限制 key 的顺序
- 输入的字符串参数不会为空
- 忽略空白字符
function count(str) {
let obj = {}
for(var i = 0;i<str.length;i++){
if(str[i]!==''){
var key = str[i];
if (obj[key] !== undefined){
obj[key] = obj[key] + 1;
}
else{
obj[key]=1;
}
}
}
return obj
}
将字符串转换成驼峰格式
css 中经常有类似 background-image 这种通过 - 连接的字符,通过 javascript 设置样式的时候需要将这种样式转换成 backgroundImage 驼峰格式,请完成此转换功能
- 以 - 为分隔符,将第二个起的非空单词首字母转为大写
- -webkit-border-image 转换后的结果为 webkitBorderImage
function cssStyle2DomStyle(sName) {
return sName.replace(/^-/, '').replace(/-([a-z])/g, (_, $) => $.toUpperCase());
}
数组去重
为 Array 对象添加一个去除重复项的方法
Array.prototype.uniq = function () {
return Array.from(new Set(this))
}
Set数据结构类似于数组,但里面的成员都是唯一的,判断是否唯一的标准基本等同于===,唯一的区别在于, ‘===’判断是NaN与NaN不相等,但Set会认为它们相等并去重。
由于Set只是类似数组,所以要用Array.from返回一个真正的数组。
根据包名,在指定空间中创建对象
function namespace(oNamespace, sPackage) {
var list = sPackage.split('.');
if (list[0] === '') {
return;
}
if (oNamespace[list[0]] instanceof Object) {
namespace(oNamespace[list[0]], list.slice(1).join('.'));
}
else{
oNamespace[list[0]] = {};
namespace(oNamespace[list[0]], list.slice(1).join('.'));
}
};
dom节点查找
查找两个节点的最近的一个共同父节点,可以包括节点自身
思路:判断一个节点以及它的父节点是否包含另一个节点。
function commonParentNode(oNode1, oNode2) {
while(true){
oNode1 = oNode1.parentNode;
if(oNode1.contains(oNode2)){
return oNode1;
}
}
}
function commonParentNode(oNode1, oNode2) {
let parent1 = oNode1.parentNode;
let parent2 = oNode2.parentNode;
let parent3,parent4;
let res = commonParent(parent1,parent2);
return res;
function commonParent(parent1,parent2){
if(parent1 === parent2){
return parent1;
}else{
parent3 = parent1.parentNode;
parent4 = parent2.parentNode;
//递归调用
commonParent(parent3,parent4);
}
}
}
获取url参数
- 指定参数名称,返回该参数的值 或者 空字符串
- 不指定参数名称,返回全部的参数对象 或者 {}
- 如果存在多个同名参数,则返回数组
输入
http://www.nowcoder.com?key=1&key=2&key=3&test=4#hehe key
输出
[1, 2, 3]
function getUrlParam(sUrl, sKey) {
var paramArr = sUrl.split('?')[1].split('#')[0].split('&'); // 取出每个参数的键值对放入数组
const obj = {};
paramArr.forEach(element => {
const [key, value] = element.split('='); // 取出数组中每一项的键与值
if(obj[key] === void 0){ // 表示第一次遍历这个元素,直接添加到对象上面
obj[key]=value
} else{
obj[key]=[].concat(obj[key],value); // 表示不是第一次遍历说明这个键已有,通过数组存起来。
}});
return sKey===void 0? obj:obj[sKey]||'' // 如果该方法为一个参数,则返回对象。
//如果为两个参数,sKey存在,则返回值或数组,否则返回空字符。
}
修改this指向
封装函数 f,使 f 的 this 指向指定的对象
function bindThis(f,oTarget){
let args = Array.prototype.slice.call(arguments,2);
return function(){
return f.apply(oTarget,Array.prototype.slice.call(arguments).concat(args));
}
}
箭头函数
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或new.target。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数
-
基本语法
(param1, param2, …, paramN) => { statements } (param1, param2, …, paramN) => expression //相当于:(param1, param2, …, paramN) =>{ return expression; } // 当只有一个参数时,圆括号是可选的: (singleParam) => { statements } singleParam => { statements } // 没有参数的函数应该写成一对圆括号。 () => { statements }
-
高级语法
//加括号的函数体返回对象字面量表达式: params => ({foo: bar}) //支持剩余参数和默认参数 (param1, param2, ...rest) => { statements } (param1 = defaultValue1, param2, …, paramN = defaultValueN) => { statements } //同样支持参数列表解构 let f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c; f(); // 6
引入箭头函数有两方面的作用:更简短的函数
并且不绑定this
var elements = [
'Hydrogen',
'Helium',
'Lithium',
'Beryllium'
];
elements.map(function(element) {
return element.length;
}); // 返回数组:[8, 6, 7, 9]
//上面的普通函数可以改写成如下的箭头函数
elements.map((element)=>{
return element.length;
});
// 当箭头函数只有一个参数时,可以省略参数的圆括号
elements.map(element => {
return element.length;
}); // [8, 6, 7, 9]
// 当箭头函数的函数体只有一个 `return` 语句时,可以省略 `return` 关键字和方法体的花括号
elements.map(element => element.length); // [8, 6, 7, 9]
// 在这个例子中,因为我们只需要 `length` 属性,所以可以使用参数解构
// 需要注意的是字符串 `"length"` 是我们想要获得的属性的名称,而 `lengthFooBArX` 则只是个变量名,
// 可以替换成任意合法的变量名
elements.map(({ "length": lengthFooBArX }) => lengthFooBArX); // [8, 6, 7, 9]
- 没有单独的this
在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的this值:- 如果是该函数是一个构造函数,this指针指向一个新的对象
- 在严格模式下的函数调用下,this指向undefined
- 如果是该函数是一个对象的方法,则它的this指针指向这个对象