入职15天,面经-读代码总结(二)

219 阅读11分钟

(一)、被裁之后自己的总结的技术点面经(八股文) 难度指数 2~3颗星

(二)、作者面试中面到的笔试题 难度指数 2~3颗星

(三)、JS异步原理与进阶-Event Loop事件循环 难度指数 1~3颗星

(四)、从JS基础知识到Web API(ajax-跨域-存储) 难度指数 1~3颗星

(五)、关于http, 强制缓存-协商缓存看这个 难度指数 2~4颗星

(六)、手写深度比较,模拟loadsh isEqual 难度指数 1~3颗星

(七)、Typescript基础篇 难度指数 1颗星

(八)、TS核心语法+各种实战应用(上) 难度指数 3颗星

(九)、TS核心语法+各种实战应用(下) 更新中.....

1.读代码 val 输出什么?

var value = 1;
function foo() {
    console.log(value);
}
 
function bar() {
    var value = 2;
    foo();
}
bar();   
当调用bar()函数时,它会调用foo()函数。最后输出的结果是:  1   考点: 变量提升

1.因为在bar()函数内部,虽然有一个局部变量value被声明并赋值为2

2.但在foo()函数中,value并没有在函数内部声明
	因此它会在作用域链中向上查找,找到最近的一层包含value的变量声明,
  也就是在全局作用域中声明的value,其值为1

2.读代码 newArray输出什么?

var array = [];
for (var i = 0; i < 3; i++) {
    array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); 
1.首先 创建了一个array空数组[]
2.使用for循环和var关键字声明了一个变量i
3.循环3次push到array中
4.然后使用map对arr中的每个函数进行调用&&存储到newArray中
5.由于使用了箭头函数,他们会捕获所在上下文的变量i,在这个上下文中, i 的值是在循环结束时的值也就是3
6.所以newArray数组中会有33 [3, 3, 3]  
  
考点:
    考察的是JavaScript中的闭包和变量作用域的概念。

但是我们期望输出的应该是[0, 1, 2]
可以使用let关键字来声明i,因为let具有块级作用域,每次迭代会创建一个新的i变量
var array = [];
for (let i = 0; i < 3; i++) {
    array.push(() => i);
}
var newArray = array.map(el => el());
console.log(newArray); // 输出: [0, 1, 2] 

3.读代码 num 输出什么?

var num = 10;
function foo() {
    console.log(num);
    var num = 20;
}
foo();
1. 首先声明了一个全局变量 num = 10
2. foo函数内部声明了一个 num = 20
3. 在函数内部打印num值
4. 因为在js中变量声明会被提升到其所在作用域的顶部,而变量赋值保留在原来的位置
function foo() {
    var num; // 变量声明被提升到函数作用域的顶部
    console.log(num); // 此时 num 为 undefined
    num = 20; // 然后才进行赋值操作
} // 最终输出undefined  考察变量声明和变量作用域的概念

4.读代码 会报错吗?

// react组件内
import React, {useRef} from 'react';
function App() {
    function test() {
        const val = useRef('1');
        return val;
    }
    return (
        <div className="App">
            {test().current}
        </div>
    );
}
// 上述代码会报错吗?
React组件中,useRef不能在组件的渲染过程中声明和使用。
因为useRef是用来在组件渲染之间保持数据的稳定引用的
如果在每次渲染时重新声明它,那么之前的引用就会丢失,这会导致错误。

报错信息 : 
	React Hook“useRef”在函数“test”中被调用
  该函数既不是React函数组件,也不是自定义的React Hoo克函数。
  React组件名称必须以大写字母开头。React Hook名称必须以单词“use”开头

正确使用: 在组件外部声明useRef,然后在组件内部使用。
import React, { useRef } from 'react';
function App() {
  const valRef = useRef('1'); // 在组件外部声明 useRef
  function test() {
    return valRef;
  }
  return (
    <div className="App">
      {test().current}
    </div>
  );
}
export default App;

考点: hook的使用规则

5.读代码 连续赋值

const obj1 = {a: '1111'}
const obj2 = obj1;
obj1.a = '2222'
console.log(obj2.a)
1.这里的考点是关于JavaScript中对象引用和赋值的概念。

2.首先,我们创建了一个名为obj1的对象,其中有一个属性a,其值为'1111'3.然后,我们将obj1赋值给另一个变量obj2。在这里,不是将obj1的值拷贝给obj2
     而是实际上将obj2指向了obj1所在的内存地址,也就是说它们指向同一个对象。

4.接着修改obj1的属性a的值为'2222'5.由于obj1和obj2指向同一个对象,所以对obj1的修改也会影响到obj2。
     因此,console.log(obj2.a)输出的结果是'2222'。

这种情况被称为浅拷贝,因为只拷贝了对象的引用而不是对象本身。
	如果想要避免这种引用关系,可以使用深拷贝技术来复制对象的所有属性,从而生成一个新的对象。

6.读promise代码

function p1() {
    return new Promise((resolve, reject) => {    
        reject(111);
    });
}
 
