前端算法类面试总结(持续更新...)

1.字符串反转,即"I am Tony" 变成"Tony am I"

这道题本来是很简单的,str.split("").reverse().join() 即可,但是题目说明单词间有可能有多个空格,比如

var s = "I     am Tony";
//这时需要给split函数传递正则

s.split(/\s+/)
  .reverse()
  .join(" ");
//"Tony am I"
复制代码

2.写一个 js 脚本整理文件

整理前:

111.232.213 ascqwdwd
111.232.213 qwdqwdqw
122.31.34.1 wdojqwodjqwp
232.34.13.3 adhwdhwqhd
复制代码

整理后:

'111.232.213': [ 'ascqwdwd', 'qwdqwdqw' ],
'122.31.34.1': [ 'wdojqwodjqwp' ],
'232.34.13.3': [ 'adhwdhwqhd' ]
复制代码

解析:读取文件,遍历每行,去掉头尾空白以后以空格风格,以 js 的 hashmap 存储

var fs = require("fs");
function processFile(filepath) {
  fs.readFile(filepath, "utf-8", (err, data) => {
    if (err) throw err;
    var arr = data.split("\n");
    var res = {};
    arr.forEach(function(item) {
      var temp = item.trim().split(" ");
      //如果没有该ip,则初始化该ip数组
      if (!res[temp[0]]) {
        res[temp[0]] = [];
      }
      res[temp[0]].push(temp[1]);
    });
    console.log(res);
  });
}
processFile("./test.txt");
复制代码

3. Javascript 字符串模板

需要将字符串中所有使用花括号括起来的关键词,同义替换为对象字面量中对应的键值; eg: 字符串:{text} 对象字面量:{ href:'www.zyy1217.com' , text:'媛媛小窝'}}

解析: 实现一个 render(template, context) 方法,将 template 中的占位符用 context 填充;要求: 不需要有控制流成分(如 循环、条件 等等),只要有变量替换功能即可 级联的变量也可以展开 被转义的的分隔符 { 和 } 不应该被渲染,分隔符与变量之间允许有空白字符 代码

function render(template, context) {
  //匹配{}和前面的转义字符
  var reg = /^<(.*?) \{(\w+)\}<\/(\1)>$/;
  var reg = /(\\)?\{([^\{\}])(\\)?\}/;
  template.replace(reg, function(word, slash1, token, slash2) {
    if (slash1 || slash2) {
      return word.replace("\\", "");
    }
    var current = context;
    var variables = token.replace(/\s/g, "").split(".");
    for (var i = 0; i < variables.length; i++) {
      current = current[variables[i]];
      if (!current) return "";
    }
    return current;
  });
}
var template = '<a href="{href}">{text}</a>';
var context = {
  href: "www.zyy1217.com",
  text: "媛媛小窝"
};
render(template, context);
复制代码

4. 写一个求和的函数 sum,达到下面的效果

// Should equal 15
sum(1, 2, 3, 4, 5);
// Should equal 0
sum(5, null, -5);
// Should equal 10
sum(
  "1.0",false,1,true,1,"A",1,"B",1,"C",1,"D",1,"E",1,"F",1,"G",1
);
// Should equal 0.3, not 0.30000000000000004
sum(0.1, 0.2);
复制代码

对于小数和小整数的简单运算可用如下方式,但是,可能会有溢出情况:

默认值

function numAdd(num1 /*:String*/, num2 /*:String*/) {
  var baseNum, baseNum1, baseNum2;
  try {
    baseNum1 = num1.split(".")[1].length;
  } catch (e) {
    baseNum1 = 0;
  }
  try {
    baseNum2 = num2.split(".")[1].length;
  } catch (e) {
    baseNum2 = 0;
  }
  baseNum = Math.pow(10, Math.max(baseNum1, baseNum2));
  return (num1 * baseNum + num2 * baseNum) / baseNum;
}
复制代码

完整代码如下:

function sum() {
  var arr = [].slice.call(arguments);
  return arr.reduce(function(prev, next) {
    if (isNaN(next)) return prev;
    basenum1 = prev.toString().split(".")[1]
      ? prev.toString().split(".")[1].length
      : 0;
    basenum2 = next.toString().split(".")[1]
      ? next.toString().split(".")[1].length
      : 0;

    basenum = Math.pow(10, Math.max(basenum1, basenum2));
    return (prev * basenum + next * basenum) / basenum;
  });
}
复制代码

5.js 判断对象是否为空

假如是 a={},这时候如果用 if(!a) isEmptyObject 的实现,jQuery 中有一个很有想法的方式

function isEmptyObject(obj) {
  for (var key in obj) {
    return false;
  }
  return true;
}
复制代码

6. 求 add(1)(2)(3),其中调用次数未限定

看到这个就想到了闭包,通过闭包保存上次相加的结果,以下代码实现:

function add(n) {
  var fn = function(x) {
    return add(n + x);
  };
  return fn;
}
复制代码

理论上这样就解决问题了,但在调试中我们会发现,当函数调用完毕后,输出的结果并不是 sum,而是 add 函数,因为我们每次调用后都 return 函数自身;那现在需要解决的问题就是如何让函数输出 sum 值;

首先要知道 JavaScript 中,打印和相加计算,会分别调用 toString 或 valueOf 函数,所以我们重写 tmp 的 toString 或者 valueOf 方法,返回 sum 的值;

function add(n) {
  var fn = function(x) {
    return add(n + x);
  };
  fn.valueOf = function() {
    return n;
  };
  return fn;
}
复制代码

ok,现在我们通过递归返回闭包的方法解决了问题;可现在,如果 add 参数个数不固定,比如要实现 add(2,3)(5), 上面的方法就不适用了; 解决方法如下:

function add() {
  var sum = 0;
  var arr = [].slice.call(arguments);
  sum = arr.reduce(function(prev, next) {
    return prev + next;
  });
  // for ( var index in arguments){
  // 	sum = sum+ arguments[index];
  // }
  var fn = function() {
    // for (var val in arguments){
    // 	sum += arguments[val];
    // }
    var arr = [].slice.call(arguments);
    sum += arr.reduce(function(prev, next) {
      return prev + next;
    });
    return fn;
  };
  fn.toString = function() {
    return sum;
  };
  return fn;
}
复制代码

注意:这里的 arguments 是参数对象,不是数组;因此如果想要获得参数有两种方法

调用 Array.slice 将 arguments 转化为数组 var arr = [].slice.call(arguments) 使用 for in 遍历 argumens 对象;这里要注意,for in 遍历实例和原型的可枚举属性;

for (var val in arguments) {
  sum += arguments[val];
}
复制代码

7. 求 1000 以内的质数

function prime(n) {
  var arr = [];
  for (var i = 2; i < n; i++) {
    for (var j = 2; j < Math.sqrt(i); j++) {
      if (i % j === 0) {
        break;
      }
    }
    if (j >= Math.sqrt(i)) {
      arr.push(i);
    }
  }
  return arr;
}
复制代码

8. 下面的代码会输出什么?为什么?

    console.log(1 + "2" + "2");
    console.log(1 + +"2" + "2");
    console.log(1 + -"1" + "2");
    console.log(+"1" + "1" + "2");
    console.log( "A" - "B" + "2");
    console.log( "A" - "B" + 2);
    //输出什么,自己去运行吧,需要注意三个点:

    //多个数字和数字字符串混合运算时,跟操作数的位置有关
    console.log(2 + 1 + '3'); / /'33'
    console.log('3' + 2 + 1); //'321'

    //数字字符串之前存在数字中的正负号(+/-)时,会被转换成数字
    console.log(typeof '3'); // string
    console.log(typeof +'3'); //number

    //同样,可以在数字前添加 '',将数字转为字符串
    console.log(typeof 3); // number
    console.log(typeof (''+3)); //string

    //对于运算结果不能转换成数字的,将返回 NaN
    console.log('a' * 'sd'); //NaN
    console.log('A' - 'B'); // NaN
复制代码

