前端Js面试题总结

JS简单,但要学好需要多花些功夫,总结下以前遇到的Js面试题

不知道说些什么来当一些引子,总之前端Js占据了大半壁江山。

1.变量提升及作用于

1
2
3
4
5
6
7
8
var foo = 1;
function bar() {
foo = 10;
return;
function foo() {}
}
bar();//function foo() {}
console.log(foo);

在JavaScript中,函数、变量的声明都会被提升(hoisting)到该函数或变量所在的scope的顶部。即——JavaScript的变量提升.
function的定义会提前到当前作用域之前,所以等同于,在foo=10的时候,foo是有定义的,属于局部变量,影响不到外层的foo

2.连续赋值

1
2
3
4
5
6
function a(){
var o1 = o2 = 5;
}
a();
console.log(o1); //not definde
console.log(o2); //5

实际顺序:
var o1;
o2 = 5; //o2未使用var声明,所以变全局变量了
o1 = o2;

3.连续赋值/词法分析/函数执行原理

1
2
3
4
5
var foo = {n:1};
var bar = foo;
foo.x = foo = {n:2};
console.log(foo.x);
console.log(bar.x);

JS引擎遇到foo.x = foo = {n:2}; 词法分析为foo.x, =, foo, =, {n:2}
注解链接
注解链接
推荐两本书:《javascript权威指南》《你不知道的javascript》
小技巧:对象,Array,任意的.x获取的值在不存在value时都是undefind

4.this在javascript中是如何工作的

1
2
3
4
5
6
7
8
9
10
11
12
13
var fullname = 'John Doe';
var obj = {
fullname: 'Colin Ihrig',
prop: {
fullname: 'Aurelio De Rosa',
getFullname: function() {
return this.fullname;
}
}
};
console.log(obj.prop.getFullname());
var test = obj.prop.getFullname;
console.log(test());

注解链接
在 JavaScript 中,一个函数的上下文环境,也就是this关键词所引用对象,是依赖于函数是如何被调用的,而不是依赖于函数如何b被定义的。
在第一个console.log()调用中, getFullname()是作为obj.prop的函数被调用的。因此,这里的上下文环境指向后者并且函数返回this对象的 fullname属性。相反,当 getFullname() 被赋为test变量的值时,那个语境指向全局对象(window)。这是因为,test被隐式设置为全局对象的属性。因此,函数调用返回window的fullname属性值,在此段代码中,这个值是通过第一行赋值语句设置的。

5.浅赋值/

1
2
3
4
5
var a = {};
a.value = 1;
b = a;
b.value = 2;
console.log(a.value);

注解来源
a是对象,a应该是保存了一个地址,这个地址指向了a存储的实际内容,b=a并不是将a指向地址的内容复制给b而是把a保存的地址给了b,即使得b与a指向同一个地址(a存储的实际内容),所以b.value=2设置的是a存储的实际内容,a存储的实际内容的值改变了,所以a.value也改变了。
当一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到为新变量分配的空间中。不同的是这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此改变其中一个变量,就会影响到另一个变量
javascrip变量包含两种类型的值,一种为引用类型的值,一种是基本类型的值。引用类型包括:Array,Object,Function(可以这么理解,非基本类型的都是引用类型);5种基本类型包括:undefined,null,string,boolean,number

6.异步、作用域、闭包/下面这个ul,如何点击每一列的时候alert其index?

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
for(var i=1;i<=3;i++){
setTimeout(function(){
console.log(i);
},0);
};
var i = 0;
console.log(i);
i++;
console.log(i);
i++;
console.log(i);
i++;
setTimeout(function() {
console.log(i);
}, 0);
setTimeout(function() {
console.log(i);
}, 0);
setTimeout(function() {
console.log(i);
}, 0);
for (var i = 0; i < 3; i++) {
setTimeout((function(i) {
return function() {
console.log(i);
};
})(i), 0);
console.log(i);
}

调用setTimeout时,把函数参数,放到事件队列中。等主程序运行完,再调用

