1. == 与 === 的区别
①如果是两个相同类型的原始类型数据相比,==和===一样,都是比较他们的值。
②如果两个都是引用类型,==和===一样,都是看他们是否指向同一个对象。
③如果两个类型不同,===直接返回false,==会进行数据格式。如果是两个基本类型相比,==会把他们都转换为Number型,比较他们实际的值;如果是一个基本类型一个引用类型,则会调用引用类型的valueOf函数,得到他的原始值再与基本类型的数进行判断。
④null与undefined都与自身严格相等;但是null与undefined是普通相等。
注意下面这种情况也是相等的哦~
var arr = ['a'];
arr.valueOf = function(){
return '1';
};
var num = 1;
arr.toString = function(){
return 'b';
}
console.log(arr == num);//true
会先调用arr的valueOf得到'1'这个字符串,再将字符串转成数值1与num进行比较。
2.实现add(1)(2)(3)
只有充分理解闭包,才能做对的一道题。
原本应该写成
function add(x){
return function(y){
return function(z){
return x+y+z;
}
}
}
但是当我再增加一个参数的时候,这个就又要再return,肯定是不可取的。
我们要做的是return出去一个函数,里面包含本次传来的参数:
function add(x){
var temp = function(y){
return add(x+y);
};
return temp;
};
由于我们要打印成字符串或是显示他原始值,因此我们需要:
//JavaScript中,打印和相加计算,会分别调用toString(获得字符串)或valueOf(获得原始值)函数
//所以我们重写tmp的toString和valueOf方法,返回sum的值
temp.valueOf = temp.toString = function(){
return x;
}
完整的版本是:
function add(x){
var temp = function(y){
return add(x+y);
};
//JavaScript中,打印和相加计算,会分别调用toString(获得字符串)或valueOf(获得原始值)函数
//所以我们重写tmp的toString和valueOf方法,返回sum的值
temp.valueOf = temp.toString = function(){
return x;
}
return temp;
};
十几天之后我又看到一道类似的题,但是他要求更加严格了,需要这个函数也能实现add(1,2,3)。
function add(x) {
if(arguments.length > 1) {
var sum = 0;
for(var i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
} else {
var sum = x;
var tmp = function(y) {
sum = sum + y;
return tmp;
};
tmp.toString = function() {
return sum;
};
return tmp;
}
}
其实只是加了判断是否参数大于1,如果参数大于1可以直接加,如果不是就要使用我们之前的函数了。
3.验证素数
首先了解素数的含义:任何只能被1和自身整除的大于1的数。所以我们首先排除小于等于1的,其次搞清楚这个数如果再自己的一半这个数之前不能被整除,那他就永远不能被整除了,所以我们的循环只需要写到他的一半即可。
function isPrime(n){
if(n<=1) return false;
for(var i=2; i<=n/2; i++){
if(n%i === 0) return false;
}
return true;
}
4.统计一个字符串出现最多的字母
首先我们需要声明一个对象,他的属性是传入字符串所拥有的字母,对应的值是字母重复的次数,最后我们通过比较出对象中最大值,找到字符串中重复次数最多的字母。得到对象可以通过先将字符串转化成数组,再使用forEach参数element、i、arr得到这个数组从i位之后是否还包含element。
function count(str){
var arr = str.split("");
var obj = {};
arr.forEach(function(element,i,arr) {
obj[element] = obj[element]||0;
if(arr.slice(i+1,arr.length).indexOf(element) !== -1){
obj[element]++;
}
}, this);
var maxChar = '',maxValue = 1;
for(var i in obj){
if(obj[i]>=maxValue){
maxChar += i;
maxValue = obj[i];
}
};
return maxChar;
};
从其他地方看到一种获得对象的方法是通过charAt函数,此法不需要先把字符串转化为数组,很方便。
let obj = {};
for(var i=0; i<str.length; i++){
if(!obj[str.charAt(i)]){
obj[str.charAt(i)] = 1;
}else{
obj[str.charAt(i)] += 1;
}
}
5.数组从小到大或从大到小排列
冒泡排序:掌握一个重点,声明一个中间量,先将小的给中间量,再将大的给小的位置上,将中间量的值给大的位置,完成转换。
function bubbleSort(arr){
for(let i=0; i<arr.length; i++){
for(let j=i+1;j<arr.length; j++){
if(arr[i]>arr[j]){
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
插入排序:插入排序的重点是选取一个关键数,并将此数与他之前排着的已经排序完毕的数进行比较,如果一直比那些数小,则把关键数前面的一个数向后错一位,直到找到比关键数小的数,就把关键数插到这个数的后面。
function insertSort(arr){
for(let i=1; i<arr.length; i++){
let temp = arr[i];
let j= i-1;
while(j>0 && arr[j]>temp){
arr[j+1] = arr[j];
j--;
}
arr[j+1] = temp;//注意是j+1不是j,因为你是要把temp插到小于他的数后面
}
return arr;
}
快速排序:找到中间那个点并从数组中取出他,并将小于它的放在左边,大于他的放在右边,然后再对左右两个数组进行递归快速排序,直到这个数组只有中间一个值构成的数组时,就返回这个数组(这是最底层)。
function quickSort(arr){
if(arr.length<=1){
return arr;//如果数组只有一个数,就直接返回;
}
var num = Math.floor(arr.length/2);//找到中间数的索引值,如果是浮点数,则向下取整
var numValue = arr.splice(num,1);//找到中间数的值并取出他
var left = [];
var right = [];
for(var i=0;i<arr.length;i++){
if(arr[i]<numValue){
left.push(arr[i]);//基准点的左边的数传到左边数组
}
else{
right.push(arr[i]);//基准点的右边的数传到右边数组
}
}
return quickSort(left).concat(numValue,quickSort(right));//递归不断重复比较
}
6.实现类似getElementsByClassName 的功能
本题最重要的是要搞清楚className这个DOM属性,它的样子是"className1 className2……"可以选择两种方式。
首先是简单的分成数组进行判断:
function getElementsByClassName(oEle,sClass){
var aEle = oEle.getElementsByTagName('*');
var arr = [];
for(var i=0; i<aEle.length; i++){
var arrClass = [];
arrClass = aEle[i].className.split(" ");
if(arrClass.includes(sClass)){
arr.push(aEle[i]);
}
}
return arr;
}
也可以使用正则:
function getElementsByClassName(oEle,sClass){
var aEle = oEle.getElementsByTagName('*'),
//reg = new RegExp('(^|[ \n\r\t\f])'+sClass+'($|[ \n\r\t\f])'),
reg = new RegExp('(^|[ \s])'+sClass+'($|[ \s])'),
//\n 匹配一个换行符
//\r 匹配一个回车符
//\t 匹配一个制表符
//\f 查找换页符
//\s与\n\r\t\f是等效果的,但是他不包括空格,所以需要加上空格
arr = [];
for(var i=0; i<aEle.length; i++){
if(reg.test(aEle[i].className)){
//className是"myDic myDiv"这种形式
arr.push(aEle[i]);
}
}
return arr;
}
很多地方都能用到正则,正则帮助我们简化代码。
如果我们想获得url的某个参数的值,采用正则会非常简单:
function getURL(name){
let search = window.location.search,
reg = new RegExp("[^&?]*"+name+"=([^&]*)","g"),//name前面0~无数个不为&或?的字符,后面0~无数个不为&的字符
result = search.substring(1).match(reg);
return result;
}
如果我们使用split先根据&分再根据=分会很麻烦:
function getParam(){
//window.location.search是包括?的
var search = window.location.search.substring(1);
var arr = search.split("&");
var obj = {};
for(var i=0; i<arr.length; i++){
obj[arr[i].split("=")[0]]=arr[i].split("=")[1];
}
return obj;
};
7.实现一个去除字符串前后空格trim的函数
function trim(str){
//判断这个字符串的类型是否为string
if(str&&typeof(str)==='string'){
return str.replace(/^(\s)|(\s)$/g,"");
}
}
8.降维数组
我想的是使用扩展字符串...
function Jw(obj){
return [].concat(...obj);
}
看到一个答案是使用Array.prototype.concat.apply,和我这个大同小异,只是一定要注意apply从第二个参数是作为参数的数组,数组中的每一项都是前面函数(concat)的参数。
function Jw(obj){
return Array.prototype.concat.apply([],obj);
}
9.自定义一个bind函数
思想是先拿到此时的this,最后要返回一个函数,这个函数里要返回“改变this”
Function.prototype.myBind = function(){
var self = this;
var content = Array.prototype.slice.call(arguments)[0];
var args = Array.prototype.slice.call(arguments,1);
return function(){
return self.apply(content,args);
}
};
var obj = {
name:'Ann',
getName: function(){
return this.name;
}
};
console.log(obj.getName());//Ann
var getName2 = obj.getName;
console.log(getName2());//空
var getName3 = obj.getName.myBind(obj);
console.log(getName3());//Ann
10.实现已经排序好的两个数组的排序合并
我犯了一个错误(i<a.length&&j<b.length)导致如果两个数组个数不一样就会扫不完长的那个。一定要扫完所有,所以需要判断:如果a已经扫完需要将b送进去,又或者a大于b而b也确实没扫完,此时也将b送进去。
function mergeSortedArray(a,b){
var arr = [],i=0,j=0;
if(!a.length) arr=b;
if(!b.length) arr=a;
while(i<a.length||j<b.length){
if(a[i]>b[j]&&j!==b.length||i===a.length){
arr.push(b[j]);
j++;
}else{
arr.push(a[i]);
i++;
}
}
return arr;
}
11.实现单词翻转和字符翻转
单词只需要翻转一遍,通过空格将他们拆成一个一个数组:
function reverseStr(str){
var arr = str.split(" ");
return arr.reverse().join(" ");
}
字符反转需要翻转两遍,首先单词翻转,再整个句子翻转,实现内部字符翻转:
function reverseWord(str){
var arr = str.split(" ");
return arr.reverse().join(" ").split("").reverse().join("");//反转两次
}
12.查找这个数组中是否有两个数的和满足给定值
重点是扫到的每个数,将每个数与给定值的差值存入对象作为属性,接下来检测这个数是否是对象中的属性。
function twoSum(arr,sum){
var obj = {};
for(var i=0; i<arr.length; i++){
var n = sum-arr[i];
if(!obj[n]){
obj[arr[i]]=true;
}else{
return true;
}
}
return false;
}
13.实现js的五种基本类型(String,Number,Boolean,Array,Object)的值的复制
function copy(a){
if(a instanceof Object){
var obj = a instanceof Array? []:{};
for(var i in a){
obj[i] = copy(a[i]);
}
return obj;
}else{
return a;
}
}
重点是验证的过程:
var b = {
c:[12,3],
d:{
success:"success",
bad: {
"error":"error"
}
}
}
var bb = copy(b);
bb.t = 3;
console.log(b);
根据结果我们发现b上并未添加属性t,也就是bb与b是两块不同的对象。这就有区别于浅拷贝。
var cc = Object.assign(b);//浅拷贝
cc.t = 4;
console.log(b);//b上可以发现属性t的值为4
14.二维数组全排列
思路:将内部第一个数组扫进去形成三个数组['A','B','C']。将第二个数组中的每一个元素都分别排在他们后面形成3*3=9种方式,以此类推。
function getArrayByArrays(arrays){
var arr = [""];
for(let i=0; i<arrays.length; i++){
arr = getValuesByArray(arr, arrays[i]);
}
return arr;
}
function getValuesByArray(arr1,arr2){
var a = [];
for(let i=0; i<arr1.length; i++){
for(let j=0; j<arr2.length; j++){
a.push(arr1[i]+arr2[j]);
}
}
return a;
}
var arr=[['A','B','C'],['A1','B1','C1'],['A2','B2']];
var array = getArrayByArrays(arr);
console.log(array);
15.青蛙跳台阶(递归、尾递归、generator)
思路:n=100时递归会造成内存溢出,所以采用尾递归、generator的方式。尾递归每次记录上一次的结果,generator也类似,只不过使用for循环进行one、two的更新。
//递归
function step(n){
if(n<=1) return n;
return step(n-1)+step(n-2);
}
//尾递归
function step1(n, one, two){
if(n<=1) return two;
return step1(n-1, two, one+two);
}
//generator
function* step2(n){
var one = 0, two = 1;
for(var i=0; i<n; i++){
[one, two] = [two, one+two];
yield one;
}
}
console.log(step(5));
console.log(step1(5, 0, 1));
var gen = step2(5);
for(let i of gen){
console.log(i);
}
generator可以采用for/of循环代替调用next~