9. 给基本类型数据添加属性,不报错,但取值时是 undefined

var a = 10;
a.pro = 10;
console.log(a.pro + a);
var s = "hello";
s.pro = "world";
console.log(s.pro + s);
//其中a.pro和s.pro都为undefined

//答案:NaN undefinedhello
复制代码

给基本类型数据加属性不报错,但是引用的话返回 undefined,10+undefined 返回 NaN,而 undefined 和 string 相加时转变成了字符串;

10. 看下列代码,输出什么?解释原因;

var a = null;
alert(typeof a); //object
//解释:null是一个只有一个值的数据类型,这个值就是null;
//表示一个空指针对象,所以用typeof检测会返回”object”;
复制代码

11. 原生 JS 的 window.onload 与 Jquery 的$(document).ready(function(){})有什么不同?如何用原生 JS 实现 Jq 的 ready 方法?

window.onload()方法是必须等到页面内包括图片的所有元素加载完毕后才能执行; $(document).ready()是 DOM 结构绘制完毕后就执行,不必等到加载完毕;

function ready(fn) {
  if (document.addEventListener) {
    //标准浏览器
    document.addEventListener(
      "DOMContentLoaded",
      function() {
        //注销事件, 避免反复触发
        document.removeEventListener(
          "DOMContentLoaded",
          arguments.callee,
          false
        );
        fn(); //执行函数
      },
      false
    );
  } else if (document.attachEvent) {
    //IE
    document.attachEvent("onreadystatechange", function() {
      if (document.readyState == "complete") {
        document.detachEvent("onreadystatechange", arguments.callee);
        fn(); //函数执行
      }
    });
  }
}
复制代码

12. 判断一个单词是否是回文?

function checkPalindrom(str) {
  return (
    str ==
    str
      .split("")
      .reverse()
      .join("")
  );
}
复制代码

13. 不借助临时变量,进行两个整数的交换

function swap(a, b) {
  b = b - a;
  a = a + b;
  b = a - b;
  return [a, b];
}

module.exports = swap;
复制代码

14. 找出下列数组的最大差值:

输入 [10,5,11,7,8,9]; 输出 6

function maxdiff(arr) {
  var minvalue = arr[0],
    maxprofit = 0;
  for (var i = 0; i < arr.length; i++) {
    minvalue = Math.min(minvalue, arr[i]);
    maxprofit = Math.max(arr[i] - minvalue, maxprofit);
  }
  return maxprofit;
}
复制代码

15. 实现类似 getElementsByClassName 的功能

自己实现一个函数,查找某个 DOM 节点下面的包含某个 class 的所有 DOM 节点?不允许使用原生提供的 getElementsByClassName querySelectorAll 等原生提供 DOM 查找函数;

function getElementsByClass(oParent, target) {
  var aEle = oParent.getElementsByTagName("*");
  var aResult = [];
  var i = 0;
  for (i = 0; i < aEle.length; i++) {
    if (aEle[i].className.splite(' ').indexOf(target) > -1) {
      aResult.push(aEle[i]);
    }
  }
  return aResult;
}
复制代码

16. 获取 url 中的参数

指定参数名称,返回该参数的值 或者 空字符串 不指定参数名称,返回全部的参数对象 或者 {} 如果存在多个同名参数,则返回数组

例子:

getUrlParam('http://www.nowcoder.com?key=1&key=2&key=3&test=4#hehe', 'key');

function getUrlParam(sUrl, sKey) {
  var res,
    param = {};
  sUrl.replace(/[\?&]?(\w+)=(\w+)[&#]/g, function($0, $1, $2) {
    param[$1] = param[$1] === undefined ? $2 : [].concat(param[$1], $2);
  });
  res = sKey === undefined || "" ? param : param[sKey] || "";
  return res;
}
复制代码

17.查找两个节点的最近公共父节点

oNode1 和 oNode2 在同一文档中,且不会为相同的节点
  • 第一种解法比较简单,分别从两个节点开始,沿着 parent 指针走向根节点,得到两个栈,然后求两个栈的第一个公共节点;