p1().catch((res) => {
    return res
}).then((res) => {
    console.log(res)
}).catch((res) => {
    console.log('catch ' + res)
})
1.考点是关于Promise的链式调用和错误处理。

2.首先,我们调用 p1() 方法返回一个Promise对象,该Promise对象立即进入rejected状态并传递了一个拒绝值 1113.然后,我们使用 catch() 方法来处理拒绝状态。在这里,catch()方法返回一个新的Promise,其状态为resolved,并且返回值为 111,这个值会传递到后面的 then() 方法。

4.接着,我们使用 then() 方法来处理 catch() 返回的值,输出 111。

注意:在Promise链式调用中,如果前面的Promise状态为resolved,
      那么会继续执行后面的 then() 方法
      如果前面的Promise状态为rejected,会执行后面的 catch() 方法。
      在这个例子中,由于 catch() 方法返回了一个resolved状态的Promise
      所以后续执行了 then() 方法。

最终的输出结果是 111

7.读代码 sally能不能访问到sayFuc?

Function.prototype.sayFuc = function () {
    console.log('fuc');
}

function Person (name) {
    this.name = name;
}

Person.prototype.sayName = function () {
    console.log(this.name);
}

var sally = new Person('sally');
// sally能不能访问到sayFuc?
1.sally 对象不能直接访问到 sayFuc 方法
   因为 sayFuc 方法是定义在 Function.prototype 上的
   而不是 Person.prototype 上。

2.JavaScript 中,当你创建一个对象实例时(比如 var sally = new Person('sally'))
  这个实例会继承其构造函数的原型对象中的属性和方法。
  因此,sally 对象可以访问 Person.prototype 上定义的方法  如 sayName 方法 
  因为 sayName 是通过原型继承从 Person.prototype 继承而来的。

3.但是,sayFuc 方法是定义在 Function.prototype 上
  而 Function.prototype 并不是 Person 构造函数的原型。因此,sally 对象无法直接访问到 sayFuc 方法。

4.只有当 Person 构造函数本身或其原型链中的其他原型链上存在 sayFuc 方法时
  sally 对象才能访问到 sayFuc 方法。
  比如,如果你将 sayFuc 方法定义在 Person.prototype 或者 Function.prototype 的原型链上
  那么 sally 对象就能够通过原型链继承访问到该方法。
  但根据你提供的代码,sayFuc 方法是直接定义在 Function.prototype 上的
  因此 sally 对象无法直接访问到它。

8.arr最终输出什么?

const arr = [1,2,3];
arr.push(8,9);
arr.concat(5,[6]);
arr.shift(7);
arr.splice(1,1,2);
arr.slice(1,3);
console.log(arr);

解析之后
const arr = [1,2,3];
arr.push(8,9); // [1,2,3,8,9]
arr.concat(5,[6]); // concat() 方法用于连接两个或多个数组
arr.shift(7);  // shift()没有参数 就算传了也删除首位  // [2, 3, 8, 9]
arr.splice(1,1,2); // 参数1: 开始位置 参数2: 结束位置 参数3: 替换为2 // [2, 2, 8, 9]
arr.slice(1,3); // 从索引1截取到索引3 输出[2, 2, 8, 9]  因为slice是一个纯函数 不会改变原数组 
const a = arr.slice(1, 3)  // [2, 8] 这样赋值就是正确的  直接改变是不会改变原数组的
console.log(arr, '======', a); 

9.name值是多少?为什么?

var name = '1';
new Promise(function (resolve, reject) {
    resolve();
    reject();
}).then(function () {
    name = '2';
}).catch(function () {
    name = '3';
});
name = '4';
console.log(name, '===='); // 等于 4 ? 为什么?

// 解析  初始值=1
var name = '1';  // 初始
new Promise(function (resolve, reject) {  // 创建一个Promise对象
    resolve(); // 俩个回调参数会立即执行 不会对Promise产生任何影响
    reject();
}).then(function () { // 状态resolve 会执行  
    name = '2';  // 被赋值name=2
}).catch(function () { //  resolve() 已经执行过reject() 所以并不会执行
    name = '3';  
});
name = '4';   // 链式调用之后,执行到 name = '4' 被重新赋值

10.普通函数输出什么?

var obj = {
    foo: function () { 
        console.log(this.bar) 
    },
    bar: 1
};
  
var foo = obj.foo;
var bar = 2;
  
obj.foo(); // 1   // 通过obj调用foo() this指向obj对象本身 
foo(); // undefined 这里直接调用的foo函数 不是通过对象调用 this指向全局window 全局中未定义bar

11.是箭头函数输出什么?

const animate = {
    // 忽略了它是箭头函数
    say: () => {   // 箭头函数中 this 值 undefined  回去外层寻找 window
        console.log(this);
    }
};
const cat = {name: 'lily'};
// bind() 方法会创建一个新函数,并将其内部的 this 绑定到 cat 对象
// 然而,由于箭头函数的特性,它会忽略 bind() 中的绑定,并继续继承外部作用域的 this 值。
// 在浏览器控制台中输出 this,你会看到一个空对象,
// 这是因为浏览器环境下的 window 对象通常以空对象的形式显示。
animate.say.bind(cat)(); // {}

