《前端JavaScript面试技巧》

《前端JavaScript面试技巧》笔记一

思考:javascript

  • 拿到一个面试题,你第一时间看到的是什么 -> 考点
  • 又如何看待网上搜出来的永远也看不完的题海 -> 不变应万变
  • 如何对待接下来遇到的面试题 -> 题目到知识再到题目

知识体系:html

JS基础知识前端

1、变量类型和计算java

题目:jquery

  • JS中使用typeof能获得哪些类型?
  • 什么时候使用 === 什么时候使用 == ?
  • JS中有哪些内置函数
  • JS变量按照存储方式区分为哪些类型,并描述其特色
  • 如何理解JSON

知识点:面试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/*1、变量类型*/
   /*值类型vs引用类型*/
   //从内存来讲,值类型是把每一个值分块存放在内存中,而引用类型是好几个变量公用一个内存块,节省内存空间。
   //引用类型包括:对象、数组、函数。引用类型的属性是能够无限扩展的,属性多了,就会占用更多的内存空间,因此引用类型是为了公用内存空间。
   //值类型
   var  a = 100;
   var  b = a;
   a = 200;
   console.log(b);  //100
   //引用类型
   var  a = {age:20};
   var  b = a;
   b.age = 21;
   console.log(a.age);  //21
 
   /*typeof运算符详解*/
   //typeof只能区分值类型,引用类型也只能区分function,由于function的定位很是高。
   typeof  undefined  //undefined (值类型)
   typeof  'abc'  //string (值类型)
   typeof  123  //number (值类型)
   typeof  true  //boolean (值类型)
   typeof  {}  //object (引用类型)
   typeof  []  //object (引用类型)
   typeof  null  //object (引用类型)
   typeof  console.log  //function (引用类型)
/*2、变量计算---强制类型转换*/
   //字符串拼接
   var  a = 100+10;  //110
   var  b = 100+ '10' //'10010'
   //运算符
   //==会把两边的值转换为true或false
   100 ==  '100' //true
   0 ==  '' //true
   null  == undefined;  //true
   //if语句
   //if会把括号里面的值转换为true或false
   var  a =  true ;
   if (a){...}
     var  b = 100;
   if (b){...}
     var  c =  '' ;
   if (c){...}
   //逻辑运算
   console.log(10 && 0);  //0
   console.log( ''  ||  'abc' );  //'abc'
   console.log(!window.abc);  //true
   //判断一个变量会被看成true仍是false
   var  a = 100;
   console.log(!!a);

解题:ajax

JS中使用typeof能获得哪些类型?正则表达式

  undefined、string、number、boolean、object、functionjson

什么时候使用 === 什么时候使用 == ?api

1
2
3
4
if (obj.a== null ){
    //这里至关于 obj.a === null || obj.a ===undefined ,简写形式
    //这是 jquery 源码中推荐的写法
}

  双等会进行强制类型转换,三等不会进行强制类型转换。

  除了上面的例子用双等,其它的都用三等。

JS中有哪些内置函数 

  数据封装类对象
  Object
  Array
  Boolean
  Number
  String
  Function
  Date
  RegExp :正则表达式
  Error

JS变量按照存储方式区分为哪些类型,并描述其特色

  值类型和引用类型。

  从内存来讲,值类型是把每一个值分块存放在内存中,而引用类型是好几个变量公用一个内存块,节省内存空间。

如何理解JSON  

  JSON只不过是一个JS对象,也是一种数据格式。
  JSON基本的api只有两个:
  JSON.stringify({a:10,b:20}); //对象转字符串
  JSON.parse('{"a":10,"b":20}'); //字符串转对象

 

 2、原型和原型链

题目:

  • 如何准确判断一个变量是数组类型
  • 写一个原型链继承的例子
  • 描述new一个对象的过程
  • zepto(或其余框架)源码中如何使用原型链

 知识点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//大写开头的函数基本都是构造函数,这么写提升代码可读性
 
/*1、构造函数*/
   function  Foo(name,age){
     this .name = name;
     this .age = age;
     this . class  'class-1' ;
     //return this //默认有这一行,最好不要写
   }
   var  f =  new  Foo( 'zhangsan' ,20);
   //var f1 = new Foo('lisi',22); //建立多个对象
   //new时把参数传进去。new函数执行时里面的this会变成空对象,给this赋值后,再把this给return回来,return回来就把值赋值给了f,这时f就具有f.name = 'zhangsan',f.age = 20,f.class ='' 'class-1'
   
 