function commonParentNode(oNode1, oNode2) {
  var nodes1 = [],
    nodes2 = [];
  while (oNode1) {
    nodes1.push(oNode1);
    oNode1 = oNode1.parentNode;
  }
  while (oNode2) {
    nodes2.push(oNode2);
    oNode2 = oNode2.parentNode;
  }
  while ((a = nodes1.pop()) === nodes2.pop()) {
    node = a;
  }

  return node;
}
复制代码
  • 第二种方法是借助 contains 方法,通过 for 循环遍历查找
function commonParentNode(oNode1, oNode2) {
  if (!oNode1 || !oNode2) {
    return null;
  }
  for (; oNode1; oNode1 = oNode1.parentNode) {
    if (oNode1.contains(oNode2)) {
      return oNode1;
    }
  }
}
复制代码

18.根据包名,在指定空间中创建对象

输入描述:
namespace({a: {test: 1, b: 2}}, 'a.b.c.d')
输出描述:
{a: {test: 1, b: {c: {d: {}}}}}
复制代码

方法一:

function namespace(oNamespace, sPackage) {
  var arr = sPackage.split(".");
  var res = oNamespace; // 保留对原始对象的引用
  for (var i = 0, len = arr.length; i < len; i++) {
    if (arr[i] in oNamespace) {
      // 空间名在对象中
      if (typeof oNamespace[arr[i]] !== "object") {
        // 为原始值
        oNamespace[arr[i]] = {}; // 将此属性设为空对象
      }
    } else {
      // 空间名不在对象中,建立此空间名属性,赋值为空
      oNamespace[arr[i]] = {};
    }
    oNamespace = oNamespace[arr[i]]; // 将指针指向下一个空间名属性;
  }
  return res;
}
复制代码

方法二递归

function namespace(oNamespace, sPackage) {
  if (sPackage.length <= 0) return;
  var pointer = oNamespace;
  if (sPackage[0] in oNamespace) {
    if (typeof oNamespace[sPackage[0]] !== "object") {
      oNamespace[sPackage[0]] = {};
    }
  } else {
    oNamespace[sPackage[0]] = {};
  }
  oNamespace = oNamespace[sPackage[0]];
  namespace(oNamespace, sPackage.slice(2));
  return pointer;
}
复制代码

19. 时间格式化输出

输入例子: formatDate(new Date(1409894060000), 'yyyy-MM-dd HH:mm:ss 星期 w')
输出例子: 2014-09-05 13:14:20 星期五

function formatDate(oDate, sFormation) {
  var obj = {
    yyyy: oDate.getFullYear(),
    yy: ("" + oDate.getFullYear()).slice(-2),
    MM: ("0" + (oDate.getMonth() + 1)).slice(-2),
    M: oDate.getMonth() + 1,
    dd: ("0" + oDate.getDate()).slice(-2),
    d: oDate.getDate(),
    HH: ("0" + oDate.getHours()).slice(-2),
    H: oDate.getHours(),
    hh: ("0" + (oDate.getHours() % 12)).slice(-2),
    h: oDate.getHours() % 12,
    mm: ("0" + oDate.getMinutes()).slice(-2),
    m: oDate.getMinutes(),
    ss: ("0" + oDate.getSeconds()).slice(-2),
    s: oDate.getSeconds(),
    w: ["日", "一", "二", "三", "四", "五", "六"][oDate.getDay()]
  };
  return sFormation.replace(/([a-z]+)/gi, function($1) {
    return obj[$1];
  });
}
复制代码

20. 获取字符串的长度

如果第二个参数 bUnicode255For1 === true,则所有字符长度为 1 否则如果字符 Unicode 编码 > 255 则长度为 2

//unicode编码大于255的字符可以通过
s.charCodeAt(i) > 255;

或者;

s.match(/[\u0256-\uffff]/g);

function strLength(s, bUnicode255For1) {
  //如果bUnicode255For1为false,返回s的长度加正则匹配\u0256-\uffff的长度
  return (
    s.length +
    (bUnicode255For1 ? 0 : (s.match(/[\u0256-\uffff]/g) || []).length)
  );
}
复制代码

