常见前端面试题目整理(JavaScript)

所有题目答案整理自网络,如有错误,接受指正,拒绝批评!

js常见的数据类型,判断方法

数据类型:

基本数据类型:

number, string, bool, array, object, null, undefined


引用类型:

Object, Array, Date, Function, RegExp, Function等


判断方法:

  • typeof()
    常见,返回的是基本数据类型(没有null)
console.log(typeof("123"))  //string
console.log(typeof(null))    //object
  • instanceof
    判断的是对象类型(Array, Date, Function等)
var arr = [1, 2, 3]
console.log(arr instanceof Array)   //true
  • constructor

    判断的是对象,很繁琐,继承的时候会出错
var arr = [1, 2, 3]
console.log(arr.constructor === Array)   //true
  • prototype

    通用但很繁琐的方法
var arr = [1, 2, 3]
console.log(Object.prototype.toString.call(a) === `[Object Array]`)   //true
  • jQuery.type()

    能判断基本数据类型和引用类型
var a = 1
console.log(jQuery.type(a))   //number
var reg = /abc/
console.log(jQuery.type(reg))
//regexp

判断NaN

  • isNaN() 需要首先检查是Number类型
function myIsNaN(value) { 
    return typeof value === 'number' && isNaN(value); 
} 
  • 利NaN严格不等于自身
function myIsNaN(value) { 
    return value !== value; 
} 

IIFE

立即执行函数表达式,匿名函数

  • 创建块级(私有)作用域,避免了向全局作用域中添加变量和函数,因此也避免了多人开发中全局变量和函数的命名冲突
  • IIFE中定义的任何变量和函数,都会在执行结束时被销毁。这种做法可以减少闭包占用的内存问题,因为没有指向匿名函数的引用。只要函数执行完毕,就可以立即销毁其作用域链了

变量的作用域与作用域链

全局执行环境是最外围的一个执行环境,所有全局变量和函数都是作为window对象的属性和方法创建的。局部变量只在函数体内有定义。在局部变量和全局变量重名时,局部变量优先于全局变量。


当代码在环境中执行时,会创建变量对象的一个作用域链,用于保证对执行环境有访问权的所有变量和函数的有序访问。作用域链的前端始终是当前执行代码所在的环境的变量对象。


如果一个环境是函数,则将其活动对象作为变量对象,活动对象在最开始只包含一个变量(arguments),作用域链的下一个变量对象来自外部环境,再下一个变量来自下一个外部环境,直到全局执行环境。


可参考
深入理解javascript作用域系列第五篇——一张图理解执行环境和作用域

同步/异步 并行/并发

同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。
(例如: 在商场买东西,等待仓库配货完成后付款离开,一次购物完成)

异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。而,异步方法通常会在另外一个线程中,“真实”地执行着。整个过程,不会阻碍调用者的工作。
(在某宝购买一件商品,下单付款后就算完成一次购物)

并行 任务在同一时间运行,例如,在多核处理器上。有同时处理多个任务的能力。


并发 两个任务可以在重叠的时间段内启动,运行和完成。有处理多个任务的能力,不一定要同时。

ES6

可参考ES6这些就够了
let, const

此前通常用var声明变量,会造成变量提升。

用let来声明变量,使用范围局限于当前块级作用域
const表示常量,值不能改变

console.log(a)  //undefined
var a = 10
console.log(b)  //Uncaught ReferenceError: b is not defined
let b = 1
console.log(b)  //1
const a = 10
a = 20
console.log(a)  //Uncaught SyntaxError: Identifier 'a' has already been declared

字符串


基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定

const name = 'xiaoming'
console.log(`hello ${name}`) //hello xiaoming

在ES5时我们通过反斜杠()来做多行字符串或者字符串一行行拼接。ES6反引号(``)直接搞定

const template = `<div>
        <span>hello world</span>
    </div>`

函数
为参数提供默认值

function action(num = 200) {
        console.log(num)
    }
action(0) // 0
action() //200
action(300) //300

箭头函数
省略function,return关键字

var fun = a => a + 1
//等价于
var fun = function(a){
    return a + 1
}

Object
对象初始化简写

function people(name, age) {
    return {
        name,
        age
    };
}

import与export

import导入模块、export导出模块

loadsh

一套工具库,内部封装了很多字符串、数组、对象等常见数据类型的处理函数

参考学习lodash——这一篇就够用

BOM与DOM

BOM

window, location, navigator

DOM

document

面向对象的写法

概述ES5和ES6中面向对象的实现方法
总之,ES6类似Java

arguments

arguments对象是所有(非箭头)函数中都可用的局部变量。你可以使用arguments对象在函数中引用函数的参数。此对象包含传递给函数的每个参数,第一个参数在索引0处。