7.数组去重

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var arr = [1,3,"1",2,3,4,55,23,4,23,2];
var ar2 = new Set(arr);
console.log(Array.from(ar2))
var array = [1, '1', 1, 2, 3, 2, 4];
var tmpObj = {};
var result = [];
array.forEach(function(a) {
var key = (typeof a) + a;
if (!tmpObj[key]) {
tmpObj[key] = true;
result.push(a);
}
});
console.log(result);

注释来源

8.立即执行函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var test = ( function(a){
this.a = a;
return function(b) {
return this.a+ b;
}
})((function(a, b){
return a
})(1,2));
console.log(test(4));
var test = function(a){
return (function(b){
return b+1
})(a)
}
console.log(test(2))

注解解释

9.Js中new操作符到底干了些什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function A() {
this.name = 'lvdaofeng';
return 'wangdaming';
};
var a = new A();
console.log(a.name);//lvdaofeng
console.log(A());//wangdaming
function B() {
this.name = 'chengli';
return {
name : 'dapang'
}
}
var b = new B();
console.log(b.name);//dapang
console.log(B());//{name : 'dapang'}

注释来源
Js中new操作符

10.Js单线程/setTimeout

1
2
3
4
5
6
7
8
9
10
11
12
13
for(var i=1;i<=3; i++) {
console.log(i);
}
for(var i=1;i<=3; i++) {
setTimeout(function() {
console.log(i);
},0)
}
for(var i=1;i<=3; i++) {
setTimeout((function(a) {
console.log(a);
})(i),0)
}

注解来源

11.JavaScript中proto与prototype的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var a = 1;
console.log(a.__proto__);//Number
console.log(a.__proto__.__proto__);//Object
console.log(a.__proto__.__proto__.__proto__);//null
console.log(a.prototype);
var a = {
x:1
};
console.log(a.__proto__);//Object
console.log(a.__proto__.__proto__);//null
console.log(a.prototype);
function a() {
this.y = 2;
}
console.log(a.__proto__);//Object
console.log(a.__proto__.__proto__);//null
console.log(a);
var Person = function() {};
var p = new Person();
console.log(p.__proto__ );
console.log(Person.prototype);

JavaScript中proto与prototype的关系

12.this作用对象及引用类型的对象复制时的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var obj = {
name:1
};
var aa = obj;
aa.name = 2;
var bb = obj ;
bb.name = 3;
console.log(aa.name);
function Obj() {
this.name = 1;
}
var aa = new Obj();
aa.name = 2;
var bb = new Obj();
bb.name = 3;
console.log(aa.name);

[已经不在话下]

13.深度理解Js静态作用域

1
2
3
4
5
6
7
8
9
var scope = 'top';
var f1 = function() {
console.log(scope);
};
var f2 = function() {
var scope = 'f2';
f1();
};
f2();//top

深度理解Js静态作用域

14.Js函数prototype

1
2
3
4
5
6
7
8
9
10
function A() {
this.add1 = function() {
return ++this.num;
}
}
A.prototype.num = 1;
var a1 = new A();
var a2 = new A();
console.log(a1.add1());
console.log(a2.add1());

15.Js函数prototype

1
2
3
4
5
6
7
8
9
10
11
12
function A() {
this.add1 = function() {
return ++this.num.x;
}
}
A.prototype.num = {
x:1
};
var a1 = new A();
var a2 = new A();
console.log(a1.add1());
console.log(a2.add1());

16.setTimeout定时器的异步操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let person = {
name : '大胖',
say : function() {
console.log(this.name);
},
say1 : ()=>{
console.log(this.name);
}
};
let name = '二胖';
setTimeout(function() {
console.log(this);
person.say();//大胖
}, 100);
setTimeout( person.say, 100 );//二胖
person.say1();//二胖
setTimeout(function() {
console.log(this);
person.say1();//二胖
}, 100);
setTimeout( person.say1, 100 );//二胖

