[关闭]
@fantaghiro 2014-11-21T19:13:13.000000Z 字数 9713 阅读 2900

妙味课堂——JS面试题

学习笔记 js 前端 妙味课堂


选择题


题1:

  1. (function(){
  2. return typeof arguments;
  3. })();

解析:

typeof一共返回六种类型:字符串、number、布尔值、object对象、undefined、function函数,所以2、3项就剔除了

arguments是一个实参的集合,它是一个对象。


题2:

  1. var f = function g(){ return 23; };
  2. typeof g();

解析:

如果第一行中的g不写的话,那么就是一个函数赋给了一个变量,这叫作“函数表达式”。这里这个函数是有名字的,因此叫作“有名的函数表达式”。多了这个名字,这种写法就是不规范的。这个f是能够找到的,但是这个g()在有些浏览器下是找不到的。因此选第四项Error。


题3:

  1. (function(x){
  2. delete x;
  3. return x;
  4. })(1);

解析:

delete不能删除变量,也不能删除参数。它只能删除一个对象下面的属性。因此选第1项。


题4:

  1. var y = 1, x = y = typeof x;
  2. x;

解析:

是从右向左执行的,那么在执行typeof x的时候,x还没有值的。所以typeof x返回的是"undefined"(typeof返回的都是字符串类型的)。这个字符串"undefined"又赋值给了y。这时候y就不是1了。然后再赋给x,x就是字符串"undefined"。


题5:

  1. (function f(f){
  2. return typeof f();
  3. })(function(){ return 1; });

解析:

传入的参数f是匿名函数function(){return 1;},然后在自执行的函数里面f执行了(f()),所以这个f()就相当于1,那么typeof 1就是字符串的"number"


题6:

  1. var foo = {
  2. bar: function(){ return this.baz; },
  3. baz: 1
  4. };
  5. (function(){
  6. return typeof arguments[0]();
  7. })(foo.bar)

解析:

foo.bar可以视为函数function(){ return this.baz; }的函数名,它通过arguments[0]传到自执行函数里面。也就是说foo.bar就是arguments[0],这个函数去执行,看起来这里的this是指向foo,其实不是。这个this要看从哪儿调用,它前面是谁。现在这个foo.bar看作一个整体,视为一个函数名,那么这个名为foo.bar的函数是在window下调用的,window下面没有baz,因此返回的应该是"undefined"。


题7:

  1. var foo = {
  2. bar: function(){return this.baz;},
  3. baz: 1
  4. }
  5. typeof(f = foo.bar)();

解析:

这个题目跟上个题目差不多。也是将foo.bar作为一个整体赋给了f,那么f调用的时候,也还是在window下调用的。this指向是window,window下面没有baz,因此应选"undefined"。


题8:

  1. var f = (function f(){return "1";},function g(){return 2;})();
  2. typeof f;

解析:

分组选择符:小括号。

  1. var a = (1, 2, 3);
  2. alert(a); //弹出3 变量取得是最后一位

上面要走分组选择符后面的这个函数,后面函数return的是2,所以选择"number"


题9:

  1. var x = 1;
  2. if(function f(){}){
  3. x += typeof f;
  4. }
  5. x;

解析:

函数声明是不能写到运算符的运算过程当中,例如if的括号或者for循环的括号中。函数写到这里面的话,函数名字就找不到了。函数声明必须是全局的或局部的,不能写到运算当中。所以typeof后面的f是找不到的,因此typeof f返回的是"undefined"。另外一个问题,这个函数写到括号当中,是返回真的。应该选第三个。


题10:

  1. var x = [typeof x, typeof y][1];
  2. typeof typeof x;

解析:

不管x是什么,typeof x返回的必然是字符串。因此typeof 字符串返回的是"string"


题11:

  1. (function(foo){
  2. return typeof foo.bar;
  3. })({foo: {bar: 1}});

解析:

形参foo只的是{foo: {bar: 1}}这个整体。在typeof foo.bar;中的这个foo下面只有一个foo属性,并没有bar这个属性,因此应该选"undefined"


题12:

  1. (function f(){
  2. function f(){return 1;}
  3. return f();
  4. function f(){return 2;}
  5. })()

解析:

函数声明是会预解析的,因此在return f();这一句还没有执行的时候,上面和下面的这两句就已经执行完毕了。后面把前面的覆盖了,因此在return f();这一句执行的时候,这个f已经是下面的那个函数了。因此应该选择“2”。


题13:

  1. function f(){return f;}
  2. new f() instanceof f;

解析:

instanceof方法是看前面的对象是否是后面的构造函数构造出来的。在构造函数中如果return了函数或者对象的话,那么这个函数或者对象就会把这个构造函数生成的对象覆盖掉。就相当于new f()执行完之后,就不是原本构造出来的对象了,而是f这个函数。f instanceof f返回的是false。


题14:

  1. with (function(x, undefined){}) length;

解析:

函数的长度就是函数的形参数量。选择第二项:2。

  1. function test(num1, num2, num3){
  2. }
  3. alert(test.length); //弹出3

运行题 作用域

1)外层的变量,内层可以找到(全局);内层的变量,外层找不到(局部)

  1. //内部的可以调到外部的
  2. var a = 10;
  3. function aaa(){
  4. alert(a);
  5. }
  6. aaa(); //10
  1. //外部的调不到内部的
  2. function aaa(){
  3. var a = 10;
  4. }
  5. aaa();
  6. alert(a); //报错
  1. var a = 10;
  2. function aaa(){
  3. alert(a);
  4. }
  5. function bbb(){
  6. var a = 20;
  7. aaa();
  8. }
  9. bbb(); //弹出的是10

2) 当var不加的时候,会自动生成全局的变量(不建议这样写,最好把所有要定义的变量加上var)

  1. function aaa(){
  2. a = 10;
  3. }
  4. aaa();
  5. alert(a); //10
  1. function aaa(){
  2. var a = b = 10;
  3. }
  4. aaa();
  5. alert(a); //a找不到
  6. alert(b); //弹出10

3)变量的查找是就近原则去寻找var定义的变量。当就近没有找到的话,就会查找外层。

  1. var a = 10;
  2. function aaa(){
  3. var a = 20;
  4. alert(a);
  5. }
  6. aaa(); //弹出20,因为在内层就找到a了
  1. var a = 10;
  2. function aaa(){
  3. a = 20;
  4. alert(a);
  5. }
  6. aaa(); //弹出20。在内层没有找到var定义的a,会去找到外层的a,找到外层a之后,在内层,变量a的值先又变成了20,所以弹出的就是20。
  1. var a = 10;
  2. function aaa(){
  3. alert(a);
  4. a = 20;
  5. }
  6. aaa(); //弹出10
  1. var a = 10;
  2. function aaa(){
  3. alert(a);
  4. var a = 20;
  5. }
  6. aaa(); //undefined 在内层找到了var定义的a,因此就不会再找外层的var a = 10了。但是在内层找到了a,在alert(a)的时候,a还没有赋值20,因此就是undefined。
  1. //上面的代码其实相当于这段代码:
  2. //预解析原理
  3. var a = 10;
  4. function aaa(){
  5. var a;
  6. alert(a);
  7. a = 20;
  8. }
  9. aaa(); //undefined
  1. var a = 10;
  2. function aaa(){
  3. bbb();
  4. alert(a);
  5. function bbb(){
  6. var a = 20;
  7. }
  8. }
  9. }
  10. aaa(); //10 函数bbb里面定义的局部变量a,在alert的时候是找不到的,alert的时候找的a是外层的var a = 10

