关于前端:2023我的前端面试小结

5次阅读

共计 7379 个字符,预计需要花费 19 分钟才能阅读完成。

TCP/IP 五层协定

TCP/IP五层协定和 OSI 的七层协定对应关系如下:

  • 应用层 (application layer):间接为利用过程提供服务。应用层协定定义的是利用过程间通信和交互的规定,不同的利用有着不同的应用层协定,如 HTTP 协定(万维网服务)、FTP 协定(文件传输)、SMTP 协定(电子邮件)、DNS(域名查问)等。
  • 传输层 (transport layer):有时也译为运输层,它负责为两台主机中的过程提供通信服务。该层次要有以下两种协定:

    • 传输控制协议 (Transmission Control Protocol,TCP):提供面向连贯的、牢靠的数据传输服务,数据传输的根本单位是报文段(segment);
    • 用户数据报协定 (User Datagram Protocol,UDP):提供无连贯的、尽最大致力的数据传输服务,但不保障数据传输的可靠性,数据传输的根本单位是用户数据报。
  • 网络层 (internet layer):有时也译为网际层,它负责为两台主机提供通信服务,并通过抉择适合的路由将数据传递到指标主机。
  • 数据链路层 (data link layer):负责将网络层交下来的 IP 数据报封装成帧,并在链路的两个相邻节点间传送帧,每一帧都蕴含数据和必要的管制信息(如同步信息、地址信息、差错控制等)。
  • 物理层 (physical Layer):确保数据能够在各种物理媒介上进行传输,为数据的传输提供牢靠的环境。

从上图中能够看出,TCP/IP模型比 OSI 模型更加简洁,它把 应用层 / 表示层 / 会话层 全副整合为了 应用层

在每一层都工作着不同的设施,比方咱们罕用的交换机就工作在数据链路层的,个别的路由器是工作在网络层的。在每一层实现的协定也各不同,即每一层的服务也不同,下图列出了每层次要的传输协定:

同样,TCP/IP五层协定的通信形式也是对等通信:

说一下 JSON.stringify 有什么毛病?

1. 如果 obj 外面有工夫对象,则 JSON.stringify 后再 JSON.parse 的后果,工夫将只是字符串的模式,而不是对象的模式
2. 如果 obj 里有 RegExp(正则表达式的缩写)、Error 对象,则序列化的后果将只失去空对象;3、如果 obj 里有函数,undefined,则序列化的后果会把函数或 undefined 失落;4、如果 obj 里有 NaN、Infinity 和 -Infinity,则序列化的后果会变成 null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,例如 如果 obj 中的对象是有构造函数生成的,则应用 JSON.parse(JSON.stringify(obj))深拷贝后,会抛弃对象的 constructor;6、如果对象中存在循环援用的状况也无奈正确实现深拷贝;

为什么 0.1 + 0.2 != 0.3,请详述理由

因为 JS 采纳 IEEE 754 双精度版本(64 位),并且只有采纳 IEEE 754 的语言都有该问题。

咱们都晓得计算机示意十进制是采纳二进制示意的,所以 0.1 在二进制示意为

// (0011) 示意循环
0.1 = 2^-4 * 1.10011(0011)

那么如何失去这个二进制的呢,咱们能够来演算下

小数算二进制和整数不同。乘法计算时,只计算小数位,整数位用作每一位的二进制,并且失去的第一位为最高位。所以咱们得出 0.1 = 2^-4 * 1.10011(0011),那么 0.2 的演算也根本如上所示,只须要去掉第一步乘法,所以得出 0.2 = 2^-3 * 1.10011(0011)

回来持续说 IEEE 754 双精度。六十四位中符号位占一位,整数位占十一位,其余五十二位都为小数位。因为 0.10.2 都是有限循环的二进制了,所以在小数位开端处须要判断是否进位(就和十进制的四舍五入一样)。

所以 2^-4 * 1.10011...001 进位后就变成了 2^-4 * 1.10011(0011 * 12 次)010。那么把这两个二进制加起来会得出 2^-2 * 1.0011(0011 * 11 次)0100 , 这个值算成十进制就是 0.30000000000000004

上面说一下原生解决办法,如下代码所示

parseFloat((0.1 + 0.2).toFixed(10))

原型

JavaScript 中的对象都有一个非凡的 prototype 内置属性,其实就是对其余对象的援用
简直所有的对象在创立时 prototype 属性都会被赋予一个非空的值,咱们能够把这个属性当作一个备用的仓库
当试图援用对象的属性时会登程 get 操作,第一步时查看对象自身是否有这个属性,如果有就应用它,没有就去原型中查找。一层层向上直到 Object.prototype 顶层

基于原型扩大形容一下原型链,什么是原型链,原型的继承,ES5 和 ES6 继承与不同点。

Promise.all

形容:所有 promise 的状态都变成 fulfilled,就会返回一个状态为 fulfilled 的数组(所有promisevalue)。只有有一个失败,就返回第一个状态为 rejectedpromise 实例的 reason

实现

