一些面试题(2)

第1题 作用域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//
// (1) 变量提升 var num1, num2, function f1(){}
var num1 = 55
var num2 = 66
function f1(num, num1){
// 有参数先声明形参的临时作用域 var num, num1 形参赋值 num=55, num1=66
num = 100 // 修改num
num1 = 100 // 修改num1
num2 = 100 // 查找当前作用域 没有上一层作用域查找 找到全局 num2并修改为100
console.log(num) // 100
console.log(num1) // 100
console.log(num2) // 100
}
f1(num1, num2)
console.log(num2) // 100 全局num2在函数内部被修改为100
console.log(num1) // 55 全局num1
console.log(num) // 全局未找到num声明则报错 num is not defiend

第2题 值类型和引用类型的传递

1
2
3
4
5
6
7
8
9
10
11
12
13
function Person(name, age){
this.name = name
this.age = age
}
function f1(person){
// 形参临时作用域 var person 形参声明 person = p (0x111) 赋值
person.name = 'ls' // 将 0x1111 地址中的name修改为 { name: 'ls', age: 16 }
person = new Person('ww', 18) // 重新开辟空间 Ox2222 {name: 'ww', age: 18} 局部变量person指向新地址Ox2222
}
var p = new Person('zs', 16) // 创建对象地址 Ox1111
console.log(p.name) // zs
f1(p)
console.log(p.name) // ls 输出 Ox1111 地址中的 name: ls

第3题 字符串转驼峰命名

1
2
3
4
5
6
7
8
9
// 将 'get-element-by-id' 转化为 'getElementById'
function toHump(str){
var arr = str.split('-')
for(var i=1;i<arr.length;i++){
arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substring(1)
}
return arr.join('')
}
toHump('get-element-by-id') // getElementById

第4题 冒泡排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var arr = [2, 1, 5, 3, 4]
var len = arr.length-1
// 轮数
for(var i=0;i<len;i++){
// 次数
for(var j=0;j<len-i;j++){
// 交换位置
if(arr[j]>arr[j+1]){
var temp = arr[j]
arr[j] = arr[j+1]
arr[j+1] = temp
}
}
}
console.log(arr)

第5题 不使用reverse反转数组

1
2
3
4
5
6
7
8
9
10
11
12
// 交换位置 反转数组
var arr = [1,2,3,4,5]
for(var i=0;i<arr.length/2;i++) {
// arr[0] 和 arr[arr.length-1-0] 交换
// arr[1] 和 arr[arr.length-1-1] 交换
// arr[2] 和 arr[arr.length-1-2] 交换
// 利用第三方变量交换两个变量的值
var temp = arr[i]
arr[i] = arr[arr.length-1-i]
arr[arr.length-1-i] = temp
}
console.log(arr)

第6题 js综合面试题getName

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
/* 变量提升 函数提升
1 function Foo(){
getName = function(){ console.log(1) }
return this
}
// 2 var getName 被同名函数 getName() 覆盖
// 3 function getName(){ console.log(5) }

// 执行代码阶段
Foo.getName = function(){ console.log(2) }
Foo.prototype.getName = function(){ console.log(3) }
getName = function(){ console.log(4) }
4 getName = function(){ console.log(4) } // 全局方法

最终执行到这里时
function Foo(){
getName = function(){ console.log(1) }
return this
}
Foo.getName = function(){ console.log(2) } // 类的静态方法
Foo.prototype.getName = function(){ console.log(3) } // 累的实例方法
getName = function(){ console.log(4) } // 全局方法

*/
function Foo(){
getName = function(){ console.log(1) }
return this
}
Foo.getName = function(){ console.log(2) } // 类的静态方法
Foo.prototype.getName = function(){ console.log(3) }
var getName = function(){ console.log(4) }
function getName(){ console.log(5) }

Foo.getName() // 2 执行类的静态方法 function(){ console.log(2) }
getName() // 4 执行全局方法 function(){ console.log(4) }()
// Foo() 执行 将全局getName修改为 getName = function(){ console.log(1) } 并返回this --> window
Foo().getName() // 1 (Foo()).getName()--> window.getName()
getName() // 1 执行全局方法 getName = function(){ console.log(1) }
// . 操作符优先级高 new (Foo.getName)() --> new (function(){ console.log(2) })()
new Foo.getName() // 2
// (new Foo()).getName()--> foo.getName() === Foo.prototype.getName()
// foo.getName === Foo.prototype.getName 实例对象的隐式原型就等于构造函数的显示原型
new Foo().getName() // 3
// new ((new Foo()).getName)()--> new (foo.getName)()--> new (function(){ console.log(3) })()
new new Foo().getName() // 3

getName