12.读代码,输出什么

function Foo () {
    Foo.a = function () {
        console.log(1);
    }
    this.a = function () {
        console.log(2);
    }
}

Foo.prototype.a = function () {
    console.log(3);
}

Foo.a = function () {
    console.log(4);
}

Foo.a() // 4
let obj = new Foo();
obj.a() // 2  先从它自身找 找不到然后去原型上去找  3
Foo.a() // 1 
不要一行一行读代码

13.Promise.then执行顺序问题

Promise.resolve().then(() => {
    console.log(0);
    return Promise.resolve(4)
}).then((res) => {
    console.log(res);
})

Promise.resolve().then(() => {
    console.log(1);
}).then(() => {
    console.log(2);
}).then(() => {
    console.log(3);
}).then(() => {
    console.log(5);
}).then(() => {
    console.log(6);
})

// 输出0, 1, 2, 3, 4, 5, 6   为什么?
解释:
	1.如果有多个fulfilled promise 实例,同时执行then链式调用
	2.then会交替执行
	3.这是编译器的优化,防止一个promise占据太久时间

then 中返回 promise实例
	1.相当于多出一个promise实例
	2.也会遵循"交替执行"
	3.(但和直接声明一个promise实例结果有差异)

慢两拍
	1.then 中返回promise实例, 会出现慢两拍的效果
	2. 第一拍, promise需要由pending 变为 fulfilled
	3.第二拍, then函数挂载到 MicroTaskQueue 队列中  (需要了解Event loop  事件循环机制)

14.添加一行代码输出Hello, World

const object = {
  message: 'Hello, World!'
};

function logMessage() {
  console.log(this.message); // => "Hello, World!"
}

logMessage.apply(object)  // apply   立即执行
logMessage.call(object)   // call 立即执行 
// bind  需要返回一个函数然后调用执行
const boundLogMessage = logMessage.bind(object); // 使用 bind 方法绑定执行上下文
boundLogMessage(); // 输出 "Hello, World!"

15.读代码

var num1 = 1
var num2 = 2
function Person () {
  var num1 = 10
  var num2 = 20
  console.log(this.num1 + this.num2);
}

new Person() // NAN
Person()  // NAN

1.new Person():当通过 new 关键字调用 Person 函数时,会创建一个新的对象
  并将这个新对象作为 Person 函数的执行上下文(this)传递给函数。在这种情况下
  this.num1this.num2 分别指向新创建的对象的属性,由于没有给新对象添加 num1 和 num2 属性
  所以它们的值为 undefined。因此,输出结果为 NaNNot a Number)。

2.Person():当直接调用 Person 函数时,没有指定执行上下文,因此在非严格模式下
  this 默认指向全局对象(在浏览器环境中是 window 对象)
  在全局对象中,并没有 num1 和 num2 这两个属性
  所以 this.num1this.num2 均为 undefined。因此,输出结果仍然是 NaN

16.读代码 arr输出什么?

const arr = [1,2];
arr.pop(2);
arr.slice(1,3);
arr.concat(5,[6]);
arr.push(8,9);
arr.splice(1,1,2);
console.log(arr);

// 打印什么?
const arr1 = [1,2];
arr1.pop(2); // [1] 返回删除的哪一项
arr1.slice(1,3);  // [1]  纯函数  不会改变原数组
arr1.concat(5,[6]); // [1]  concat() 方法用于连接两个或多个数组
arr1.push(8,9); // [1, 8, 9]
// arr1.splice(1,1,2); // [1, 8, 9]
console.log(arr1);

17.读代码 promise

const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result)
.catch(e => e);

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(result => result)
.catch(e => e);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));

// 打印什么? [ 'hello', Error: 报错了 ]

1.p1 Promise:这个 Promise 在执行时会立即调用 resolve('hello'),将状态改为已解决(fulfilled)
  并且结果值为 'hello'2.p2 Promise:这个 Promise 在执行时会抛出一个错误 new Error('报错了')
  将状态改为已拒绝(rejected),并且错误原因为这个 Error 对象。

3.Promise.all([p1, p2]):Promise.all() 方法接收一个包含多个 Promise 的数组
  并返回一个新的 Promise,当所有传入的 Promise 都成功解决时,新的 Promise 也会成功解决
  并返回一个包含所有 Promise 结果的数组。如果其中任何一个 Promise 被拒绝
  新的 Promise 将立即被拒绝,并返回拒绝原因。

4. 根据以上分析,Promise.all([p1, p2]) 中的 p1 被解决,p2 被拒绝。
   因此,新的 Promise 也会立即被拒绝,并返回拒绝原因,即 Error 对象。
   所以,最终的输出结果将是一个包含 Error 对象的数组,即 [ 'hello', Error: 报错了 ]。

请注意,即使 p1 在执行时被解决了,但 Promise.all() 会等待所有传入的 Promise 都有结果后才返回
因此,即使 p1 先被解决了,但在 p2 抛出错误之前,Promise.all() 并不会立即返回成功状态。