/*2、构造函数-扩展*/
   /*
   var a = {} 实际上是 var a = new Object() 的语法糖。 构造函数是Object函数。
   var a = [] 实际上是 var a = new Array() 的语法糖。 构造函数是Array函数。
   function Foo(){...} 实际上是 var Foo = new Function(...)。 构造函数是Function。
   推荐前面的书写方式。
   使用instanceof判断一个函数是不是一个变量的构造函数。好比:判断一个变量是否为“数组”:变量 instanceof Array
   */
 
/*3、原型规则和示例*/
   //5条原型规则-原型规则是学习原型链的基础
   //一、全部的引用类型(数组、对象、函数),都具备对象特性,便可自由扩展属性(除了null之外)
   var  obj = {}; obj.a = 100;
   var  arr = []; arr.a = 100;
   function  fn(){}; fn.a = 100;
   //二、全部的引用类型(数组、对象、函数),都有一个__proto__属性,属性值是一个普通的对象。 __proto__ 隐式原型
   console.log(obj.__proto__);
   console.log(arr.__proto__);
   console.log(fn.__proto__);
   //三、全部的函数,都有一个prototype属性,属性值也是一个普通的对象。  prototype显式原型
   console.log(fn.prototype);
   //四、全部的引用类型(数组、对象、函数),__proto__属性值指向它的构造函数的"prototype"属性值。
   console.log(obj.__proto__ === Object.prototype);
   //五、当试图获得一个对象的某个属性时,若是这个对象自己没有这个属性,那么会去它的 __proto__(即它的构造函数的prototype)中寻找。
   //构造函数
   function  Foo(name,age){
     this .name = name;
   }
   Foo.prototype.alertName =  function (){
     alert( this .name);
   }
   //建立示例
   var  f =  new  Foo( 'zhangsan' );
   f.printName =  function (){
     console.log( this .name);
   }
   //测试
   f.printName();  //zhangsan
   f.alertName();  //zhangsan
 
   //补充 -- 循环对象自身的属性
   var  item;
   for (item  in  f){
     //高级浏览器已经在 for in 中屏蔽了来自原型的属性
     //可是这里建议仍是加上这个判断,保证程序的健壮性
     if (f.hasOwnProperty(item)){
       console.log(item);
     }
   }
 
 
/*4、原型链*/
   //构造函数
   function  Foo(name,age){
     this .name = name;
   }
   Foo.prototype.alertName =  function (){
     alert( this .name);
   }
   //建立示例
   var  f =  new  Foo( 'zhangsan' );
   f.printName =  function (){
     console.log( this .name);
   }
   //测试
   f.printName();  //zhangsan
   f.alertName();  //zhangsan
   f.toString();  //要去f.__proto__.__proto__中查找
 
 
/*5、instanceof*/
   //用于判断引用类型属于哪一个构造函数的方法
   //构造函数
   function  Foo(name,age){
     this .name = name;
   }
   Foo.prototype.alertName =  function (){
     alert( this .name);
   }
   //建立示例
   var  f =  new  Foo( 'zhangsan' );
   f.printName =  function (){
     console.log( this .name);
   }
   //测试
   f.printName();  //zhangsan
   f.alertName();  //zhangsan
   f.toString();  //要去f.__proto__.__proto__中查找
 
   //instanceof
   //f instanceof Foo 的判断逻辑是:
   //一、f 的 __proto__ 一层一层往上,可否对应到 Foo.prototype
   //二、再试着判断 f instanceof Object

 

  

 

 

解题:

如何准确判断一个变量是数组类型 

1
2
3
var  arr = [];
arr  instanceof  Array;  //true
typeof  arr;  //object   typeof是没法判断是不是数组的 

写一个原型链继承的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//动物
  function  Animal(){
      this .eat =  function (){
          console.log( 'animal eat' );
      }
  }
  
  //狗
  function  Dog(){
      this .bark =  function (){
          console.log( 'dog bark' );
      }
  }
  Dog.prototype =  new  Animal();
  //哈士奇
  var  hashiqi =  new  Dog();
 
  /*面试时千万不要写这个例子,要写更贴近于实战的原型链例子*/
1
/*用下面的例子*/

//写一个封装DOM查询的例子
function Elem(id){
  this.elem = document.getElementById(id);
}