Promise.all = function(promises) {return new Promise((resolve, reject) => {if(Array.isArray(promises)) {if(promises.length === 0) return resolve(promises);
            let result = [];
            let count = 0;
            promises.forEach((item, index) => {Promise.resolve(item).then(
                    value => {
                        count++;
                        result[index] = value;
                        if(count === promises.length) resolve(result);
                    }, 
                    reason => reject(reason)
                );
            })
        }
        else return reject(new TypeError("Argument is not iterable"));
    });
}

说一下 data 为什么是一个函数而不是一个对象?

JavaScript 中的对象是援用类型的数据,当多个实例援用同一个对象时,只有一个实例对这个对象进行操作,其余实例中的数据也会发生变化。而在 Vue 中,咱们更多的是想要复用组件,那就须要每个组件都有本人的数据,这样组件之间才不会互相烦扰。所以组件的数据不能写成对象的模式,而是要写成函数的模式。数据以函数返回值的模式定义,这样当咱们每次复用组件的时候,就会返回一个新的 data,也就是说每个组件都有本人的公有数据空间,它们各自保护本人的数据,不会烦扰其余组件的失常运行。

参考 前端进阶面试题具体解答

实现模板字符串解析

形容 :实现函数使得将 template 字符串中的{{}} 内的变量替换。

外围:应用字符串替换办法 str.replace(regexp|substr, newSubStr|function),应用正则匹配代换字符串。

实现

function render(template, data) {// 模板字符串正则 /\{\{(\w+)\}\}/, 加 g 为全局匹配模式, 每次匹配都会调用前面的函数
    let computed = template.replace(/\{\{(\w+)\}\}/g, function(match, key) {
        // match: 匹配的子串;  key:括号匹配的字符串
        return data[key];
    });
    return computed;
}

// 测试
let template = "我是{{name}},年龄{{age}},性别{{sex}}";
let data = {
  name: "张三",
  age: 18
}
console.log(render(template, data)); // 我是张三,年龄 18,性别 undefined

Set,Map 解构

ES6 提供了新的数据结构 Set。它相似于数组,然而成员的值都是惟一的,没有反复的值。Set 自身是一个构造函数,用来生成 Set 数据结构。ES6 提供了 Map 数据结构。它相似于对象,也是键值对的汇合,然而“键”的范畴不限于字符串,各种类型的值(包含对象)都能够当作键。

localStorage sessionStorage cookies 有什么区别?

localStorage: 以键值对的形式存储 贮存工夫没有限度 永恒失效 除非本人删除记录
sessionStorage:当页面敞开后被清理与其余相比不能同源窗口共享 是会话级别的存储形式
cookies 数据不能超过 4k 同时因为每次 http 申请都会携带 cookie 所有 cookie 只适宜保留很小的数据 如会话标识

<script src=’xxx’’xxx’/> 内部 js 文件先加载还是 onload 先执行,为什么?

onload 是所以加载实现之后执行的

Promise.reject

Promise.reject = function(reason) {return new Promise((resolve, reject) => reject(reason));
}

代码输入问题

function fun(n, o) {console.log(o)
  return {fun: function(m){return fun(m, n);
    }
  };
}
var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1);  c.fun(2);  c.fun(3);

输入后果:

undefined  0  0  0
undefined  0  1  2
undefined  0  1  1

这是一道对于闭包的题目,对于 fun 办法,调用之后返回的是一个对象。咱们晓得,当调用函数的时候传入的实参比函数申明时指定的形参个数要少,剩下的形参都将设置为 undefined 值。所以 console.log(o); 会输入 undefined。而 a 就是是 fun(0)返回的那个对象。也就是说,函数 fun 中参数 n 的值是 0,而返回的那个对象中,须要一个参数 n,而这个对象的作用域中没有 n,它就持续沿着作用域向上一级的作用域中寻找 n,最初在函数 fun 中找到了 n,n 的值是 0。理解了这一点,其余运算就很简略了,以此类推。

说一下 web worker

在 HTML 页面中,如果在执行脚本时,页面的状态是不可相应的,直到脚本执行实现后,页面才变成可相应。web worker 是运行在后盾的 js,独立于其余脚本,不会影响页面的性能。并且通过 postMessage 将后果回传到主线程。这样在进行简单操作的时候,就不会阻塞主线程了。

如何创立 web worker:

  1. 检测浏览器对于 web worker 的支持性
  2. 创立 web worker 文件(js,回传函数等)
  3. 创立 web worker 对象

说一下常见的 git 操作