4)当参数跟局部变量重名的时候,优先级是等同的。

  1. var a = 10;
  2. function aaa(a){
  3. alert(a);
  4. var a = 20;
  5. }
  6. aaa(a); //10
  1. var a = 5;
  2. var b = a;
  3. b += 3;
  4. alert(a); //5 基本类型的赋值,只存在一个值的关系,不存在引用的关系。
  1. var a = [1, 2, 3];
  2. var b = a;
  3. b.push(4);
  4. alert(a); //[1, 2, 3, 4]; //对象之间是一种引用关系
  1. var a = [1, 2, 3];
  2. var b = a;
  3. b = [1, 2, 3, 4];
  4. alert(a); //[1, 2, 3] b是重新赋值的,与a之间的联系就断开了。a、b之间就不存在什么关系了
  1. var a = 10;
  2. function aaa(a){
  3. a += 3;
  4. }
  5. aaa(a);
  6. alert(a); //10 当我们传参进来之后,就相当于是重新赋值了,a是个基本类型,它+=3是不会影响到外部的这个变量a的。外面alert(a)中的a还是var a = 10这个a。alert(a)这里的a根本找不到aaa函数里面的a,因为函数的参数和局部变量的概念是一样的。
  1. var a = [1, 2, 3];
  2. function aaa(a){
  3. a.push(4);
  4. }
  5. aaa(a);
  6. alert(a); //[1, 2, 3, 4] 函数aaa里面的参数a虽然与外面的a没有什么关系,但是因为这两者之间是一个引用关系,对局部的a进行push,也就相当于改了外边的a的引用,因此alert(a)的时候这个a还是外面的a,但是外面的a已经变成[1, 2, 3, 4]了。
  1. var a = [1, 2, 3];
  2. function aaa(a){
  3. a = [1, 2, 3, 4];
  4. }
  5. aaa(a);
  6. alert(a); // [1, 2, 3] 里面的参数a是重新赋值了,而不是与外面的a存在引用关系。里面的a与外面的a没有任何关系了。alert(a)这里的a是外面的a,外面的a没变,还是[1, 2, 3]

字符串操作的试题

题1:写一个字符串转成驼峰的方法

例如:border-bottom-color => borderBottomColor

方法一:字符串操作

  1. var str = 'border-bottom-color';
  2. function test(str){
  3. var arr = str.split('-'); //[border, bottom, color]
  4. for(var i=1; i < arr.length; i++){
  5. arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substring(1); //[border, Bottom, Color]
  6. }
  7. return arr.join('');
  8. }
  9. alert(test(str)); //borderBottomColor

方法二:正则操作

  1. var str = 'border-bottom-color';
  2. function test(){
  3. var re = /-(\w)/g;
  4. return str.replace(re, function($0, $1){
  5. return $1.toUpperCase();
  6. });
  7. }
  8. alert(test(str));

题2:查找字符串中出现最多的字符和个数

例如:sdjksfssscfssdd => 字符最多的是s,出现了7次

方法一:字符串操作

  1. var str = sdjksfssscfssdd;
  2. function test(str){
  3. var obj = {};
  4. var num = 0;
  5. var value = '';
  6. for(var i=0; i<str.length; i++){
  7. if( !obj[ str[i] ] ){
  8. obj[ str[i] ] = [];
  9. }
  10. obj[ str[i] ].push( str[i] );
  11. }
  12. for (var attr in obj){
  13. if(num < obj[attr].length){
  14. num = obj[attr].length;
  15. value = obj[attr][0];
  16. }
  17. }
  18. return '最多的字符是:' + value + ',出现了:' + num;
  19. }
  20. alert(test(str));

方法二:正则操作

  1. var str = sdjksfssscfssdd;
  2. function test(str){
  3. var arr = str.split('');
  4. arr.sort();
  5. str = arr.join('');
  6. var re = /(\w)\1+/g;
  7. var num = 0;
  8. var value = 0;
  9. str.replace(re, function($0, $1){
  10. if(num < $0.length){
  11. num = $0.length;
  12. value = $1;
  13. }
  14. })
  15. return '最多的字符是:' + value + ',出现了:' + num;
  16. }
  17. alert(test(str));

题3:如何给字符串加千分符

例如:3562123761 => 3,562,123,761

方法一:字符串操作

  1. var str = '3562123761';
  2. function test(str){
  3. var iNum = str.length%3;
  4. var prev = '';
  5. var arr = [];
  6. var iNow = 0;
  7. var tmp = '';
  8. if(iNum != 0){
  9. prev = str.substring(0, iNum);
  10. arr.push(prev);
  11. }
  12. str = str.substring(iNum);
  13. for(var i=0; i<str.length; i++){
  14. iNow++;
  15. tmp += str[i];
  16. if(iNow==3 && tmp){
  17. arr.push(tmp);
  18. tmp = '';
  19. iNow = 0;
  20. }
  21. }
  22. return arr.join(',');
  23. }
  24. alert(test(str));