Elem.prototype.html = function(val){
  var elem = this.elem;
  if(val){
    elem.innerHTML = val;
    return this; //链式操做
  }else {
    return elem.innerHTML;
  }
}

Elem.prototype.on = function(type,fn){
  var elem = this.elem;
  elem.addEventListener(type,fn);
  return this;
}

var div1 = new Elem('div1');
console.log(div1.html());
div1.html('<h2>新内容</h2>').on('click',function(){
alert(div1.html());
}).html('<h2>新内容新内容新内容</h2>').on('click',function(){
alert('第二次');
}); //链式操做


/*div1.html('<h2>新内容</h2>')
div1.on('click',function(){
alert(div1.html());
})*/

1
  

描述new一个对象的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
   一、建立一个新对象
   二、this指向这个新对象
   三、执行代码,即对this赋值
   四、返回this
  */
  /*构造函数*/
   function  Foo(name,age){
     this .name = name;
     this .age = age;
     this . class  'class-1' ;
     //return this //默认有这一行
   }
   var  f =  new  Foo( 'zhangsan' ,20);
   //var f1 = new Foo('lisi',22); //建立多个对象

zepto(或其余框架)源码中如何使用原型链

  1. 阅读源码是高效提升技能的方式。如zepto
  2. 但不能“埋头苦钻”,有技巧在其中
  3. 慕课网搜索“zepto设计和源码分析”

 

3、做用域和闭包

题目:

  • 说一下对变量提高的理解
  • 说明this几种不一样的使用场景
  • 建立10个<a>标签,点击时弹出对应的序号
  • 如何理解做用域
  • 实际开发中闭包的应用

知识点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*1、执行上下文*/
  //范围:一段<script>或者一个函数
  //全局:变量定义、函数声明(一段<script>)
  //函数:变量定义、函数声明、this、arguments(函数)
  //PS:注意“函数声明”和“函数表达式”的区别
  //实际代码中不要下面这种写法,都是先定义,再执行,提升代码可读性。
  console.log(a);  //undefined
  var  a = 100;
 
  fn( 'zhangsan' );  //'zhangsan' 20
  function  fn(name){  //函数声明
   age = 20;
   console.log(name,age);
   var  age;
  }
  var  fn =  function (){}  //函数表达式
 
 
/*2、this*/
     //this要在执行时才能确认值,定义时没法确认。
     //做为构造函数执行
     //做为对象属性执行
     //做为普通函数执行
     //call、apply、bind
     var  a = {
         name: 'A' ,
         fn: function (){
             console.log( this .name);
         }
     }
     a.fn();  //this===a
     a.fn.call({name: 'B' });  //this==={name:'B'}
     var  fn1 = a.fn;
     fn1();  //this===window
 
 
/*做用域*/
     /*无块级做用域*/
     //没有块级做用域,写在里面和外面是同样的。不建议下面这种写法,程序不易读。
     if ( true ){
         var  name =  'zhangsan' ;
     }
     console.log(name);  //zhangsan
     /*函数和全局做用域*/
     var  a = 100;  //全局变量
     function  fn(){
         var  a = 200;  //局部变量
         console.log( 'fn' ,a);
     }
     console.log( 'global' ,a);
     fn();
 
 
/*做用域链*/
     //函数的父级做用域是函数定义时的做用域,不是函数执行时的做用域。
     var  a = 100;
     function  fn(){
         var  b = 200;
 
         //当前做用域没有定义的变量,即“自由变量”
         console.log(a);  //100
         console.log(b);  //200
     }
     fn();
     //例子
     var  a = 100;
     function  F1(){
         var  b = 200;
         function  F2(){
             var  c = 300;
             console.log(a);  //100 //自由变量
             console.log(b);  //200 //自由变量
             console.log(c);  //300
         }
         F2();
     }
     F1();
 
 
 
/*闭包*/
     function  F1(){
         var  a = 100;
 
         //返回一个函数(函数做为返回值)
         return  function (){
             console.log(a);
         }
     }
     //f1获得一个函数
     var  f1 = F1();
     var  a = 200;
     f1();
     //f1执行的是return里的函数,return里的a是个自由变量,要去父级做用域寻找,父级做用域F1里面定义了a,因此打印出来的a的值是100.
     /*闭包的使用场景
     一、函数做为返回值(上一个demo)
     二、函数做为函数传递(下面这个例子)
     */
     function  F1(){
         var  a = 100;
         return  function (){
             console.log(a);
         }
     }
     var  f1 = F1();
     function  F2(fn){
         var  a = 200;
         fn();
     }
     F2(f1);

