liangbus / blogging

Blog to go
10 stars 0 forks source link

代码相关面试题合集 #40

Open liangbus opened 4 years ago

liangbus commented 4 years ago

1. 创建一个如图类似的开关切换按钮,hover 时触发,并符合以下条件

  1. 仅使用一个元素
  2. 开关切换时,带有过渡动画
  3. 宽度不定,resize 时能够自适应宽度,高度自定

image image

这道题看上去,并不是很难,但是实际上考察样式方面的知识面特别广,实用性也很高,下面看看我的答案 .html

<div class="switch-btn"></div>

.css

.switch-btn{
  width: 100%;
  height: 100px;
  position: relative;
  background-color: grey;
}
.switch-btn::after{
  content: ' ';
  position: absolute;
  top: 5px;
  left: 5px;
  width: 50%;
  height: 90px;
  transition: all .3s linear;
  transform: translateX(0px);
  background-color: #fff;
}
.switch-btn:hover {
  background-color: green;
}
.switch-btn:hover::after{
  content: ' ';
  position: absolute;
  top: 5px;
  left: 5px;
  width: 50%;
  height: 90px;
  transition: all .3s linear;
  transform: translateX(calc(100% - 10px));
  background-color: #fff;
}

codepen demo

liangbus commented 4 years ago

2. 闭包相关,求 console.log() 输出


function fun(n, k) {
    console.log(k)
        return {
            fun:function(m){
                return fun(m,n);
            }
    };
}
var a = fun(0); // k -> undefined a : {fun : function(){}}
a.fun(1);
a.fun(2); 
a.fun(3);

var b = fun(0).fun(1).fun(2).fun(3);

分析

首先是声明一个 fun 函数,该函数返回一个对象,对象里面有一个属性,也叫 fun,其值是一个函数,其函数返回的是外层 fun 函数执行的结果(有点绕,当时就是给绕晕了 >_<)

var a = fun(0) 执行了一次 fun 函数,传了一个 0 (对应形参为 m)作为参数, 此时 console.log(k) 打印出来当然是 undefined 这里应该都没什么大问题,紧接着就是返回一个对象赋值给 a

a = {
    fun:function(m){
        return fun(m,n);
    }
}

a.fun(1); 传入一个参数 m,执行fun(m,n) ,m 为刚刚传入的 1,n 为之前第一次执行 fun 函数时传入的 0 (因为 n 被返回出去的函数作为参数引用,因此会常驻内存当中),因此这里再次打印值为 0,后续两个表达式同理,因为 a 并没有改变,n 值也没有改变,故均输出 0

a.fun(1); // 0
a.fun(2);  // 0
a.fun(3); // 0

然后再来看下最后一个链接调用的表达式

首次执行 fun(0) 打印 undefined 这个应该没有太大疑问

紧接着其链式执行 .fun(1) ,这时先来看下 fun(0) 的返回结果

{
    fun:function(m){
        return fun(m,n);
    }
}

这时跟上面执行 a.fun(0) 时是一样的,读取的 n 依然是之前 fun(0) 传入的 0,因此 k 打印为 0,但是这里返回给下一步链式调用的对象已经变了,是一个全新的对象,因此下一次链式调用时,返回的 fun(m, n) n 就是上一次传入的 m 值, 这就与上面 a.fun(0), a.fun(1) 不一样,这里 a 的对象一直是没有变的,因此此处输出值为

var b = fun(0).fun(1).fun(2).fun(3);
// undefined
// 0
// 1
// 2
liangbus commented 4 years ago

3. 闭包,求以下 console.log() 输出

var result = []
var a = 10
function foo(a){
    var i = 0;
    for(; i < 3; i++){
        result[i] = function() {
            a += i * a
            console.log(a)
        }
    }
}
foo(1)
result[0]()
result[1]()
result[2]()

这也算是一道经典的闭包相关的题目了。

liangbus commented 4 years ago

4. 求以下输出

var a = 10
function f(a) {
    console.log(a)
    var a = 2
    function a() {}
    console.log(a)
}
f(a)

主要考察变量提升,函数声明相关知识点

答案:

// function a() {} // 2

函数提升优先级比变量提升要高,且不会被变量声明覆盖,但是会被变量赋值覆盖。(我猜测应该是声明还没有真正改变其变量的指向)


var length = 10
function foo() {
    console.log(this.length)
}
var obj = {
    length: 5,
    fn: function(f) {
        f()
        arguments[0]()
    }
}
obj.fn(foo, 1)

答案

// 10 // 2