第7题 什么是函数节流? 什么是函数防抖

  • 函数节流:一个函数执行一次后,只有大于设定的执行周期后才会执行第二次
    需要频繁触发的函数,每间隔规定的时间执行一次, 降低执行频率`
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 节流函数
* @param fn 要被节流的函数
* @param delay 规定的时间
*/
function throttle(fn, delay){
var lastTime = 0 // 记录上一次函数触发的时间
return function(){
var nowTime = Date.now() // 记录当前函数触发的时间
if (nowTime - lastTime > delay) {
fn.call(this) // 修改this指向
}
lastTime = nowTime // 同步时间
}
}

document.onscroll = throttle(function(){
console.log('scroll')
}, 60)
  • 防抖函数:一个需要频繁触发的函数,在规定时间内,只让函数触发一次
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /**
    * 防抖函数
    * @param fn 要被防抖的函数
    * @param delay 规定的时间
    */
    function debounce(fn, delay){
    var timer = null
    return function () {
    clearTimeout(timer) // 清除上一次延时器
    // 重新设置新的延时器
    timer = setTimeout(function () {
    fn.call(this)
    }, delay)
    }
    }
    input.oninput = debounce(function(){
    console.log(input.value)
    }, 200)

第8题 多维数组扁平化

1
2
3
4
5
6
7
var arr = [[1,2],[3,4,5,[6,7]]]
function flatten(arr){
return [].concat(
...arr.map(item => Array.isArray(item) ? flatten(item) : item)
)
}
console.log(flatten(arr))

第9题 变量隐式转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var a = { },
b = { key:'b' },
c = { key:'c' };
a[b] = 123;
a[c] = 456;
console.log(a[b]) // 456
/* 首先a[b] 这里面b最后显示成什么?
关于变量隐式转换,对象的a[]这种写法,里面添加的是属性名称--string类型
所以a[b],b的原类型是object,目标是string类型。实际上是执行了a[b.toString()]
同理a[c]也一样
a[b.toString()]=123;
a[c.toString()]=456;
Object类型的变量 执行toString() 结果都是同样的字符串。----"[object Object]"
所以a["[object Object]"]=123; a["[object Object]"]=456;
*/

第10题 连等号执行顺序

1
2
3
4
5
6
var y = 1, x = y = typeof x;
console.log(x) // undefiend
/* var y; var x // x ,y 此时都是undefiend ---> y = 1
连等号从右向左执行
typeof x -->undefiend(字符串) -->y = undefined(字符串) --> x = undefied(字符串)
*/

第11题 toString 特例

1
2
3
4
5
// 对象.toString() ---> [object Object]
console.log({a: 1}.toString()=='[object Object]');
// 纯数字.toString() .在前面是纯数字时会被当成 小数点
// 10.toString() ==> (10.)toString() 报错
// 10..toString() ==> (10.).toString() '10'

第12题 对象引用

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
var a = {n:1}
var b = a
a = {n:2}
a.x = a
console.log(a.x) // {n: 2, x: { n:2, x: {n:2, x: ...}}} 无限
console.log(b) // {n:1}
console.log(b.x) // undefined

// = 赋值操作 不但会将右边的值保存到左边的变量中, 而且还会返回等号右边的值 a = {n:2} // {n:2}
// A=B=C连等赋值真正的运算规则是 B=C; A=B 即连续赋值是从右至左永远只取等号右边的表达式结果赋值到等号左侧。
var a = {n:1}
var b = a
a.x = a = {n:2}
console.log(a) // {n: 2}
console.log(a.x) // undefiend
console.log(b) // {n:1, x: {n:2}}
console.log(b.x) // {n:2}
/*解答思路:
1. 首先将对象{n:1}赋值给a变量 再将a赋值给b 这是一层浅拷贝过程 所以a和b变量都指向同一个对象{n:1}
2. 由于点运算符优先于赋值运算符所以先在{n:1}对象中创建了一个x属性 {n:1,x:(暂时无值)} 这个过程 因为运算符优先级的原因 赋值符号还未执行的时候就已经完成了
3. 再然后执行 a = {n:2} 此时a的引用地址发生了改变 从对象{n:1}指向了{n:2}
4. a.x此时指向的是 {n:1,x:(暂时无值)}对象 然后将{n:2} 赋值给了a.x属性 此时A对象{n:1, x:{n:2}}
5. 此时的a.x属性的父级对象a的初始引用地址是A对象 而A对象赋值给了b变量 所以你能在b.x中得到对象{n:2}
6. 原对象地址因无人引用他 被垃圾回收机制回收了 对象被赋值了B对象{n:2} 所以原有的a.x为undefined
*/

第13题 var 会突破if限制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var name = "World!";
(function(){
/*
var name 变量提神 if限制不住var变量提升
...
*/
if (typeof name === "undefined") {
var name = "Jack"
console.log(name)
} else {
console.log(name)
}
})()
// Jack

第14题 原型链与对象属性查找

1
2
3
4
5
6
7
8
9
10
function A () {
}
A.prototype.n = 1
var b = new A()
A.prototype = {
n: 2,
m: 3
}
var c = new A()
console.log(b.n, b.m, c.n, c.m) // 1 undefined 2 3

第15题 变量提升 this 原型链

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
function Person() {
getAge = function () {
console.log(10)
}
return this
}
Person.getAge = function () {
console.log(20)
}
Person.prototype.getAge = function () {
console.log(30)
}
var getAge = function () {
console.log(40)
}
function getAge() {
console.log(50)
}
/*
变量提升 1. function Person {getAge = function () { console.log(10) } return this }
2. var getAge; function getAge() { console.log(50) }
执行 3. Person.getAge = function () { console.log(20) } 给Person添加静态属性 getAge = function () { console.log(20) }
4. Person.prototype.getAge = function () { console.log(30) } 给Person原型上添加属性 getAge = function () { console.log(30) }
5. 为全局的getAge赋值 getAge = function () { console.log(40) }
*/

Person.getAge() // 20 ===> function () { console.log(20) }() 输出 20
getAge() // 40 ===> 执行全局 getAge = function () { console.log(40) } 输出 40
Person().getAge() // 10 ===> 执行Person() 将全局 getAge修改为 function () { console.log(10) } 并返回this--> window 调用window.getAge 即全局getAge 输出10
getAge() // 10 ===> 执行全局 getAge 输出10
new Person.getAge() // 20 ==> new 运算先找()才执行 执行 new Person.getAge() -->Person.getAge --> new function () { console.log(20) } 输出20
new Person().getAge() // 30 ===> new 先找()执行 var p = new Person() 创建实例对象p 调用p的方法 为找到则去构造函数的原型上查找 Person.prototype.getAge = function () { console.log(30) } 执行 function () { console.log(30) } 输出30 实例对象的 p.__proto__=== Person.prototype
-------------本文结束感谢您的阅读-------------
0%