17.柯丽化/函数 add 可以实现连续的加法运算

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
使用示例
add(10)(10); // 20
add(10)(20)(50); // 80
add(10)(20)(50)(100); // 180
var add30 = add(10)(20); // 30
var add100 = add30(30)(40); // 100
var add31 = add30(1); // 31
var add40 = add31(9); // 40
function add(x) {
function helper(y, x) {
var sum = y + x;
var f = helper.bind(null, sum);
f.toString = function () {
return '' + sum;
};
f.valueOf = function () {
return sum;
};
return f;
}
return helper(x, 0);
}
console.log(+add(10)(10)); // 20
console.log(+add(10)(20)(50)); // 80
console.log(+add(10)(20)(50)(100)); // 180
var add30 = add(10)(20); // 30
var add100 = add30(30)(40); // 100
var add31 = add30(1); // 31
var add40 = add31(9); // 40
console.log(+add30, +add100, +add31, +add40);

18.函数重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1.var m= 1, j = k = 0;
function add(n) {
return n = n+1;
  }
console.log(add(m)); //4
function add(n) {
return n = n + 3;
}
console.log(add(m));//4
2.function sum(n){
return n = n+1
}
console.log(sum(1))//2
var sum =function(n){
return n = n+2
}
console.log(sum(2))//4

js里面没有函数重载的概念,在其他语言中(如java)java中,可以存在同名函数,只要传入的参数数量或者类型不同即可。在js中,定义了两个同名函数后,
后面的函数会覆盖前面定义的函数。结合这道题来说,由于函数声明提升,所以函数声明会提前,由于存在同名函数,后面的add函数将覆盖第一个add函数,
所以两次调用add()返回的值是相同的。也就是y,z都为4.
函数声明跟函数表达式是不同的
注解来源

19.使用 for in 循环数组中的元素会枚举原型链上的所有属性,过滤这些属性的方式是使用hasOwnProperty函数

20.Function 函数声明提升,关键点在于function将外部var bb定义带入到了函数内部,此时函数里面的定义就是局部变量

1
2
3
4
5
6
7
var bb = 1;
function aa(bb) {
bb = 2;
alert(bb);
};
aa(bb);
alert(bb);

此题来源

21.经典的变量提升问题

1
2
3
4
5
6
7
8
9
if(!"a" in window){
var a = 1;
}
alert(a);//undefined
等价于:
if("a" in window){
var a = 1;
}
alert(a);//1

22.Number函数
Number(null)
阮一峰

23.变量提升问题

1
2
3
4
5
var msg='hello';
for (var i=0; i<10; i++)
{
var msg='hello'+i*2+i;
}

24.Function外部变量/变量提升

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var x=10;
function calc(myNum) {
return x+myNum;
}
console.log(calc(7))//17
var name=’World!’;
(function() {
if(typeof name===’undefined’) {
var name=’Jack’;
console.log(‘Goodbye’+name);
} else {
console.log(‘Hello’+name);
}
})();

  1. 函数的命名方式
    1
    2
    3
    4
    var f = function g() {
    return 23;
    };
    typeof g();

在 JS 里,声明函数只有 2 种方法:
第 1 种: function foo(){…} (函数声明)
第 2 种: var foo = function(){…} (等号后面必须是匿名函数,这句实质是函数表达式)
如果是typeof f,结果是function
如果是typeof f(),结果是number
如果是typeof g,结果是undefined.
如果是typeof g(),结果是ReferenceError,g is not defined

26.检测数组基本类型
1.typeof 2.Array.isArray() 3.is instanceof 4.Object.prototype.toString.call() 5.Object.constructor == Array
6.比较好的检测数组方法

1
2
3
4
5
if(typeof a == 'function'){
return Array.isArray(a)
}else{
return Object.prototype.toString.call(a) =='[Object Array]'
}

注解来源

27.Web前端性能优化
[注解来源]https://segmentfault.com/a/1190000005035440

