其他分享
首页 > 其他分享> > 腾讯前端必会面试题合集

腾讯前端必会面试题合集

作者:互联网

什么是闭包

闭包是一种特殊的对象,它由两部分组成:执行上下文(代号 A),以及在该执行上下文中创建的函数 (代号 B),当 B 执行时,如果访问了 A 中变量对象的值,那么闭包就会产生,且在 Chrome 中使用这个执行上下文 A 的函数名代指闭包。

对 CSSSprites 的理解

CSSSprites(精灵图),将一个页面涉及到的所有图片都包含到一张大图中去,然后利用CSS的 background-image,background-repeat,background-position属性的组合进行背景定位。

优点:

缺点:

函数柯里化

什么叫函数柯里化?其实就是将使用多个参数的函数转换成一系列使用一个参数的函数的技术。还不懂?来举个例子。

function add(a, b, c) {
    return a + b + c
}
add(1, 2, 3)
let addCurry = curry(add)
addCurry(1)(2)(3)

现在就是要实现 curry 这个函数,使函数从一次调用传入多个参数变成多次调用每次传一个参数。

function curry(fn) {
    let judge = (...args) => {
        if (args.length == fn.length) return fn(...args)
        return (...arg) => judge(...args, ...arg)
    }
    return judge
}

Object.is 实现

题目描述:

Object.is不会转换被比较的两个值的类型,这点和===更为相似,他们之间也存在一些区别。
    1. NaN在===中是不相等的,而在Object.is中是相等的
    2. +0和-0在===中是相等的,而在Object.is中是不相等的

实现代码如下:

Object.is = function (x, y) {
  if (x === y) {
    // 当前情况下,只有一种情况是特殊的,即 +0 -0
    // 如果 x !== 0,则返回true
    // 如果 x === 0,则需要判断+0和-0,则可以直接使用 1/+0 === Infinity 和 1/-0 === -Infinity来进行判断
    return x !== 0 || 1 / x === 1 / y;
  }

  // x !== y 的情况下,只需要判断是否为NaN,如果x!==x,则说明x是NaN,同理y也一样
  // x和y同时为NaN时,返回true
  return x !== x && y !== y;
};

深/浅拷贝

首先判断数据类型是否为对象,如果是对象(数组|对象),则递归(深/浅拷贝),否则直接拷贝。

function isObject(obj) {
    return typeof obj === "object" && obj !== null;
}

这个函数只能判断 obj 是否是对象,无法判断其具体是数组还是对象。

哪些情况会导致内存泄漏

以下四种情况会造成内存的泄漏:

数组有哪些原生方法?

实现一个对象的 flatten 方法

题目描述:

const obj = {
 a: {
        b: 1,
        c: 2,
        d: {e: 5}
    },
 b: [1, 3, {a: 2, b: 3}],
 c: 3
}

flatten(obj) 结果返回如下
// {
//  'a.b': 1,
//  'a.c': 2,
//  'a.d.e': 5,
//  'b[0]': 1,
//  'b[1]': 3,
//  'b[2].a': 2,
//  'b[2].b': 3
//   c: 3
// }

实现代码如下:

function isObject(val) {
  return typeof val === "object" && val !== null;
}

function flatten(obj) {
  if (!isObject(obj)) {
    return;
  }
  let res = {};
  const dfs = (cur, prefix) => {
    if (isObject(cur)) {
      if (Array.isArray(cur)) {
        cur.forEach((item, index) => {
          dfs(item, `${prefix}[${index}]`);
        });
      } else {
        for (let k in cur) {
          dfs(cur[k], `${prefix}${prefix ? "." : ""}${k}`);
        }
      }
    } else {
      res[prefix] = cur;
    }
  };
  dfs(obj, "");

  return res;
}
flatten();

代码输出结果

function a(xx){
  this.x = xx;
  return this
};
var x = a(5);
var y = a(6);

console.log(x.x)  // undefined
console.log(y.x)  // 6

输出结果: undefined 6

解析:

  1. 最关键的就是var x = a(5),函数a是在全局作用域调用,所以函数内部的this指向window对象。所以 this.x = 5 就相当于:window.x = 5。之后 return this,也就是说 var x = a(5) 中的x变量的值是window,这里的x将函数内部的x的值覆盖了。然后执行console.log(x.x), 也就是console.log(window.x),而window对象中没有x属性,所以会输出undefined。
  2. 当指向y.x时,会给全局变量中的x赋值为6,所以会打印出6。

IE 兼容

说一下原型链和原型链的继承吧

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

Person.prototype.constructor = Person

** 标准答案更正确的解释**

什么是原型链?

当对象查找一个属性的时候,如果没有在自身找到,那么就会查找自身的原型,如果原型还没有找到,那么会继续查找原型的原型,直到找到 Object.prototype 的原型时,此时原型为 null,查找停止。
这种通过 通过原型链接的逐级向上的查找链被称为原型链

什么是原型继承?

一个对象可以使用另外一个对象的属性或者方法,就称之为继承。具体是通过将这个对象的原型设置为另外一个对象,这样根据原型链的规则,如果查找一个对象属性且在自身不存在时,就会查找另外一个对象,相当于一个对象可以使用另外一个对象的属性和方法了。