arguments对象不是一个 Array 。它类似于Array,但除了length属性和索引元素之外没有任何Array属性。例如,它没有 pop 方法。但是它可以被转换为一个真正的Array
type of返回object

var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);

// ES2015
const args = Array.from(arguments);

属性

  • callee 指向当前执行函数
  • length
  • arguments[@@iterator] 返回一个新的Array迭代器对象,该对象包含参数中每个索引的值

this

在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了。因为this的取值是执行上下文环境的一部分,每次调用函数,都会产生一个新的执行上下文环境。

  • 构造函数
    • 函数作为构造函数用,那么其中的this就代表它即将new出来的对象。直接调用Foo函数,而不是new Foo(),则this为window。
  • 函数作为对象的一个属性
    • 如果函数作为对象的一个属性时,并且作为对象的一个属性被调用时,函数中的this指向该对象。如果fn函数不作为obj的一个属性被调用,就是window
  • 全局
    • window

静态作用域

要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,切记切记

bind, call, apply

call、apply和bind是Function对象自带的三个方法,都是为了改变函数体内部 this 的指向。bind 是返回对应 函数,便于稍后调用;apply 、call 则是立即调用。call 是把参数按顺序传递进去,而 apply 则是把参数放在数组里。

  • apply:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.apply(A,arguments);即A对象应用B对象的方法。
  • call:调用一个对象的一个方法,用另一个对象替换当前对象。例如:B.call(A,args1,args2);即A对象调用B对象的方法。

作用域链

静态作用域

要到创建这个函数的那个作用域中取值——是“创建”,而不是“调用”,切记切记

作用域链(取某个自由变量)

  • 现在当前作用域查找a,如果有则获取并结束。如果没有则继续;
  • 第二步,如果当前作用域是全局作用域,则证明a未定义,结束;否则继续;
  • 第三步,(不是全局作用域,那就是函数作用域)将创建该函数的作用域作为当前作用域;
  • 第四步,跳转到第一步

闭包

  • 函数作为返回值
  • 函数作为参数传递

函数调用完成之后,其执行上下文环境不会接着被销毁。这就是需要理解闭包的核心内容。因此使用闭包的开销会比较大。

继承

  • 原型链
  • call() / apply()
  • 混合方式(prototype和call()/apply()结合)
  • 对象冒充

prototype原型

默认的给函数一个属性——prototype。对,每个函数都有一个属性叫做prototype。

这个prototype的属性值是一个对象(属性的集合,再次强调!),默认的只有一个叫做constructor的属性,指向这个函数本身

每个对象都有一个隐藏的属性——“__proto__”,指向创建该对象的函数的prototype。(例如:obj.__proto__=== Object.prototype)。Object.prototype确实一个特例——它的__proto__指向的是null

原型链

访问一个对象的属性时,先在基本属性中查找,如果没有,再沿着proto这条链向上找,这就是原型链。

那么我们在实际应用中如何区分一个属性到底是基本的还是从原型中找到的呢?大家可能都知道答案了——hasOwnProperty,特别是在for…in…循环中,一定要注意。

headers

字段有

  • Accept(浏览器端可以接受的媒体类型)
  • Accept-Encoding
  • Accept-Language
  • Connection(keep-alive 当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接)
  • Host(发送请求时,该报头域是必需的,请求报头域主要用于指定被请求资源的Internet主机和端口号,它通常从HTTP URL中提取出来的)
  • Referer(告诉服务器我是从哪个页面链接过来的,服务器籍此可以获得一些信息用于处理)
  • User-Agent(告诉HTTP服务器,客户端使用的操作系统和浏览器的名称和版本.)

promise

Promise 的基础用法

Promise 是异步编程的一种解决方案,比传统的解决方案–回调函数和事件。在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了

三种状态:

  • pending:进行中
  • fulfilled :已经成功
  • rejected 已经失败

状态改变,Promise对象的状态改变,只有两种可能:

  • 从pending变为fulfilled
  • 从pending变为rejected。
    这两种情况只要发生,状态就凝固了,不会再变了,这时就称为resolved(已定型)
//Promise对象是一个构造函数,用来生成Promise实例
const promist = new Promise(function(resolve,reject){
    if(/*异步操作成功*/){
        resolve(value);
    }else{
        reject(error);
    }
})

//Promise 实例生成以后,可以用then 方法分别指定resolved状态和rejected状态的回调函数。
promise.then(function(value){
//success
},function(error){
//failure
});

跨域

参考[前端常见跨域解决方案(全)]

浏览器对于javascript的同源策略的限制,同源是指”协议+域名+端口”三者相同,即便两个不同的域名指向同一个ip地址,也非同源。非同源的情况下,Cookie、LocalStorage 和 IndexDB 无法读取,DOM 和 Js对象无法获得,AJAX 请求不能发送。