28.对象的集中创建模式解决没有类的问题

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
1.工厂模式
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job
o.sayName = function(){
console.log(this.name)
}
return o;
}
var person1 = createPerson('1','2','3');
var person2 = createPerson('4','5','6');
console.log(person1)
console.log(person2)
解决了创建多个相似对象的问题,但没有解决对象识别的问题(即怎么知道一个对象的类型)
2.构造函数模式
function CreatePerson(name,age,job){
this.name = name;
this.age = age;
this.job = job
this.sayName = function(){
console.log(this.name)
}
}
var person1 = new CreatePerson('1','2','3');
var person2 = new CreatePerson('4','5','6');
console.log(person1.name)
console.log(person2.name)
构造函数的问题主要是,就是每个方法都需要在实例中重新创建一遍;不同实例中的同名函数的不相等的,有个解决方案是将方法改为全局函数,构造函数里面只剩指针
function CreatePerson(name,age,job){
this.name = name;
this.age = age;
this.job = job
this.sayName = sayName
}
function sayName(){
console.log(this.name)
}
3.原型模式
function Person(){
}
Person.prototype.name = '1'
Person.prototype.age = '2'
Person.prototype.job = '3'
Person.prototype.sayName = function(){
console.log(this.name)
}
var person1 = new Person()
person1.sayName()//1
var person2 = new Person()
person2.sayName() //1
console.log(person1.sayName == person2.sayName)//true
function Person(){
}
Person.prototype = {
name = '1',
age = '2',
job = '3',
sayName = function(){
console.log(this.name)
}
}
此时所有的实例访问的都是同一组属性和方法,新对象的属性和方法都是由实例共享的
Object.getPrototype()返回对象的原型;hasOwnPrototype()检测访问属性什么时候是实例属性/true,什么时候是原型属性/false
Object.keys()返回所有可以枚举的实例属性
Object.getOwnPropertyNames()返回所有无论是否可以枚举的对象
Object.defineProperty()重设数据属性
4.组合使用构造函数模式和原型模式(使用程度最高的创建自定义类型的方法,定义引用类型的一种默认方法)
构造函数用于定义实例属性,而原型模式用于定义方法和共享的属性,还可以通过构造函数传递参数
function Person(name,age,job){
this.name =name;
this.age = age;
this.job = job;
this.friends = ['baby','angle']
}
Person.prototype = {
constructor: Person,
sayName: function(){
console.log(this.name)
}
}
var person1 = new Person('Nichon',29,'SoftWare');
var person2 = new Person('Lamdr',26,'Engineer');
person1.friends.push('Van');
console.log(person1.friends)/3
console.log(person2.friends)/2
5.动态原型模式
function Person(name,age,job){
this.name =name;
this.age = age;
this.job = job;
if(typeof this.sayName !='function'){
Person.prototype.sayName = function(){
console.log(this.name)
}
}
}
var friend = new Person('nich',29,'SoftWare')
friend.sayName()
6.寄生构造函数
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job
o.sayName = function(){
console.log(this.name)
}
return o;
}
var person1 = new createPerson('1','2','3');
var person2 = new createPerson('4','5','6');
7.稳妥构造函数模式(所谓的稳妥对象,指的是没有公共属性,而且其方法也不引用this的对象;适合在一些环境中禁止this和new)
function createPerson(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job
o.sayName = function(){
console.log(name)
}
return o;
}
var result = createPerson('can',29,'SoftWare');
result.sayName()
29.Js继承问题
var Cat = function(){
this.Color = "black";
this.Eat = function(){
alert("吃老鼠");
};
}
Cat.prototype.A = function(){
alert("Cat A");
};
Cat.prototype.B = function(){
alert("Cat B");
}
var Dog = function(){
this.Weight = "30";
}
Dog.prototype.testDog = function(){
alert("test Dog");
}
var extend = function(Child,Parent){
var p = new Parent();
var c = Child.prototype;
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
}
//用我们写好的继承方法执行继承
extend(Dog,Cat);
var dog1 = new Dog();
dog1.A(); // Cat A
dog1.Eat(); // 吃老鼠
dog1.testDog(); // test Dog
alert(dog1.Weight); // 30
2.组合继承
[高程设计](注解来源)
var Cat = function(){
this.Color = "black";
this.Eat = function(){
console.log("吃老鼠");
};
}
Cat.prototype.A = function(){
console.log("Cat A");
};
Cat.prototype.B = function(){
console.log("Cat B");
}
var Dog = function(){
this.Weight =30
}
Dog.prototype.testDog = function(){
console.log('吃Dog')
}
var extend = function(child,parent){
var p = new parent();
var c = child.prototype;
for(var i in p){
c[i] = p[i]
}
c.uber =p
}
extend(Dog,Cat);
var dog1 = new Dog();
dog1.A(); // Cat A
dog1.Eat(); // 吃老鼠
dog1.testDog(); // test Dog
console.log(dog1.Weight); // 30
console.log(dog1.constructor);

