56 道高频 JavaScript 与 ES6+ 的面试题及答案(上)
本文讲解 56 道 JavaScript 和 ES6 面试题的内容。
注意 文章的题与题之间用下划线分隔开 答案仅供参考。
前端硬核面试专题的完整版在此 前端硬核面试专题 包含 HTML CSS JS ES6 Webpack Vue React Node HTTPS 数据结构与算法 Git 。
常见的浏览器内核有哪些
mouseenter 和 mouseover 的区别
用正则表达式匹配字符串 以字母开头 后面是数字、字符串或者下划线 长度为 9 - 20
var re new RegExp( ^[a-zA-Z][a-zA-Z0-9_]{9,20}$
手机号码校验
function checkPhone(){ var phone document.getElementById( phone ).value; if(!(/^1(3|4|5|7|8)\d{9}$/.test(phone))){ alert( 手机号码有误 请重填 return false; ^1(3|4|5|7|8)d{9}$ 表示以 1 开头 第二位可能是 3/4/5/7/8 等的任意一个 在加上后面的 d 表示数字 [0-9] 的 9 位 总共加起来 11 位结束。 手机号码格式验证方法(正则表达式验证)支持最新电信 199 移动 198 联通 166 // 手机号码校验规则 let valid_rule /^(13[0-9]|14[5-9]|15[012356789]|166|17[0-8]|18[0-9]|19[8-9])[0-9]{8}$/; if ( ! valid_rule.test(phone_number)) { alert( 手机号码格式有误 return false; }
这样 phone_number 就是取到的手机号码 即可
js 字符串两边截取空白的 trim 的原型方法的实现
js 中本身是没有 trim 函数的。
// 删除左右两端的空格 function trim(str){ return str.replace(/(^\s*)|(\s*$)/g, // 删除左边的空格 /(^\s*)/g // 删除右边的空格 /(\s*$)/g
介绍一下你对浏览器内核的理解 ?
内核主要分成两部分 渲染引擎(layout engineer 或 Rendering Engine) 和 JS 引擎。
渲染引擎
负责取得网页的内容 HTML、XML、图像等等 、整理讯息 例如加入 CSS 等 以及计算网页的显示方式 然后会输出至显示器或打印机。
浏览器的内核的不同对于网页的语法解释会有不同 所以渲染的效果也不相同。
所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。
JS 引擎
解析和执行 javascript 来实现网页的动态效果。
最开始渲染引擎和 JS 引擎并没有区分的很明确 后来 JS 引擎越来越独立 内核就倾向于只指渲染引擎。
哪些常见操作会造成内存泄漏
内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。
垃圾回收器定期扫描对象 并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0 没有其他对象引用过该对象 或对该对象的惟一引用是循环的 那么该对象的内存即可回收。
线程与进程的区别
线程在执行过程中与进程还是有区别的。
每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行 必须依存在应用程序中 由应用程序提供多个线程执行控制。从逻辑角度来看 多线程的意义在于一个应用程序中 有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用 来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
eval() 函数有什么用
eval() 函数可计算某个字符串 并执行其中的的 JavaScript 代码。
实现一个方法 使得 add(2, 5) 和 add(2)(5) 的结果都为 7
var add function (x, r) { if (arguments.length 1) { return function (y) { return x }; } else { return x console.log(add(2)(5)); // 7 console.log(add(2,5)); // 7
alert(1 2) 和 alert(1 || 0) 的结果是
alert(1 2 ) 的结果是 2
alert(0 || 1) 的结果是 1
下面的输出结果是
var out 25, inner { out: 20, func: function () { var out 30; return this.out; console.log((inner.func, inner.func)()); console.log(inner.func()); console.log((inner.func)()); console.log((inner.func inner.func)());
结果 25 20 20 25
代码解析 这道题的考点分两个
先看第一个输出 25 因为 ( inner.func, inner.func ) 是进行逗号运算符 逗号运算符就是运算前面的 ”,“ 返回最后一个 举个栗子
var i 0, j 1, k console.log((i , j , k)) // 返回的是 k 的值 2 如果写成 k 的话 这里返回的就是 3 console.log(i); // 1 console.log(j); // 2 console.log(k); // 2
回到原题 ( inner.func, inner.func ) 就是返回 inner.func 而 inner.func 只是一个匿名函数
function () { var out 30; return this.out; }
而且这个匿名函数是属于 window 的 则变成了
(function () { var out 30; return this.out; })()
此刻的 this window
所以 out 是 25。
第二和第三个 console.log 的作用域都是 inner 也就是他们执行的其实是 inner.func();
inner 作用域中是有 out 变量的 所以结果是 20。
第四个 console.log 考查的是一个等号运算 inner.func inner.func 其实返回的是运算的结果
举个栗子
var a 2, b console.log(a b) // 输出的是 3
所以 inner.func inner.func 返回的也是一个匿名函数
function () { var out 30; return this.out; }
此刻 道理就和第一个 console.log 一样了 输出的结果是 25。
下面程序输出的结果是
if (!( a in window)) { var a alert(a);
代码解析 如果 window 不包含属性 a 就声明一个变量 a 然后赋值为 1。
你可能认为 alert 出来的结果是 1 然后实际结果是 “undefined”。
要了解为什么 需要知道 JavaScript 里的 3 个概念。
首先 所有的全局变量都是 window 的属性 语句 var a 等价于 window.a
你可以用如下方式来检测全局变量是否声明 变量名称 in window。
第二 所有的变量声明都在范围作用域的顶部 看一下相似的例子
alert( b in window); var b;
此时 尽管声明是在 alert 之后 alert 弹出的依然是 true 这是因为 JavaScript 引擎首先会扫描所有的变量声明 然后将这些变量声明移动到顶部 最终的代码效果是这样的
var a; alert( a in window);
这样看起来就很容易解释为什么 alert 结果是 true 了。
第三 你需要理解该题目的意思是 变量声明被提前了 但变量赋值没有 因为这行代码包括了变量声明和变量赋值。
你可以将语句拆分为如下代码
var a; //声明 a //初始化赋值
当变量声明和赋值在一起用的时候 JavaScript 引擎会自动将它分为两部以便将变量声明提前
不将赋值的步骤提前 是因为他有可能影响代码执行出不可预期的结果。
所以 知道了这些概念以后 重新回头看一下题目的代码 其实就等价于
var a; if (!( a in window)) { alert(a);
这样 题目的意思就非常清楚了 首先声明 a 然后判断 a 是否在存在 如果不存在就赋值为1 很明显 a 永远在 window 里存在 这个赋值语句永远不会执行 所以结果是 undefined。
提前这个词语显得有点迷惑了 你可以理解为 预编译。
下面程序输出的结果是
var a var b function a(x) { x a(--x); alert(a);
这个题目看起来比实际复杂 alert 的结果是 1。
这里依然有 3 个重要的概念需要我们知道。
澄清一下 函数声明是如下这样的代码
function functionName(arg1, arg2){ //函数体 如下不是函数 而是函数表达式 相当于变量赋值 var functionName function(arg1, arg2){ //函数体 };
澄清一下 函数表达式没有提前 就相当于平时的变量赋值。
为了解释这个 我们来看一个例子
function value(){ return 1; var value; alert(typeof value); // function
尽管变量声明在下面定义 但是变量 value 依然是 function 也就是说这种情况下 函数声明的优先级高于变量声明的优先级 但如果该变量 value 赋值了 那结果就完全不一样了
function value(){ return 1; var value alert(typeof value); // number
该 value 赋值以后 变量赋值初始化就覆盖了函数声明。
重新回到题目 这个函数其实是一个有名函数表达式 函数表达式不像函数声明一样可以覆盖变量声明 但你可以注意到 变量 b 是包含了该函数表达式 而该函数表达式的名字是 a。不同的浏览器对 a 这个名词处理有点不一样 在 IE 里 会将 a 认为函数声明 所以它被变量初始化覆盖了 就是说如果调用 a(–x) 的话就会出错 而其它浏览器在允许在函数内部调用 a(–x) 因为这时候 a 在函数外面依然是数字。
基本上 IE 里调用 b(2) 的时候会出错 但其它浏览器则返回 undefined。
理解上述内容之后 该题目换成一个更准确和更容易理解的代码应该像这样
var a 1, b function(x) { x b(--x); alert(a);
这样的话 就很清晰地知道为什么 alert 的总是 1 了。
下面程序输出的结果是
function a(x) { return x * 2; var a; alert(a); alert 的值是下面的函数 function a(x) { return x * 2; }
这个题目比较简单 即函数声明和变量声明的关系和影响 遇到同名的函数声明 不会重新定义。
下面程序输出的结果是
function b(x, y, a) { arguments[2] 10; alert(a); b(1, 2, 3);
结果为 10。
活动对象是在进入函数上下文时刻被创建的 它通过函数的 arguments 属性初始化。
三道判断输出的题都是经典的题
var a function b() { console.log(a); function a(){}; b();
明显输出是 3 因为里面修改了 a 这个全局变量 那个 function a(){} 是用来干扰的 虽然函数声明会提升 就被 a 给覆盖掉了 这是我的理解。
不记得具体的 就类似如下
var baz var bazz { baz: 2, getbaz: function() { return this.baz console.log(bazz.getbaz()) var g bazz.getbaz; console.log(g()) ;
第一个输出是 2 第二个输出是 3。
这题考察的就是 this 的指向 函数作为对象本身属性调用的时候 this 指向对象 作为普通函数调用的时候 就指向全局了。
还有下面的题
var arr [1,2,3,4,5]; for(var i i arr.length; i ){ arr[i] function(){ alert(i) arr[3]();
典型的闭包 弹出 5 。
JavaScript 里有哪些数据类型
一、数据类型
undefiend 没有定义数据类型 number 数值数据类型 例如 10 或者 1 或者 5.5 string 字符串数据类型用来描述文本 例如 你的姓名 boolean 布尔类型 true | false 不是正就是反 object 对象类型 复杂的一组描述信息的集合function 函数类型解释清楚 null 和 undefined
null 用来表示尚未存在的对象 常用来表示函数企图返回一个不存在的对象。 null 表示 没有对象 即该处不应该有值。
null 典型用法是
当声明的变量还未被初始化时 变量的默认值为 undefined。 undefined 表示 缺少值 就是此处应该有一个值 但是还没有定义。
变量被声明了 但没有赋值时 就等于 undefined。 调用函数时 应该提供的参数没有提供 该参数等于 undefined。 对象没有赋值的属性 该属性的值为 undefined。 函数没有返回值时 默认返回 undefined。未定义的值和定义未赋值的为 undefined null 是一种特殊的 object NaN 是一种特殊的 number。
讲一下 1 和 Number(1) 的区别*
讲一下 prototype 是什么东西 原型链的理解 什么时候用 prototype
prototype 是函数对象上面预设的对象属性。
函数里的 this 什么含义 什么情况下 怎么用
情况一 纯粹的函数调用
这是函数的最通常用法 属于全局性调用 因此 this 就代表全局对象 window。
function test(){ this.x alert(this.x); test(); // 1
为了证明 this 就是全局对象 我对代码做一些改变
var x function test(){ alert(this.x); test(); // 1
运行结果还是 1。
再变一下
var x function test(){ this.x test(); alert(x); // 0
情况二 作为对象方法的调用
函数还可以作为某个对象的方法调用 这时 this 就指这个上级对象。
function test(){ alert(this.x); var x 2 var o {}; o.m test; o.m(); // 1
情况三 作为构造函数调用
所谓构造函数 就是通过这个函数生成一个新对象 object 。这时的 this 就指这个新对象。
function Test(){ this.x var o new Test(); alert(o.x); // 1
运行结果为 1。为了表明这时 this 不是全局对象 对代码做一些改变
var x function Test(){ this.x var o new Test(); alert(x); // 2
运行结果为 2 表明全局变量 x 的值没变。
情况四 apply 调用
apply() 是函数对象的一个方法 它的作用是改变函数的调用对象 它的第一个参数就表示改变后的调用这个函数的对象。因此 this 指的就是这第一个参数。
var x function test(){ alert(this.x); var o {}; o.m test; o.m.apply(); // 0
apply() 的参数为空时 默认调用全局对象。因此 这时的运行结果为 0 证明 this 指的是全局对象。
如果把最后一行代码修改为
o.m.apply(o); // 1
运行结果就变成了 1 证明了这时 this 代表的是对象 o。
apply 和 call 什么含义 什么区别 什么时候用
call apply 都属于 Function.prototype 的一个方法 它是 JavaScript 引擎内在实现的 因为属于 Function.prototype 所以每个 Function 对象实例(就是每个方法)都有 call apply 属性。
既然作为方法的属性 那它们的使用就当然是针对方法的了 这两个方法是容易混淆的 因为它们的作用一样 只是使用方式不同。
语法
foo.call(this, arg1, arg2, arg3) foo.apply(this, arguments) this.foo(arg1, arg2, arg3);
每个函数对象会有一些方法可以去修改函数执行时里面的 this 比较常见得到就是 call 和 apply 通过 call 和 apply 可以重新定义函数的执行环境 即 this 的指向。
function add(c, d) { console.log(this.a this.b c d); var o { a: 1, b: 3 }; add.call(o, 5, 7); //1 3 5 7 16 //传参的时候是扁平的把每个参数传进去 add.apply(o, [10, 20]); //1 3 10 20 34 //传参的时候是把参数作为一个数组传进去 //什么时候使用 call 或者 apply function bar() { console.log(Object.prototype.toString.call(this)); // 用来调用一些无法直接调用的方法 bar.call(7); // [object Number]
异步过程的构成要素有哪些 和异步过程是怎样的
总结一下 一个异步过程通常是这样的
所以 从主线程的角度看 一个异步过程包括下面两个要素
它们都是在主线程上调用的 其中注册函数用来发起异步过程 回调函数用来处理结果。
举个具体的例子
setTimeout(fn, 1000);
其中的 setTimeout 就是异步过程的发起函数 fn 是回调函数。
注意 前面说的形式 A(args..., callbackFn) 只是一种抽象的表示 并不代表回调函数一定要作为发起函数的参数。
例如
var xhr new XMLHttpRequest(); xhr.onreadystatechange xxx; // 添加回调函数 xhr.open( GET , url); xhr.send(); // 发起函数
发起函数和回调函数就是分离的。
说说消息队列和事件循环
JavaScript中的重要组成部分之ES6特性 随着互联网技术的不断发展,前端开发也变得越来越重要。在这样的背景下,ES6(ECMAScript 2015)作为 JavaScript 的一个重要版本,为前端开发带来了许多新特性和功能,本文将介绍一些常用的 ES6 特性。
相关文章
- JavaScript代码风格要素
- javascript 常用方法 解析URL,补充前导字符, 省市联动, 循环替换模板
- GraphQL的JavaScript实现graphql-js应用举例
- JavaScript中Math介绍、Math常用方法、常用案例(随机数,随机颜色等)
- 【JavaScript】标准内置函数 eval
- JavaScript – 解构赋值 Destructuring Assignment
- JavaScript实战—JavaScript、jQuery、HTML5、Node.js实例大全
- 12个实用的 JavaScript 框架分享给前端开发者
- 总结面试题——Javascript
- [译]JavaScript 让 Monad 更简单(软件编写)(第十一部分)
- 重学前端 30 # JavaScript语法的基本规则
- 有必要知道的 24 个 JavaScript 面试题