常用方法

  • jsonp

    回调函数和数据,请求一个带参网址实现跨域通信,只支持get请求
  • iframe
  • nginx

    通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录
  • CORS(Cross-Origin Resource Sharing, HTML5)

    CORS简介


    该响应头用来记录可以访问该资源的域。在接收到服务端响应后,浏览器将会查看响应中是否包含Access-Control-Allow-Origin响应头。如果该响应头存在,那么浏览器会分析该响应头中所标示的内容
    CORS导致的跨域请求分为三种: Simple Request,Preflighted Request以及Requests with Credential
  • ...

Ajax

Ajax指异步的JavaScript和XML。Ajax的核心是XMLHTTPRequest对象(XHR)。XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

工作原理

  • 创建ajax对象(XMLHttpRequest/ActiveXObject(Microsoft.XMLHttp))
  • 判断数据传输方式(GET/POST)
  • 打开链接 open()
  • 发送 send()
  • 当ajax对象完成第四步(onreadystatechange)数据接收完成,判断http响应状态(status)200-300之间或者304(缓存)执行回调函数
var xmlhttp;
if (str.length==0)
  { 
  document.getElementById("txtHint").innerHTML="";
  return;
  }
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
xmlhttp.onreadystatechange=function()
  {
  if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
    document.getElementById("txtHint").innerHTML=xmlhttp.responseText;
    }
  }
xmlhttp.open("GET","/ajax/gethint.asp?q="+str,true);
xmlhttp.send();

垃圾回收

  • 定义:垃圾回收机制(GC:Garbage Collection),执行环境负责管理代码执行过程中使用的内存。
  • 原理:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。
  • 垃圾回收策略:标记清除(较为常用)和引用计数
    • 标记清除:当变量进入环境时,将变量标记"进入环境",当变量离开环境时,标记为:"离开环境"。某一个时刻,垃圾回收器会过滤掉环境中的变量,以及被环境变量引用的变量,剩下的就是被视为准备回收的变量。
    • 引用计数:引用计数是跟踪记录每个值被引用的次数。记录变量的引用次数,被引用一次则加1,当这个引用计数为0时,被视为准备回收的对象。
  • 触发时间:垃圾回收器会根据内存分配量与程序占用内存的比例进行动态调整,开始回收工作
  • 优化策略
    • 分代回收
      • 目的是通过区分“临时”与“持久”对象;多回收“临时对象”区(young generation),少回收“持久对象”区(tenured generation),减少每次需遍历的对象,从而减少每次GC的耗时
    • 增量GC
      • 每次处理一点,下次再处理一点

性能优化

  • 内容方面:
  1. 减少 HTTP 请求 (Make Fewer HTTP Requests)
  2. 减少 DOM 元素数量 (Reduce the Number of DOM Elements)
  3. 使得 Ajax 可缓存 (Make Ajax Cacheable)
  • 针对CSS:
  1. 把 CSS 放到代码页上端 (Put Stylesheets at the Top)
  2. 从页面中剥离 JavaScript 与 CSS (Make JavaScript and CSS External)
  3. 精简 JavaScript 与 CSS (Minify JavaScript and CSS)
  4. 避免 CSS 表达式 (Avoid CSS Expressions)
  • 针对JavaScript :
  1. 脚本放到 HTML 代码页底部
  2. 从页面中剥离 JavaScript与CSS
  3. 精简 JavaScript 与 CSS
  4. 移除重复脚本

接口安全性

  • 签名

    根据用户名或者用户id,结合用户的ip或者设备号,生成一个token。在请求后台,后台获取http的head中的token,校验是否合法(和数据库或者redis中记录的是否一致,在登录或者初始化的时候,存入数据库/redis)
  • 加密

    客户端和服务器都保存一个秘钥,每次传输都加密,服务端根据秘钥解密。
  • 第三方支持

前后端渲染对比

前端渲染
  1. 业务分离,后端只需要提供数据接口,前端开发时不需要部署对应的后端环境,通过代理服务器工具获取后端数据开发
  2. 计算量转移,原本需要后端渲染的任务转移给了前端
后端渲染

(JSP等)

  1. 对搜索引擎友好
  2. 首页加载时间短,后端渲染完后直接显示HTML,前端渲染在加载完成后还需要js渲染时间
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 文 | 彼怀 2017/09/29 星期五 天气晴 密集的人群分别聚集在火车票对应的车门口处等待着...
    彼怀阅读 2,895评论 15 21
  • 问:如何提高自己的自控力? 答:人们【不可能】靠自己提高自己的自控力,如果可以,世界上就全都是马云、乔布斯了,所以...
    智慧与运气阅读 3,161评论 0 0
  • Hr新人, 你们好! 首先恭喜你,你踏入了一个未来前景广阔的一个领域,随着人力资源逐渐被认可,且把人力资源放在战略...
    淘爸阅读 3,973评论 0 2