说一下常见的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下来


代码输出结果

function runAsync (x) {
  const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
function runReject (x) {
  const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
  return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
       .then(res => console.log(res))
       .catch(err => console.log(err))

输出结果如下:

// 1s后输出
1
3
// 2s后输出
2
Error: 2
// 4s后输出
4

可以看到。catch捕获到了第一个错误,在这道题目中最先的错误就是runReject(2)的结果。如果一组异步操作中有一个异常都不会进入.then()的第一个回调函数参数中。会被.then()的第二个回调函数捕获。

如何判断一个对象是不是空对象?

Object.keys(obj).length === 0

手写题:在线编程,getUrlParams(url,key); 就是很简单的获取url的某个参数的问题,但要考虑边界情况,多个返回值等等

代码输出结果

function foo() {
  console.log( this.a );
}

function doFoo() {
  foo();
}

var obj = {
  a: 1,
  doFoo: doFoo
};

var a = 2; 
obj.doFoo()

输出结果:2

在Javascript中,this指向函数执行时的当前对象。在执行foo的时候,执行环境就是doFoo函数,执行环境为全局。所以,foo中的this是指向window的,所以会打印出2。

对async/await 的理解

async/await其实是Generator 的语法糖,它能实现的效果都能用then链来实现,它是为优化then链而开发出来的。从字面上来看,async是“异步”的简写,await则为等待,所以很好理解async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。当然语法上强制规定await只能出现在asnyc函数中,先来看看async函数返回了什么:

async function testAsy(){
   return 'hello world';
}
let result = testAsy(); 
console.log(result)

所以,async 函数返回的是一个 Promise 对象。async 函数(包含函数语句、函数表达式、Lambda表达式)会返回一个 Promise 对象,如果在函数中 return 一个直接量,async 会把这个直接量通过 Promise.resolve() 封装成 Promise 对象。

async 函数返回的是一个 Promise 对象,所以在最外层不能用 await 获取其返回值的情况下,当然应该用原来的方式:then() 链来处理这个 Promise 对象,就像这样:

async function testAsy(){
   return 'hello world'
}
let result = testAsy() 
console.log(result)
result.then(v=>{
    console.log(v)   // hello world
})

那如果 async 函数没有返回值,又该如何?很容易想到,它会返回 Promise.resolve(undefined)

联想一下 Promise 的特点——无等待,所以在没有 await 的情况下执行 async 函数,它会立即执行,返回一个 Promise 对象,并且,绝不会阻塞后面的语句。这和普通返回 Promise 对象的函数并无二致。

注意:Promise.resolve(x) 可以看作是 new Promise(resolve => resolve(x)) 的简写,可以用于快速封装字面量对象或其他对象,将其封装成 Promise 实例。

对浏览器的缓存机制的理解

浏览器缓存的全过程:

说一下 web worker

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

如何创建 web worker:

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

CDN的原理

CDN和DNS有着密不可分的联系,先来看一下DNS的解析域名过程,在浏览器输入的解析过程如下:
(1) 检查浏览器缓存
(2)检查操作系统缓存,常见的如hosts文件
(3)检查路由器缓存
(4)如果前几步都没没找到,会向ISP(网络服务提供商)的LDNS服务器查询
(5)如果LDNS服务器没找到,会向根域名服务器(Root Server)请求解析,分为以下几步:

CDN的工作原理: (1)用户未使用CDN缓存资源的过程:

  1. 浏览器通过DNS对域名进行解析(就是上面的DNS解析过程),依次得到此域名对应的IP地址
  2. 浏览器根据得到的IP地址,向域名的服务主机发送数据请求
  3. 服务器向浏览器返回响应数据

(2)用户使用CDN缓存资源的过程:

  1. 对于点击的数据的URL,经过本地DNS系统的解析,发现该URL对应的是一个CDN专用的DNS服务器,DNS系统就会将域名解析权交给CNAME指向的CDN专用的DNS服务器。
  2. CND专用DNS服务器将CND的全局负载均衡设备IP地址返回给用户
  3. 用户向CDN的全局负载均衡设备发起数据请求
  4. CDN的全局负载均衡设备根据用户的IP地址,以及用户请求的内容URL,选择一台用户所属区域的区域负载均衡设备,告诉用户向这台设备发起请求
  5. 区域负载均衡设备选择一台合适的缓存服务器来提供服务,将该缓存服务器的IP地址返回给全局负载均衡设备
  6. 全局负载均衡设备把服务器的IP地址返回给用户
  7. 用户向该缓存服务器发起请求,缓存服务器响应用户的请求,将用户所需内容发送至用户终端。

如果缓存服务器没有用户想要的内容,那么缓存服务器就会向它的上一级缓存服务器请求内容,以此类推,直到获取到需要的资源。最后如果还是没有,就会回到自己的服务器去获取资源。

CNAME(意为:别名):在域名解析中,实际上解析出来的指定域名对应的IP地址,或者该域名的一个CNAME,然后再根据这个CNAME来查找对应的IP地址。

标签:面试题,缓存,函数,对象,git,原型,必会,服务器,合集
来源: https://www.cnblogs.com/yyzzabc123/p/16686208.html