解题:

说一下对变量提高的理解 

变量定义
函数声明(注意和函数表达式的区别)
执行上下文的概念。
各个函数中,它的变量的声明和定义,以及函数的声明都会提早,放在前面,由此就是变量提高主观上、形象上的理解。

说明this几种不一样的使用场景

做为构造函数执行

做为对象属性执行

做为普通函数执行

call、apply、bind

建立10个<a>标签,点击时弹出对应的序号

1
2
3
4
5
6
7
8
9
10
11
12
var  i;
for (i=0;i<10;i++){
     ( function (i){
         var  a = document.createElement( 'a' );
         a.innerHTML = i+ '<br>' ;
         a.addEventListener( 'click' , function (e){
             e.preventDefault();
             alert(i);
         })
         document.body.appendChild(a);
     })(i);
}

如何理解做用域

要领:
一、自由变量
二、做用域链,即自由变量的查找
三、闭包的两个场景

实际开发中闭包的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//闭包实际应用中主要用于封装变量,收敛权限
     function  isFirstLoad(){
         var  _list = [];
         return  function (id){
             if (_list.indexOf(id)>=0){
                 return  false ;
             } else  {
                 _list.push(id);
                 return  true ;
             }
         }
     }
     //使用
     var  firstLoad = isFirstLoad();
     firstLoad(10);  //true
     firstLoad(10);  //false
     firstLoad(20);  //true//在 isFirstLoad 函数外面,根本不可能修改掉 _list 的值

  

4、异步和单线程

题目: 

  • 同步和异步的区别是什么?分别举一个同步和异步的例子
  • 一个关于setTimeout的笔试题
  • 前端使用异步的场景有哪些

知识点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/*什么是异步(对比同步)*/
     //同时执行,不会阻塞程序执行
     console.log(100);
     setTimeout( function (){
         console.log(200);
     },1000);
     console.log(300);
     setTimeout( function (){
         console.log(400);
     },1000);
     //下面这个例子相似同步
     console.log(100);
     alert(200);
     console.log(300);
 
 
 
/*前端使用异步的场景*/
     /*什么时候须要异步:
     在可能发生等待的状况
     等待过程当中不能像alert同样阻塞程序进行
     所以,全部的“等待的状况”都须要异步
     一、定时任务:setTimeout、setInterval
     二、网络请求:ajax请求、动态<img>加载
     三、事件绑定
     */
     //ajax请求代码示例
     console.log( 'start' );
     $.get( './data1.json' , function (data1){
         console.log(data1);
     })
     console.log( 'end' );
     //<img>加载示例
     console.log( 'start' );
     var  img = document.createElement( 'img' );
     img.onload =  function (){
         console.log( 'loaded' );
     }
     img.src =  '/xxx.png' ;
     console.log( 'end' );
     //事件绑定示例
     console.log( 'start' );
     document.getElementById( 'btn1' ).addEventListener( 'click' , function (){
         alert( 'clicked' );
     })
     console.log( 'end' );
     
 
 
 
/*异步和单线程*/
     console.log(100);
   setTimeout( function (){
     console.log(200);
   });
   console.log(300);
   //执行结果:100、300、200
   //setTimeout是异步,最后执行。这个例子的setTimeout没有等待时间,因此待其它执行完以后立马执行setTimeout。
   //单线程就是一次只能作一件事
   /*解析:
   一、执行第一行,打印100
   二、执行setTimeout后,传入setTimeout的函数会被暂存起来,不会当即执行(单线程的特色,不能同时干两件事)
   三、执行最后一行,打印300
   四、待全部程序执行完,处于空闲状态时,会立马看有没有暂存起来的要执行。
   五、发现暂存起来的setTimeout中的函数无需等待时间,就当即拿过来执行。
   */

 

解题:

同步和异步的区别是什么?分别举一个同步和异步的例子

同步会阻塞代码执行,而异步不会。
alert是同步,setTimeout是异步。

例子:

1
2
3
4
5
6
7
8
9
10
//异步
     console.log(100);
     setTimeout( function (){
         console.log(200);
     },1000);
     console.log(300);
//同步
     console.log(100);
     alert(200);
     console.log(300);

 

一个关于setTimeout的笔试题

1
2
3
4
5
6
7
8
9
10
11
console.log(1);
   setTimeout( function (){
     console.log(2);
   },0)
   console.log(3);
   setTimeout( function (){
     console.log(4);
   },1000)
   console.log(5);
   
   //1 3 5 2 4

 