###21. 颜色字符串转换

将 rgb 颜色字符串转换为十六进制的形式,如 rgb(255, 255, 255) 转为 #ffffff rgb 中每个 , 后面的空格数量不固定 十六进制表达式使用六位小写字母 如果输入不符合 rgb 格式,返回原始输入

输入例子: rgb2hex('rgb(255, 255, 255)') 输出例子: #ffffff

利用 Number 对象的 toString 方法,toString() 方法以指定的基数返回该对象的字符串表示.

function rgb2hex(sRGB) {
  var res = [];
  var reg = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/g;
  if (!reg.test(sRGB)) {
    return sRGB;
  } else {
    var reg2 = /(\d+)/g;
    sRGB.replace(reg2, function($0) {
      res.push(("0" + (+$0).toString(16)).slice(-2));
    });
  }
  return "#" + res.join("");
}
复制代码

22.将字符串转换为驼峰形式

css 中经常有类似 background-image 这种通过 - 连接的字符,通过 javascript 设置样式的时候需要将这种样式转换成 backgroundImage 驼峰格式,请完成此转换功能

以 - 为分隔符,将第二个起的非空单词首字母转为大写 -webkit-border-image 转换后的结果为 webkitBorderImage 输入例子:

cssStyle2DomStyle('font-size') 输出例子:

fontSize 使用 String.replace 方法,注意这里 replace 的函数包含三个参数,第一个是匹配到的字符串,第二个和第三个 slash 为子匹配;而 js 传参是值传递,因此通过 word, slash1, slash2 只能获得值,对匹配到的值进行修改之后,一定记得要 return 回去;

function cssStyle2DomStyle(sName) {
  var reg = /(\w)?-+(.)?/g;
  return sName.replace(reg, function(word, slash1, slash2) {
    if (!slash1) return slash2;
    else {
      return slash2 ? slash1 + slash2.toUpperCase() : slash1 + "";
    }
  });
}
复制代码

24. 将数字转为大写

编写一个函数 fn(Number n),将数字转为大写输出;

输入 123 输出 一百二十三 解题思路: 首先设置大写度量单位'千百十亿千百十万千百十个' 将数字转为大写同时加上度量单位 eg:二千零五十四亿零二百万二千一百零三 将零(千|百|十)替换成零,将多个零替换成 1 个零,将零(亿|万)替换成(亿|万);

function fn(n) {
  if (isNaN(n)) {
    return "非法数据";
  }
  var unit = "千百十亿千百十万千百十个";
  if (n.length > unit.length) {
    return "数据过长";
  }
  var newStr = "";
  var nlength = n.length;
  unit = unit.substr(unit.length - nlength);
  for (var i = 0; i < nlength; i++) {
    newStr += "零一二三四五六七八九".charAt(n[i]) + unit.charAt(i);
  }
  newStr = newStr.substr(0, newStr.length - 1);
  console.log(newStr);
  newStr = newStr
    .replace(/零(千|百|十)/g, "零")
    .replace(/(零)+/g, "零")
    .replace(/零(亿|万)/g, "$1");
  return newStr;
}
复制代码

25.js 数组去重

1.最简单的方法(es6)

ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring);

以前,为变量赋值,只能直接指定值;

function unique(arr) {
  const seen = new Map();
  return arr.filter(a => !seen.has(a) && seen.set(a, 1));
}
// or
function unique(arr) {
  return Array.from(new Set(arr));
}
复制代码

2.如果是已知排序的 array,或者不在乎去重之后的结果顺序; 可以做一次循环,判断当前的 item 是否等于前面那个 item,如果不等于或不存在前面的 item,就 push 到 result 中;(这个很快)

Array.prototype.uniq = function() {
  if (!this.length || this.length === 0) {
    return this;
  }
  this.sort();
  var res = [this[0]];
  for (var i = 1, len = this.length; i < len; i++) {
    if (this[i] !== this[i - 1]) {
      res.push(this[i]);
    }
  }
  return res;
};
复制代码