30.javaScript中call() 与apply()用法
注解来源

31.经典的闭包题(来源:小小沧海 http://www.cnblogs.com/xxcanghai/p/4991870.html)

32.前端优化(来源:https://segmentfault.com/a/1190000005035440)

33.事件模型及事件代理/委托
事件的三个阶段:事件捕获/目标阶段/冒泡阶段,低版本IE不支持捕获阶段
注解来源
注解来源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
事件委托
var ullist = document.getElementById('links');
ullist.addEventListener('click',function(e){
var eventtarget = e.target.id
switch (eventtarget){
case 'listone':
console.log(1)
break;
case 'listtwo':
console.log(2)
break;
case 'listthree':
console.log(3)
break;
default:
break;
}
},false)
事件派发

34.身份证可能是15位或18位数字,最后一位可能是X

1
2
3
4
5
6
7
8
function sum(str){
var RegExp = /^(\d{14}|\d{17})(\d|[xX])$/
if(RegExp.test(str)){
console.log('身份证信息完全正确')
}else{
console.log('请输入正确数字')
}
}

注解来源

35.两个包含着完全相同的字符而且字符顺序也相同的字符串被认为是相同的字符串

36.时间戳的计算

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
1.计算某天到现在的天数,并且能够自动计算
var birthDay = new Date('06/30/2016 19:34:23');
setInterval(function() {
var now = new Date();
var num_1 = 1000 * 60 * 60 * 24;
var num_2 = 1000 * 60 * 60;
var duration = now.getTime() - birthDay.getTime();
var total = Math.floor(duration / num_1);
var hour = Math.floor((duration % num_1) / num_2);
var minute = Math.floor(((duration % num_1) % num_2) / (1000 * 60));
var second = Math.floor((((duration % num_1) % num_2) % (1000 * 60)) / 1000);
document.getElementById("showDays").innerHTML = "本站已运行" + total + "天" + hour + '时' + minute + '分' + second + '秒';
}, 1000);
function settime(){
date.getFullYear(); // 获取完整的年份(4位,1970)
date.getMonth(); // 获取月份(0-11,0代表1月,用的时候记得加上1)
date.getDate(); // 获取日(1-31)
date.getTime(); // 获取时间(从1970.1.1开始的毫秒数)
date.getHours(); // 获取小时数(0-23)
date.getMinutes(); // 获取分钟数(0-59)
date.getSeconds();
}
2.Js一个整数的阶乘
function factorialize(num) {
var result = 1;
for (var i = 1; i <= num; i++) {
result *= i;
}
return result;
}
3.找出一个字符串里面出现最多个数的字符
var str='dasdasdadsssssaaaaaaaaaaaaa';
function maxCode(str) {
var obj = {},
index = 0,
max = 0;
for(var i=0,len = str.length; i<len; i++) {
if( !obj[str.charAt(i)] ) {
obj[str.charAt(i)] = 1;
} else {
obj[str.charAt(i)] ++;
}
}
for(var i in obj){
if(obj[i] > max) {
index = i;
max = obj[index];
}
}
return {
index :index,
max : max
}
}
console.log(maxCode(str));

文章目录
  1. 1.
  2. 2.
  3. 3.
  4. 4.
|