方法二:正则操作

  1. //正则语法:
  2. //(?=) : 前向声明
  3. //(?!) : 反前向声明
  4. var str = 'abacad';
  5. var re = /a(?=b)/g; //用前向声明,只有a后面是b的时候,这个a才被匹配到,而b不是被匹配的内容
  6. str = str.replace(re, '*');
  7. alert(str); //'*bacad'
  1. var str = 'abacad';
  2. var re = /a(?!b)/g; //用反前向声明,只有a后面不是b的时候,这个a才能被匹配到
  3. str = str.replace(re, '*');
  4. alert(str); //'ab*c*d'
  1. var str = '3562123761';
  2. function test(str){
  3. var re = /(?=(?!\b)(\d{3})+$)/g; //这里从后往前,匹配到三位三位的位置,而且匹配上的这个位置前面不是独立部分。这个是什么意思呢,就是说如果数字正好是三个倍数位,例如123456,不添加这个(?!\b),得到的结果是',123,456'。但是添加上这个反前向声明,123前面的这个位置就不会被匹配上了。
  4. return str.replace(re, ',');
  5. }
  6. alert(test(str));

练习:返回一个只包含数字类型的一个数组?

例如:js123ldka78sdassdfd653 -> [123, 78, 653]

限制条件补全代码

题目一:a b 两个变量 不用第三个变量来切换两个变量值

  1. /* 适用性不广
  2. var a = 5;
  3. var b = 6;
  4. a = a + b;
  5. b = a - b;
  6. a = a - b;
  7. alert(a); //6
  8. alert(b); //5
  9. */
  10. var a = 'hello';
  11. var b = 'hi';
  12. a = [a, b];
  13. b = a[0];
  14. a = a[1];
  15. alert(a); //'hi'
  16. alert(b); //'hello'

题目二:有一个数n=5,不用for循环,怎么返回[1, 2, 3, 4, 5]这样一个数组

方法一

  1. var n = 5;
  2. function show(n){
  3. //用什么可以替代循环呢?递归、定时器。但是定时器用不了,因为它是异步的。考虑用递归。
  4. var arr = [];
  5. return (function(){
  6. arr.unshift(n);
  7. n--;
  8. if(n!=0){
  9. arguments.callee();
  10. }
  11. return arr;
  12. })();
  13. }
  14. alert(show(n)); //[1, 2, 3, 4, 5]

方法二

  1. var n = 5;
  2. function show(n){
  3. var arr = [];
  4. arr.length = n + 1; //数组arr变成 [, , , , ,]
  5. var str = arr.join('a'); //str -> 'aaaaa'
  6. var arr2 = [];
  7. str.replace(/a/g, function(){
  8. arr2.unshift(n--);
  9. })
  10. return arr2;
  11. }
  12. alert(show(n)); //[1, 2, 3, 4, 5]

题目三:一个数n,当n小于100就返回n,否则就返回100

不限定条件

  1. var n = 50;
  2. function show(){
  3. if(n<100){
  4. return n;
  5. } else {
  6. return 100;
  7. }
  8. }
  9. alert(show(n)); //50

限定条件:不允许用if else

  1. var n = 50;
  2. function show(){
  3. return n < 100 ? n : 100;
  4. }
  5. alert(show(n)); //50

限定条件:不允许用if else,也不允许用三目

  1. var n = 50;
  2. function show(){
  3. switch(n<100){
  4. case true:
  5. return n;
  6. break;
  7. case false:
  8. return 100;
  9. break
  10. }
  11. }
  12. alert(show(n)); //50

**限定条件:不允许用if else,三目和switch*

  1. var n = 50;
  2. function show(){
  3. return Math.min(n, 100);
  4. }
  5. alert(show(n)); //50

**限定条件:不允许用if else、三目、switch和Math.min()*

  1. var n = 50;
  2. function show(){
  3. var arr = [n, 100];
  4. arr.sort(function(n1, n2){
  5. return n1 - n2;
  6. })
  7. return arr[0];
  8. }
  9. alert(show(n)); //50

**限定条件:不允许用if else、三目、switch、Math.min()、数组*

  1. var n = 50;
  2. function show(){
  3. var m = '' + n; //n=150的话,m.length是3;n=50的话,m.length是2
  4. for(var i=2; i<m.length && n>0; i++){
  5. return 100;
  6. }
  7. return n;
  8. }
  9. alert(show(n)); //50

