JS 中可能被你忽视的高频面试题
# 说说 JS 预编译/处理
JS
是 解释性
语言,就是从上到下从左到右逐行解析,逐行执行。
预编译
:JS
在真正被解析之前,JS
引擎先把整个文件进行预处理并消除一些歧义的过程。
全局 / 局部 预编译过程大致如下:
- 产生全局对象
window
。 - 查找所有变量,并把查找到的变量作为
window
的属性,值为undefined
- 查找所有函数,并把查找到的函数作为
window
的属性,值为function
- 全局预编译结束,代码从上到下从左到右依次执行。
当执行到调用函数时,开始了局部预编译:
- 代用函数时,产生了函数的活动对象
AO
。 - 查找函数内部变量并作为
AO
的属性,值为undefined
。 - 这时函数如果有传参的话,把传过来的参数赋值给形参。
- 查找函数内部函数并作为
AO
的属性,值为function
。 - 局部预编译结束,开始从上到下从左到右依次执行代码。
有的地方说,函数优先级比变量高,所以在变量提升和函数提升时,变量会被同名函数覆盖,其实原理还是预处理的过程是先变量后函数。
优先级为:函数 > 实参 >局部变量
# 说说全局对象/变量/函数
全局对象(Global Object)
:
在浏览器环境中,JS
引擎会整合所有 <script>
标签的内容,产生 window
这个全局对象。
在 node
环境中会产生 globa
l 对象
在所有 <script>
标签里内容执行完后销毁
全局变量
:
在 <script>
标签中声明的 变量
,会作为 window的属性
存在
全局函数
:
在 <script>
标签中声明的 函数
,会作为 window的方法
存在
# 说说活动对象/局部变量/函数
活动/激活对象(Activation Object)
:
在函数调用时产生,用来保存当前函数内部的执行环境,也叫 执行期上下文
。
在函数调用结束时销毁
局部变量
:
在函数内部声明的 变量
,作为 AO
对象的 属性
存在
局部函数
:
在函数内部声明的 函数
,作为 AO
对象的 方法
存在
# 给出下面的思路和答案
let a = ?
if(a==1&&a==2&&a==3){
console.log('123');
}
描述:a 等于什么值时,可以使表达式成立
其实这道题想考察的,就是我们对 取值
的理解 和 隐式转换的理解。
比如 [10] == 10 ==> true
其实是 [10]
隐式转换了字符串即 Number([10].toString()) == 10
这里 ==
比较值时有这么几个规律:
-
对象 == 字符串
时对象.toString()
转换为字符串 -
null
和undefined
相等,和其他值不相等 -
NaN
和任何值都不相等包括自己 - 其余的都转换为
数字
解题思路大致分为两大类:toString
和 defineProperty
法一:toString 重写
let a = {
i: 0,
toString(){
return ++this.i;
}
}
if(a==1 && a==2 && a==3){
console.log('条件成立');
}
或者
var a = [1,2,3];
a.toString = a.shift;
if(a==1 && a==2 && a==3){
console.log('条件成立');
}
法二:defineProperty 数据劫持
var i = 0;
Object.defineProperty(window,'a',{
get(){
return ++i;
}
})
if(a==1 && a==2 && a==3){
console.log('条件成立');
}
# 一个青蛙一次可跳一个或者两个台阶,求 n 个台阶有多少种跳法
实际上就是斐波那契数列
,递归实现
function JumpFloor( n){
if(n<=0){
return -1;
}
if(n==1 || n==2){
return n;
}
else{
return cal(n-1)+cal(n-2);
}
}
# 说说 var let const
- ·var· 声明的变量有声明提前
可以声明不赋值
可以声明相同的变量名,后边会覆盖前面的 -
let
声明的变量没有声明提前
可以声明不赋值
同一个作用域下不能重复定义同一个名称,会报错
有严格的函数和块级作用域 -
const
声明的变量没有声明提前
不可以声明不赋值,会报错
声明的常量是只读的,不可以修改
声明的对象和数组,可修改其元素,但引用地址不可修改
# 实现一个页面滚动,图片懒加载的js库(快速滑动/滚动,img经过可视区时,不加载图片)
# 实现小球落地的过程