3.或者采用两个指针 l 和 r,l 记录不重复元素的位置,r 从 l 的下一个开始遍历数组,如果 r 位置的数字等于 l 位置的数字,说明该数字重复出现,不予处理;如果 r 位置的数字不等于 l 位置的数字,说明该数字没有重复,需要放到 l 的下一位置,并使 l 加 1;

时间复杂度:O(n)

空间复杂度:O(1)

Array.prototype.uniq = function() {
  if (!this.length || this.length === 0) {
    return this;
  }
  this.sort();
  var left = 0,
    right;
  for (right = left + 1, len = this.length; right < len; right++) {
    if (this[left] !== this[right]) {
      this[++left] = this[right];
    }
  }
  return this.slice(0, left);
};
复制代码

4.如果顺序杂乱,可以做一次循环,用一个对象标记该 item 是否存在,如果不存在,就 push 到 result 中;这里使用一个 hashtable 的结构记录已有的元素,这样就可以避免内层循环;不过,这样的话,假如数组非常庞大,性能会差;

if (!Array.prototype.unique) {
  Array.prototype.unique = function() {
    var hash = {},
      result = [],
      item;
    for (var i = 0; i < this.length; i++) {
      item = this[i];
      var key = Object.prototype.toString.call(item) + item;
      if (!hash[key]) {
        hash[key] = true;
        result.push(item);
      }
    }
    return result;
  };
}
复制代码

注意 js 中 hash 的键值是以字符存储的,所以这里将数组元素作为 hash 索引时需要加上类型,否则无法区分数字 1 和字符 1;
如果数组中存在 function,直接判断是否相等是不行的,可以 toString()一下,再进行比较;

如果碰到 Object,就继续做循环;

遍历数组,建立新数组,利用 indexOf 判断是否存在于新数组中,不存在则 push 到新数组,最后返回新数组

时间复杂度 o(n^2)

Array.prototype.indexOf =
  Array.prototype.indexOf ||
  function(item) {
    for (var i = 0, j = this.length; i < j; i++) {
      if (this[i] === item) {
        return i;
      }
    }
    return -1;
  };
function removeDuplicatedItem(ar) {
  var ret = [];
  for (var i = 0, j = ar.length; i < j; i++) {
    if (ret.indexOf(ar[i]) === -1) {
      ret.push(ar[i]);
    }
  }
  return ret;
}
复制代码

在 JavaScript 里使用 typeof 来判断数据类型,只能区分基本类型,即 "number","string","undefined","boolean","object"五种;

对于数组、函数、对象来说,其关系错综复杂,使用 typeof 都会统一返回 "object"字符串;

要想区别对象、数组、函数单纯使用 typeof 是不行的,JavaScript 中,通过 Object.prototype.toString 方法,判断某个对象值属于哪种内置类型;

26.数组各种排序算法

1.插入排序 思路: 外层循环从第二个开始循环,数组[2,1,5,4,9,8];
第一轮 第二个和第一个,如果第二个小于第一个交换位置;[1,2,5,4,9,8];
第二轮 第三个和第二个比,如果第二个小于第一个交换位置;[1,2,5,4,9,8];
第三轮 第四个和第三个比,如果第二个小于第一个交换位置;[1,2,4,5,9,8];
以此类推:外层循环完就能排出大小;

function insert(arr) {
    var s;
    for (var i = 1; i < arr.length; i++) {
        for (var j = i; j > 0; j--) {
        if (arr[j] < arr[j - 1]) {
            s=arr[j];
            arr[j]=arr[j-1]
            arr[j-1]=s
            console.log(arr)   //可以打印出来每一个改变的步骤
        }
        }
    }
    return arr
}
console.log(insert([3,3,4,2,1,4,4,3,7,8]))
复制代码

2.冒泡排序

代码演示:

    function bubble(arr) {
        var s
        for (var i =0;i<arr.length;i++) {
            for (var j = 0; j < arr.length; j++) {
                if (arr[j] > arr[j + 1]) {
                    s = arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=s;
                }
            }
        }
        return arr
    }
    console.log(bubble([1,2,3,4,5,7,8]));
    