前端使用异步的场景有哪些  

  1. 定时任务:setTimeout、setInterval
  2. 网络请求:ajax请求、动态<img>加载
  3. 事件绑定

 

5、其它知识点

题目:

  • 获取2017-06-10格式的日期
  • 获取随机数,要求是长度一致的字符串格式
  • 写一个能遍历对象和数组的通用forEach函数

知识点: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/*日期*/
  Date.now();  //获取当前时间毫秒数
  var  dt =  new  Date();
  dt.getTime();  //获取毫秒数
  dt.getFullYear();  //年
  dt.getMonth();  //月(0-11)
  dt.getDate();  //日(0-31)
  dt.getHours();  //小时(0-23)
  dt.getMinutes();  //分钟(0-59)
  dt.getSeconds();  //秒(0-59)
 
 
 
/*Math*/
     Math.random();  //获取随机数。(返回的是 >0 和 <1 的小数)。有清除缓存的做用。
 
 
 
/*数组API*/
     //forEach //遍历全部元素
     var  arr = [1,2,3];
     arr.forEach( function (item,index){
         //遍历数组的全部元素。item:元素的值。index:元素的位置
         console.log(index,item);
     })
     //every   //判断全部元素是否都符合条件
     var  arr = [1,2,3];
     // var arr = [1,2,3,4,5];
     var  result = arr.every( function (item,index){
         if (item<4){
             return  true ;
         }
     })
     console.log(result);  //true
     //some    //判断是否有至少一个元素符合条件
     var  arr = [1,2,3];
     var  result = arr.some( function (item,index){
         if (item<2){
             return  true ;
         }
     })
     console.log(result);  //true
     //sort    //排序
     var  arr = [1,4,2,5,3];
     var  arr2 = arr.sort( function (a,b){
         //从小到大排序
         return  a -b;
 
         //从大到小排序
         //return b-a;
     })
     console.log(arr2);
     //map     //对元素从新组装,生成新数组
     var  arr = [1,2,3,4];
     var  arr2 = arr.map( function (item,index){
         return  '<b>'  + item +  '</b>' ;
     })
     console.log(arr2);
     //filter  //过滤符合条件的元素
     var  arr = [1,2,3,4];
     var  arr2 = arr.filter( function (item,index){
         if (item>=2){
             return  true ;
         }
     })
     console.log(arr2);
 
 
 
/*对象API*/
     var  obj = {
         x:100,
         y:200,
         z:300
     }
     var  key;
     for (key  in  obj){  //key是obj的属性名
         //注意这里的 hasOwnProperty ,再讲原型链时候讲过了
         if (obj.hasOwnProperty(key)){  //判断key是obj原生的属性,而不是原型里面的属性
             console.log(key,obj[key]);
         }
     }

 

解题:

获取2017-06-10格式的日期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function  formatDate(dt){
   if (!dt){
     dt =  new  Date();
   }
   var  year = dt.getFullYear();
   var  month = dt.getMonth() + 1;
   var  date = dt.getDate();
   if (month<10){
     //强制类型转换
     month =  '0' +month;
   }
   if (date<10){
     //强制类型转换
     date =  '0' +date;
   }
   //强制类型转换
   return  year +  '-'  + month +  '-'  + date;
}
var  dt =  new  Date();
var  formatDate = formatDate();
console.log(formatDate);

获取随机数,要求是长度一致的字符串格式

1
2
3
4
var  random = Math.random();
var  random = random +  '0000000000' //后面加上10个零,为了确保长度一致
var  random = random.slice(0,10);  //截取前10位
console.log(random);

写一个能遍历对象和数组的通用forEach函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function  forEach(obj,fn){
     var  key;
     if (obj  instanceof  Array){
       //准确判断是否是数组
       obj.forEach( function (item,index){
         fn(index,item);
       })
     } else {
       //不是数组就是对象,对象用for in循环
       for (key  in  obj){
         fn(key,obj[key]);
       }
     }
   }
   var  arr = [1,2,3];
   //注意,这里参数的顺序换了,为了和对象的遍历格式一致
   forEach(arr, function (index,item){
     console.log(index,item);
   })
   var  obj = {x:100,y:200};
   forEach(obj, function (key,value){
     console.log(key,value);
   })

 

 

 

 

 

 
 
标签:  js面试前端
相关文章
相关标签/搜索