调用 obj 对象下的一个函数属性 fn,fn 里面调用了传入的参数 f,也就是 foo,执行 f() 时,前面并没有调用的对象,因此 this 的指向仍然是全局,所以这里打印的是全局的 length 值 而 arguments[0]() 同样是执行传入的对象,但是这里的调用方法已经改变了,是通过一个类数组对象来调用的,因此 this 正是指向该对象,而 this.length 即为该方法参数的长度

举例:

var length = 10
function foo() {
    console.log(this.length)
}
var arr = [foo, 4,5,6]
arr[0]() // 4

var f = true
if(f === true){
    var a = 2
}
function foo(){
    var b = 12
    c = 16
}
foo()

console.log(a)
console.log(b)
console.log(c)

答案:

// 2 // Error // 16 ( 理论上该条语句不执行)

liangbus commented 4 years ago

5. 如何用css画一个滚动球体?

这是一个来回滚动的球体

<!-- html -->
<div class="ball">
  @
</div>
/** css **/
.ball{
  width: 200px;
  height: 200px;
  border-radius: 50%;
  color: #fff;
  font-size: 32px;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: lightgrey;
  animation: roll 3s linear infinite;
}

@keyframes roll {
  0% {
    transform: translate(0, 0) rotate(0deg)
  }
  50% {
    transform: translate(500px, 0) rotate(360deg)
  }
  100% {
    transform: translate(0, 0) rotate(0deg)
  }
}
liangbus commented 4 years ago

6. 针对以下代码,写一段 JS,使其点击输出相应的序号,比如点击第三个 alert(3)

<ul>
  <li>haha</li>
  <li>Yoyo</li>
  <li>Wowowowo</li>
  <li>Win~!</li>
</ul>

注意不能额外增加 dom 属性,比如 data-index

答案:

var list = document.getElementsByTagName('li')
  Array.from(list).forEach((item, i) => {
    item.addEventListener('click', function(){
      alert(i)
    })
  })

通过 getElementsByTagName 获得是类数组对象,因此需要一层转换,然后再对其遍历,对每一个 li 进行监听

liangbus commented 4 years ago

7. 原型链相关

7.1 原型链的终点是 ?

Object.prototype.__proto__ === null // true

7.2 Object.proto 指向?

Object.__proto__ === Function.prototype // true

image

7.3 如何创建一个不可继承的对象

开始回答的是箭头函数,然后面试官说不是他想要的答案,后来查了一下,应该是通过

Object.create(null)

来创建吧。

liangbus commented 4 years ago

8. 求以下输出

var foo = 10;
(function foo() {
    foo = 123;
    console.log(foo)
})()

答案

ƒ foo() {
    foo = 123;
    console.log(foo)
}

解释: 在非匿名 IIFE (Immediately Invoked Function Expression 立即调用的函数表达式) 中其绑定的变量相当于是绑定在 const 上,在 strict 模式下会报错,非 strict 模式下静默失败。

var foo = 10;
(function foo() {
    'use strict';
    foo = 123;
    console.log(foo)
})()

VM9289:4 Uncaught TypeError: Assignment to constant variable.

第 33 题:下面的代码打印什么内容,为什么?

liangbus commented 4 years ago

9. 问输出,为什么?

var a = {n: 1};
var b = a;
a.x = a = {n: 2};

console.log(a.x)    
console.log(b.x)

答案:

undefined {n: 2}

解释: 这里主要考察赋值语句的运算符优先级问题,.运算符的优先级比等号要高,当执行赋值语句时,等号左边有 . 运算符,因为会先创建对应的属性,此时 a 存的指针仍指向的是 {n: 1} 堆的地址,因此这里会首先变为 {n: 1, x: undefined},执行 = 右边语句,重置了 a 的指针,指向了新的 {n:2} 的堆,并返回其指针继续赋值给原本 a.x 指向的内容,但由于原来 b 是指向这个堆的地址的,所以 b.x 为 {n:2}

liangbus commented 4 years ago

10. 词法作用域和动态作用域

var value = 1;

function foo() {
    console.log(value);
}

function bar() {
    var value = 2;
    foo();
}

bar();

// console.log 结果是 ???

答案是 1

详见:JavaScript深入之词法作用域和动态作用域

liangbus commented 4 years ago

11. 如何交换两个数值类型变量的值,不使用额外空间

  1. 直接 ES6 方法,此方法不仅适用于数字类型

    [a, b] = [b, a]
  2. 数值类型,可以通过简单计算来处理,该方法仅适用于数值类型,因为是通过运算来实现

示例:

var a = 3, b = 5
a = a+b // 求和
b = a - b // 先重新赋值给 b 
a = a - b // 再重新赋值给 a

还可以利用异或运算的特性

  • 相同的两个数异或结果为0
  • 任何数与0异或结果还是其自身
  • 异或运算满足交换律和结合律
var a = 3, b = 5
a = a^b
b = a^b
a = a^b