复制代码

3.选择排序

先找出数组中的最小项,放入新数组,同时确定这个最小项的索引,根据这个索引,当前数组删除这个最小项 以上的操作,只能进行一次,所以我们在外面加一层的循环,让他执行多次,次数就是数组的长度

代码演示:

    function select(arr) {
        var result = []
        for (var i = 0, len = arr.length; i < len; i++) {
            var min = Math.min.apply(null, arr)
            result.push(min)
            arr.forEach(function(item, index) {
                if (item == min) {
                    arr.splice(index, 1)
                }
            })
        }
        return result
    }
    var arr = [3, 2, 5, 4, 8, 7, 1]]
    var res = select(arr)
    console.log(res)
复制代码

27.为什么 ["1", "2", "3"].map(parseInt) 返回 [1,NaN,NaN]?

parseInt() 函数

parseInt(string, radix)
string 必需要被解析的字符串.
radix 可选。表示要解析的数字的基数。该介于 2 ~ 36 之间。

返回值

返回解析后的数字。

map 方法

对数组的每个元素调用定义的回调函数并返回包含结果的数组.
array1.map(callbackfn[, thisArg])
array1 必需。一个数组对象。
callbackfn 必需。一个接受最多三个参数的函数。对于数组中的每个元素,‘map‘ 方法都会调用 ‘callbackfn‘ 函数一次。
thisArg 可选。可在 ‘callbackfn‘ 函数中为其引用 ‘this‘ 关键字的对象。如果省略 ‘thisArg‘,则 ‘undefined‘ 将用作 ‘this‘ 值。

返回值

其中的每个元素均为关联的原始数组元素的回调函数返回值的新数组

异常

如果 callbackfn 参数不是函数对象,则将引发 TypeError 异常。 备注

对于数组中的每个元素,map 方法都会调用 callbackfn 函数一次(采用升序索引顺序)。 不为数组中缺少的元素调用该回调函数。

除了数组对象之外,map 方法可由具有 length 属性且具有已按数字编制索引的属性名的任何对象使用

返回[1,NaN,NaN]原因

我们从新定义一下parseInt

var parseInt = function(string, radix) {
    return string + "-" + radix;
};
 
["1", "2", "3"].map(parseInt);
//输出结果为:
["1-0", "2-1", "3-2"]
 
//在加一个参数 

var parseInt = function(string, radix, obj) {
    return string + "-" + radix + "-" + obj;
};
 
["1", "2", "3"].map(parseInt);
//输出结果:

["1-0-1,2,3", "2-1-1,2,3", "3-2-1,2,3"];

//我们再继续增加参数:

var parseInt = function(string, radix, obj, other) {
    return string + "-" + radix + "-" + obj + "-" + other;
};
 
["1", "2", "3"].map(parseInt);

复制代码

结论:["1", "2", "3"].map(parseInt) 应该对应的是: [parseInt("1", 0), parseInt("2", 1), parseInt("3", 2)] parseInt("3", 2) 的第二个参数是界于 2-36 之间的,之所以返回 NaN 是因为 字符串 "3" 里面没有合法的二进制数,所以 NaN

28.JS的forEach和map方法的区别

1、语法:

//forEach
array.forEach(callback(currentValue, index, array){
    //do something
}, this)
 
//或者
array.forEach(callback(currentValue, index, array){
    //do something
})&emsp;&emsp;
 
//map:
var new_array = arr.map(callback[, thisArg])&emsp;
 
//$.each()
$(selector).each(function(index,element))
复制代码

区别

1.forEach()返回值是undefined,不可以链式调用。

2.map()返回一个新数组,原数组不会改变。

3.没有办法终止或者跳出forEach()循环,除非抛出异常,所以想执行一个数组是否满足什么条件,返回布尔值,可以用一般的for循环实现,或者用Array.every()或者Array.some();

4.$.each()方法规定为每个匹配元素规定运行的函数,可以返回 false 可用于及早停止循环。

循环方法性能排行