限定条件:不允许用if else、三目、switch、Math.min()、数组、循环(包括for、while、do while)

  1. var n = 50;
  2. function show(){
  3. var json = {name: 'hello'};
  4. var m = n < 100 || json;
  5. for(var attr in m){
  6. return 100;
  7. }
  8. return n;
  9. }
  10. alert(show(n)); //50

限定条件:不允许用if else、三目、switch、Math.min()、数组、循环(包括for、while、do while)、{} for in

  1. var n = 50;
  2. function show(){
  3. var m = n >= 100 && 100; //n为150,前面为真,m就为100;n为50,m就是false
  4. return m = m || n; //如果n为150,m就是100,最终返回的就是100;如果n为50,m得false,返回时m就等于后面的n,所以就返回50
  5. }
  6. alert(show(n)); //50

算法题

题1:斐波那契数列:1、1、2、3、5、8、13、21

方法一

  1. //递归的写法
  2. function aaa(n){
  3. if(n <= 2) {
  4. return 1;
  5. }
  6. return aaa(n-1) + aaa(n-2);
  7. }
  8. alert(aaa(8)); //21 返回斐波那契数列第8项的值

方法二

  1. //通过迭代的方式
  2. //迭代就是开循环,三个数迭代一次;8个数迭代6次
  3. function aaa(n){
  4. var num1 = 1;
  5. var num2 = 1;
  6. var num3 = 0;
  7. for(var i=0; i <n-2; i++){
  8. num3 = num1 + num2;
  9. num1 = num2;
  10. num2 = num3;
  11. }
  12. return num3;
  13. }
  14. alert(aaa(8));

题2:数组排序

冒泡排序

  1. //冒泡排序是两个两个进行比较
  2. function aaa(arr){
  3. for(var i=0; i<arr.length; i++){
  4. for(var j=0; j<arr.length-i; j++){
  5. toCon(j, j+1);
  6. }
  7. }
  8. function toCon(prev, next){
  9. var tmp = '';
  10. if(arr[prev] > arr[next]){
  11. tmp = arr[prev];
  12. arr[prev] = arr[next];
  13. arr[next] = tmp;
  14. }
  15. }
  16. return arr;
  17. }
  18. alert(aaa([4, 5, 1, 7, 2])); //[1, 2, 4, 5, 7]

简单选择排序

  1. 简单选择排序:找到最小值扔到数组一开始,从剩余的找到最小值,排到刚才那个的前面
  2. function aaa(arr){
  3. if(arr.length == 1){
  4. return arr;
  5. }
  6. var iMin = arr[0];
  7. var iIndex = 0;
  8. for(var i=0; i<arr.length; i++){
  9. if( arr[i] < iMin ){
  10. iMin = arr[i];
  11. iIndex = i;
  12. }
  13. }
  14. var prev = arr.splice(iIndex, 1); //把1剪切出来成了一个数组[1],arr现在就变成了[4,5,7,2] 后面递归就可以了。
  15. return prev.concat(aaa(arr));
  16. }
  17. alert(aaa([4, 5, 1, 7, 2]));

题3:数组去重

方法一

  1. //找到一个不重复的,就扔到新数组里面,剩余的里面再找不重复的,再扔到新数组里面,以此类推,最后返回新数组。
  2. function aaa(){
  3. var result = [ arr[0] ];
  4. for(var i=1; i<arr.length; i++){
  5. if( toCon( arr[i] ) ){ //如果没有重复的
  6. result.push( arr[i] );
  7. }
  8. }
  9. function toCon(val){
  10. for(var i=0; i<result.length; i++){
  11. if( result[i] == val){
  12. return false;
  13. }
  14. }
  15. return true;
  16. }
  17. return result;
  18. }
  19. alert(aaa([5, 2, 7, 5, 1, 7, 5, 4])); //[5, 2, 7, 1, 4]

方法二

  1. //利用json的key值的唯一性
  2. function aaa(){
  3. var result = [];
  4. var obj = {};
  5. for(var i=0; i<arr.length; i++){
  6. if(!obj[arr[i]]){
  7. result.push(arr[i]);
  8. obj[arr[i]] = 1;
  9. }
  10. }
  11. return result;
  12. }
  13. alert(aaa([5, 2, 7, 5, 1, 7, 5, 4]));
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注