function duplicates() {
var arr = [1, 2, 4, 4, 3, 3, 1, 5, 3]
var temp = [];
arr.forEach(function(elem){
if(arr.indexOf(elem) != arr.lastIndexOf(elem) && temp.indexOf(elem) == -1){
temp.push(elem);
}
});
return temp;
}
做法:
1.找出重复的数组项‘[1,4,4,3,3,1,3]
2.在重复的数组项中做一个去重,筛选出[1,3,4]
1.它是最简单的数组去重方法(indexOf方法)
实现思路:新建一个数组,遍历去要重的数组,当值不在新数组的时候(indexOf为-1)就加入该新数组中;
var arr=[2,8,5,0,5,2,6,7,2];
function unique1(arr){
var hash=[];
for (var i = 0; i < arr.length; i++) {
if(hash.indexOf(arr[i])==-1){
hash.push(arr[i]);
}
}
return hash;
}
2.数组下标判断法
调用indexOf方法,性能和方法1差不多
实现思路:如果当前数组的第 i 项在当前数组中第一次出现的位置不是 i,那么表示第 i 项是重复的,忽略掉。否则存入结果数组。
function unique2(arr){
var hash=[];
for (var i = 0; i < arr.length; i++) {
if(arr.indexOf(arr[i])==i){
hash.push(arr[i]);
}
}
return hash;
}
1、XXX-XXX-XXXX 2、其中 X 为 Number 类型
首先:(\d{3}-) 重复了两次,所以是(\d{3}-){2}。即XXX-XXX-。
然后:d{4}。即XXXX。
最后,全局匹配,所以加上开始符^和结束符$。
*/
function matchesPattern(str) {
var res=/^(\d{3}\-){2}\d{4}$/;
return res.test(str);
}
console.log(matchesPattern('800-555-1212'));
## 4.给定字符串 str,检查其是否包含 连续3个数字 1、如果包含,返回最先出现的 3 个数字的字符串 2、如果不包含,返回 false
function captureThreeNumbers(str) {
var reg;
if(reg = str.match(/(\d{3})/)){
return reg[0];
}else{
return false;
}
}
## 5.题目描述 找出元素 item 在给定数组 arr 中的位置 输出描述: 如果数组中存在 item,则返回元素在数组中的位置,否则返回 -1
function indexOf(arr, item) {
if (Array.prototype.indexOf){ //判断当前浏览器是否支持
return arr.indexOf(item);
} else {
for (var i = 0; i < arr.length; i++){
if (arr[i] === item){
return i;
}
}
}
return -1; //总是把return -1暴漏在最外层
}
## 6.题目描述 计算给定数组 arr 中所有元素的总和 输入描述: 数组中的元素均为 Number 类型
function sum(arr) {
var arr
var sum = 0
for(var i =0;i str3 => fn(str1, str2, str3);
## 19.题目描述 函数 useArguments 可以接收 1 个及以上的参数。请实现函数 useArguments,返回所有调用参数相加后的结果。本题的测试参数全部为 Number 类型,不需考虑参数转换。
这里重点就是类数组对象:arguments -- 接收所有传入函数的参数值的类数组对象,它有两个特点跟数组很像,1.可以用下标访问每个元素2.具有length属性。
这里最好先通过Array.prototype.slice.call(我们的类数组对象) 将其转换成一个真正的数组对象,然后再遍历求和即可。
function useArguments() {
var aArguments=Array.prototype.slice.call(arguments);
var sum=0;
aArguments.forEach(function(value){
sum+=value;
});
return sum;
}
## 20.题目描述 实现函数 callIt,调用之后满足如下条件 1、返回的结果为调用 fn 之后的结果 2、fn 的调用参数为 callIt 的第一个参数之后的全部参数
function callIt(fn) {
let args = Array.prototype.slice.call(arguments,1)
return fn.apply(this,args)
}
## 21.题目描述 实现函数 partialUsingArguments,调用之后满足如下条件: 1、返回一个函数 result 2、调用 result 之后,返回的结果与调用函数 fn 的结果一致 3、fn 的调用参数为 partialUsingArguments 的第一个参数之后的全部参数以及 result 的调用参数
function partialUsingArguments(fn) {
let args1 = Array.prototype.slice.call(arguments, 1); //获得函数partialUsingArguments() 除第一个参数外的参数数组
return function() {
let args2 = Array.prototype.slice.call(arguments, 0) //获得函数的参数数组
return fn.apply(this, args1.concat(args2))
}
}
## 22.什么是柯里化?
维基百科上说道:柯里化,英语:Currying(果然是满满的英译中的既视感),是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
curry的一些性能问题你只要知道下面四点就差不多了:
存取arguments对象通常要比存取命名参数要慢一点
一些老版本的浏览器在arguments.length的实现上是相当慢的
使用fn.apply( … ) 和 fn.call( … )通常比直接调用fn( … ) 稍微慢点
创建大量嵌套作用域和闭包函数会带来花销,无论是在内存还是速度上
其实在大部分应用中,主要的性能瓶颈是在操作DOM节点上,这js的性能损耗基本是可以忽略不计的,所以curry是可以直接放心的使用。
## 23. 实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;
function add() {
// 第一次执行时,定义一个数组专门用来存储所有的参数
var _args = Array.prototype.slice.call(arguments);
// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
var _adder = function() {
_args.push(...arguments);
return _adder;
};
// 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
});
}
return _adder;
}
add(1)(2)(3) // 6
add(1, 2, 3)(4) // 10
add(1)(2)(3)(4)(5) // 15
add(2, 6)(1) // 9
## 24.题目描述 已知 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 的调用参数
柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。简单理解题目意思,就是指,我们将预定义的函数的参数逐一传入到curryIt中,当参数全部传入之后,就执行预定义函数。于是,我们首先要获得预定义函数的参数个数fn.length,然后声明一个空数组去存放这些参数。返回一个匿名函数接收参数并执行,当参数个数小于fn.length,则再次返回该匿名函数,继续接收参数并执行,直至参数个数等于fn.length。最后,调用apply执行预定义函数。
function curryIt(fn) {
//获取fn参数的数量
var n = fn.length;
//声明一个数组args
var args = [];
//返回一个匿名函数
return function(arg){
//将curryIt后面括号中的参数放入数组
args.push(arg);
//如果args中的参数个数小于fn函数的参数个数,
//则执行arguments.callee(其作用是引用当前正在执行的函数,这里是返回的当前匿名函数)。
//否则,返回fn的调用结果
if(args.length < n){
return arguments.callee;
}else return fn.apply("",args);
}
}
## 25.题目描述 完成函数 createModule,调用之后满足如下要求: 1、返回一个对象 2、对象的 greeting 属性值等于 str1, name 属性值等于 str2 3、对象存在一个 sayIt 方法,该方法返回的字符串为 greeting属性值 + ', ' + name属性值
function createModule(str1, str2) {
var obj = {
greeting:str1,
name:str2,
sayIt : function (){
return this.greeting + ', ' + this.name //注意逗号后面有空格
}
}
return obj
}
## 26.题目描述 求 a 和 b 相乘的值,a 和 b 可能是小数,需要注意结果的精度问题
function multiply(a, b) {
return parseFloat((a*b).toFixed(10));
}
## 27.题目描述 将函数 fn 的执行上下文改为 obj,返回 fn 执行后的值
在JavaScript中,函数是一种对象,其上下文是可以变化的,对应的,函数内的this也是可以变化的,函数可以作为一个对象的方法,也可以同时作为另一个对象的方法,可以通过Function对象中的call或者apply方法来修改函数的上下文,函数中的this指针将被替换为call或者apply的第一个参数。将函数 fn 的执行上下文改为 obj 对象,只需要将obj作为call或者apply的第一个参数传入即可。
共有四种正确方案(按提交的运行时间由快到慢排列):
1. apply 方法改变函数 this 值(平均 172ms )
function alterContext(fn, obj) {
return fn.apply(obj);
}
2. call 方法改变函数 this 值(178 ms)
function alterContext(fn, obj) {
return fn.call(obj);
}
3. bind 方法创建指定 this 值的函数实例(208 ms)
function alterContext(fn, obj) {
const bindedFn = fn.bind(obj);
return bindedFn();
}
4. 给 obj 增加 fn 方法(213 ms)
function alterContext(fn, obj) {
obj.fn = fn;
return obj.fn();
}
## 28.给定一个构造函数 constructor,请完成 alterObjects 方法,将 constructor 的所有实例的 greeting 属性指向给定的 greeting 变量。
function alterObjects(constructor, greeting) {
constructor.prototype.greeting = greeting;
}
## 29.题目描述 找出对象 obj 不在原型链上的属性(注意这题测试例子的冒号后面也有一个空格~) 1、返回数组,格式为 key: value 2、结果数组不要求顺序
题目要求找不在原型链上的属性,即返回实例属性,有3种正确方法(按提交运行速度由快到慢排列):
1. Object.keys 方法(156 ms)
返回可枚举的实例属性的数组。
function iterate(obj) {
return Object.keys(obj).map(function(key) {
return key + ": " + obj[key];
});
}
2. for-in 和 hasOwnProperty 方法(171 ms)
前者用于遍历所有属性,后者用于判断是否为实例属性。
function iterate(obj) {
const res = [];
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
res.push(prop + ": " + obj[prop]);
}
}
return res;
}
3. Object.getOwnPropertyNames 方法(209 ms)
用法跟1一样,区别在于返回的是所有实例属性(包括不可枚举的)。
function iterate(obj) {
return Object.getOwnPropertyNames(obj).map(function(key) {
return key + ": " + obj[key];
});
}
注意:
1. 居然不支持 let。。。。。。改成 var 就行了
2. 是的,箭头函数也不支持。。。
3. 题目没有说明返回的实例属性是否包括不可枚举的,这里理解为包括。否则,由于 Object.keys() 返回的仅为可枚举的实例属性,应该无法通过。
## 30.题目描述 给定字符串 str,检查其是否包含数字,包含返回 true,否则返回 false
function containsNumber(str) {
return /\d/.test(str)
}
## 31.题目描述 给定字符串 str,检查其是否包含连续重复的字母(a-zA-Z),包含返回 true,否则返回 false
function containsRepeatingLetter(str) {
return /([a-zA-Z])\1/.test(str); //\1表示重复正则第一个圆括号内匹配到的内容; \2表示重复正则第二个圆括号内匹配到的内容
}
## 32.题目描述 获取数字 num 二进制形式第 bit 位的值。注意: 1、bit 从 1 开始 2、返回 0 或 1 3、举例:2 的二进制为 10,第 1 位为 0,第 2 位为 1
function valueAtBit(num, bit) {
return (num >> (bit -1)) & 1;
}
## 33.题目描述 给定二进制字符串,将其换算成对应的十进制数字
function base10(str) {
/**
其它进制转十进制
parseInt(str,2)
parseInt(str,8)
parseInt(str,16)
*/
return parseInt(str,2);
}
## 34.题目描述 将给定数字转换成二进制字符串。如果字符串长度不足 8 位,则在前面补 0 到满8位。
首先通过toString方法将num转为2进制数形式,然后判断其长度是否足够8位。如不足8位,则声明一个“0000000”字符串用于补0,因为目标的2进制数形式最少为一位,因此最多只需要7个0;通过slice方法对“0000000”进行截取,然后将其结果加在目标前面即可。
unction convertToBinary(num){
//转换为2进制格式
var s = num.toString(2);
//获得2进制长度
var len = s.length
if(len < 8){
//声明一个字符串用于补满0
var s1 = '00000000'
var s2 = s1.slice(0,8-len)
s=s2 + s
}
return s
}