for循环遍历 < for...of遍历 < forEach遍历 < for...in遍历 < map遍历

29.Array的push与unshift方法性能比较分析

Array的push与unshift方法都能给当前数组添加元素,不同的是,push是在末尾添加,而unshift则是在开头添加。 从原理就可以知道,unshift的效率是较低的。原因是,它每添加一个元素,都要把现有元素往下移一个位置。两者的效率差异有多大呢?下面来测试一下

var arr=[],s = +new Date;
//push性能测试
for(var i=0;i<50000;i++){
    arr.push(i);
}
console.log(+new Date - s);

s= +new Date;
arr=[];
//unshift性能测试
for(var i=0;i<50000;i++){
    arr.unshift(i);
}
console.log(+new Date - s);
复制代码

结果为
6
238

原理: 因为头部加入元素的话,数组原来的位置将会被打乱数组会重新排序,性能就会较差,如果是尾部删除和添加的话,原来的下标位置将不会被打乱,位置信息将不会被打乱,性能较好;

若必须达到unshift的效果,可以先用push,再使用数组的reverse方法反转数组。如:

for (var i = 0; i < 50000; i++) { 
&emsp;&emsp;arr.push(i); 
} 
arr.reverse(); 

//reverse的性能又怎样呢?运行下面的代码

var arr = [ ], s = +new Date; 
for (var i = 0; i < 50000; i++) { 
&emsp;&emsp;arr.push(i); 
} 
arr.reverse(); 
console.log(+new Date - s); 

复制代码

结果为:8
可见reverse性能很高,基本没有影响

30.JavaScript:(a==1 && a==2 && a==3)能输出ture么?

const a = {
  num: 0,
  valueOf: function() {
    return this.num += 1
  }
};
const equality = (a==1 && a==2 && a==3);
console.log(equality); // true
复制代码

有什么窍门呢?

其实也没有,能有的就是js中的两个概念:

  • 隐式转换
  • object的valueOf函数

隐式转换

注意:这题里面我们用的是 == 而不是===,在js中==代表的是等于而不是全等,那么就存在变量的隐式转化问题。这就意味着结果会比我们所期望的更多的可能性。对于js的隐式转化,真的有很多文章,我推荐一下以下几篇博客,如果你想要了解,可以点进去:

记住下面两点:

  • 使用相等操作符,js会做强制类型转化
  • 我们的对象每次调用valueOf()它的值会增加1

31.手动实现浅拷贝

遍历对象,然后把属性和属性值都放在一个新的对象不就好了~

    var shallowCopy = function(obj) {
        // 只拷贝对象
        if (typeof obj !== 'object') return;
        // 根据obj的类型判断是新建一个数组还是对象
        var newObj = obj instanceof Array ? [] : {};
        // 遍历obj,并且判断是obj的属性才拷贝
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                newObj[key] = obj[key];
            }
        }
        return newObj;
    }
复制代码

33.深拷贝的实现

那如何实现一个深拷贝呢?说起来也好简单,我们在拷贝的时候判断一下属性值的类型,如果是对象,我们递归调用深拷贝函数不就好了~

    var deepCopy = function(obj) {
        if (typeof obj !== 'object') return;
        var newObj = obj instanceof Array ? [] : {};
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
            }
        }
        return newObj;
    }
复制代码

32.实现一个javascript new的功能

要想实现new的功能,我们首先要知道new是干什么的呢?

在js中 var a = new A() 后

1.创建一个空对象obj{}

2.将a的this指向obj{}

3.将a的_proto_指向A的prototype

4.执行A的构造函数

5.返回obj

代码级实现:

    function Test (name) {
        this.name = name;
        this.test = '????';
    }
    
    Test.prototype.test2 = function () {
        console.log('测试原型链上的方法!');
    }
    
    function _new () {
        let newObj = {};
        let Constructor = Array.prototype.shift.call(arguments);
        newObj.__proto__ = Constructor.prototype;
        Constructor.apply(newObj,arguments);
        return newObj;
    }

    var t = _new(Test,'yz');
    t.test2();
复制代码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值