思考: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(或其余框架)源码中如何使用原型链
- 阅读源码是高效提升技能的方式。如zepto
- 但不能“埋头苦钻”,有技巧在其中
- 慕课网搜索“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
|
前端使用异步的场景有哪些
- 定时任务:setTimeout、setInterval
- 网络请求:ajax请求、动态<img>加载
- 事件绑定
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);
})
|