git branch 查看本地所有分支
git status 查看以后状态 
git commit 提交 
git branch -a 查看所有的分支
git branch -r 查看近程所有分支
git commit -am "nit" 提交并且加正文 
git remote add origin git@192.168.1.119:ndshow
git push origin master 将文件给推到服务器上 
git remote show origin 显示近程库 origin 里的资源 
git push origin master:develop
git push origin master:hb-dev 将本地库与服务器上的库进行关联 
git checkout --track origin/dev 切换到近程 dev 分支
git branch -D master develop 删除本地库 develop
git checkout -b dev 建设一个新的本地分支 dev
git merge origin/dev 将分支 dev 与以后分支进行合并
git checkout dev 切换到本地 dev 分支
git remote show 查看近程库
git add .
git rm 文件名(包含门路) 从 git 中删除指定文件
git clone git://github.com/schacon/grit.git 从服务器上将代码给拉下来
git config --list 看所有用户
git ls-files 看曾经被提交的
git rm [file name] 删除一个文件
git commit -a 提交以后 repos 的所有的扭转
git add [file name] 增加一个文件到 git index
git commit -v 当你用-v 参数的时候能够看 commit 的差别
git commit -m "This is the message describing the commit" 增加 commit 信息
git commit -a - a 是代表 add,把所有的 change 加到 git index 里而后再 commit
git commit -a -v 个别提交命令
git log 看你 commit 的日志
git diff 查看尚未暂存的更新
git rm a.a 移除文件(从暂存区和工作区中删除)
git rm --cached a.a 移除文件(只从暂存区中删除)
git commit -m "remove" 移除文件(从 Git 中删除)
git rm -f a.a 强行移除批改后文件(从暂存区和工作区中删除)
git diff --cached 或 $ git diff --staged 查看尚未提交的更新
git stash push 将文件给 push 到一个长期空间中
git stash pop 将文件从长期空间 pop 下来

事件流传机制(事件流)

冒泡和捕捉

Promise.resolve

Promise.resolve = function(value) {
    // 1. 如果 value 参数是一个 Promise 对象,则一成不变返回该对象
    if(value instanceof Promise) return value;
    // 2. 如果 value 参数是一个具备 then 办法的对象,则将这个对象转为 Promise 对象,并立刻执行它的 then 办法
    if(typeof value === "object" && 'then' in value) {return new Promise((resolve, reject) => {value.then(resolve, reject);
        });
    }
    // 3. 否则返回一个新的 Promise 对象,状态为 fulfilled
    return new Promise(resolve => resolve(value));
}

代码输入后果

var A = {n: 4399};
var B =  function(){this.n = 9999};
var C =  function(){var n = 8888};
B.prototype = A;
C.prototype = A;
var b = new B();
var c = new C();
A.n++
console.log(b.n);
console.log(c.n);

输入后果:9999 4400

解析:

  1. console.log(b.n),在查找 b.n 是首先查找 b 对象本身有没有 n 属性,如果没有会去原型(prototype)上查找,当执行 var b = new B()时,函数外部 this.n=9999(此时 this 指向 b) 返回 b 对象,b 对象有本身的 n 属性,所以返回 9999。
  2. console.log(c.n),同理,当执行 var c = new C()时,c 对象没有本身的 n 属性,向上查找,找到原型(prototype)上的 n 属性,因为 A.n++(此时对象 A 中的 n 为 4400),所以返回 4400。

代码输入问题

window.number = 2;
var obj = {
 number: 3,
 db1: (function(){console.log(this);
   this.number *= 4;
   return function(){console.log(this);
     this.number *= 5;
   }
 })()}
var db1 = obj.db1;
db1();
obj.db1();
console.log(obj.number);     // 15
console.log(window.number);  // 40

这道题目看清起来有点乱,然而实际上是考查 this 指向的:

  1. 执行 db1()时,this 指向全局作用域,所以 window.number 4 = 8,而后执行匿名函数,所以 window.number 5 = 40;
  2. 执行 obj.db1(); 时,this 指向 obj 对象,执行匿名函数,所以 obj.numer * 5 = 15。

实现 JSONP 跨域

JSONP 外围原理script 标签不受同源策略束缚,所以能够用来进行跨域申请,长处是兼容性好,然而只能用于 GET 申请;

实现

const jsonp = (url, params, callbackName) => {const generateUrl = () => {
        let dataSrc = "";
        for(let key in params) {if(params.hasOwnProperty(key)) {dataSrc += `${key}=${params[key]}&`
            }
        }
        dataSrc += `callback=${callbackName}`;
        return `${url}?${dataSrc}`;
    }
    return new Promise((resolve, reject) => {const scriptEle = document.createElement('script');
        scriptEle.src = generateUrl();
        document.body.appendChild(scriptEle);
        window[callbackName] = data => {resolve(data);
            document.removeChild(scriptEle);
        }
    });
}

src 和 href 的区别

src 和 href 都是 用来援用内部的资源,它们的区别如下:

  • src: 示意对资源的援用,它指向的内容会嵌入到以后标签所在的地位。src 会将其指向的资源下载并应⽤到⽂档内,如申请 js 脚本。当浏览器解析到该元素时,会暂停其余资源的下载和解决,直到将该资源加载、编译、执⾏结束,所以⼀般 js 脚本会放在页面底部。
  • href: 示意超文本援用,它指向一些网络资源,建设和以后元素或本文档的链接关系。当浏览器辨认到它他指向的⽂件时,就会并⾏下载资源,不会停⽌对以后⽂档的解决。罕用在 a、link 等标签上。
正文完
 0