Go to comments

JavaScript 预编译后的面试题

一、复习预编译

开始总结的那两条

1. 函数声明整体提升

2. 变量声明  提升


这两句话不用记了,是由预编译产生的环节。

由于预编译才产生了函数声明整体提升,由预编译才产生了变量  声明提升


1、看一个问题

访问 b 输出的是什么值?

console.log(b); // undefined

var b = function () {
}

1. 访问 b 输出 undefined,

2. 因为 var b 走的是变量,不是走函数。

预编译弄懂了之后,函数执行的任何顺序。一行一行怎么去执行,都没有任何问题了,预编译就是解决执行顺序问题的。


2、看第二题

下面的题有一个小 bug

if 判断里面定义函数 function c(){},大约在 2016 年之前谷歌浏览器内核浏览器是允许这么定义的,现在不允许了,但就当做它允许为了练题。

现在 if 判断里面定义一个函数声明,是语法不通过的,但是过去可以。

a = 100;

function demo(e){
  function e(){}
  arguments[0] = 2;
  console.log(e);

  if(a){
    var b = 123;
    function c(){
      // 过去谷歌浏览器内核是允许,if里面定义函数的,现在if里面定义函数声明是语法不通过的
    }
  }

  var c;
  a = 10;
  var a;
  console.log(b);
  f = 123;
  console.log(c);// 这里应该打印function,现在打印的是undefined,因为现在if里不能定义函数
  console.log(a);
}

var a;

demo(1);

console.log(a);
console.log(f);


全局预编译

第一步,生成 GO{} 对象


第二步,GO 里面一个变量 a

GO{

  a: undefined

}


第三步,然后还有一个函数声明

GO{

  a: undefined,

  demo: function(){code...}

}


GO完事了,开始执行

全局第一行  a = 100   先把 GO 里面的 a 变成100

GO{

  a: 100,

  demo: function(){code...}

}


全局

然后 demo 函数忽略

变量 a 也忽略

直接执行 demo(1)


在 demo(1) 执行的基础上,函数预编译


第一步,创建 AO{}


第二步,提升变量和形参

AO{

  e: undefined,

  b: undefined,

  c: undefined,

  a: undefined

}


第三步,实参形参相统一,实参e等于1

AO{

  e: 1,

  b: undefined,

  c: undefined,

  a: undefined

}


第四步:找函数声明,函数声明有 function e(){}、function c(){} 

AO{

  e: function e(){},

  b: undefined,

  c: function c(){}  if里面声明函数语法是不允许的,这里当做它可以,if里面的函数声明c在预编译环节正常提升

  a: undefined

}


预编译完了,下面开始执行 demo(1) 函数里面的语句

第一句,函数声明,不看了


第二句, arguments[0] = 2 

实参列表与传的参数位置相映射,你改我也改,我改你也改,

也就是 arguments[0] 与形参 e 相映射,相当于让形参 e 的值也变成 2

AO{

  e: 2,

  b: undefined,

  c: function c(){},   // 实际上c的值里是undefined

  a: undefined

}


第三句,然后  console.log(e)  也输出 2


第四句, if(a)   此时a的值是 undefined,a 的值到 AO 里面找,不能通过判断 if 里面的语句不执行


第五句, var c   不看


第六句, a = 10   a 的值变成 10

AO{

  e: 2,

  b: undefined,

  c: function c(){},   // 实际上c的值里是undefined

  a: 10

}


第七句, var a  不看了


第八句, console.log(b)   再打印b输出AO里b的值是 undefined


第九句, f = 123   AO里没有 f,扔给GO,f是暗示全局变量放到GO里面

GO{

  a: 100,

  demo: function(){code...},

  f: 123

}


第十句, console.log(c)  打印c其实理想中是function但现是undefined,因为现在语法if里不允许声明函数


第十一句, console.log(a)   AO里a的值是10,打印输出10


函数执行完事了,外部全局

 console.log(a)   全局上访问 a,输出GO里a的值是 100


 console.log(f)   全局上访问 f,输出GO里的f值是 123


输出结果

2

undefined

undefined( IE老浏览器输入是function c(){} )

10

100

123


二、面试题

1、下面这些题处处是陷阱,考的全是隐式类型转换

第一题

加号两边只要没有"字符串",就是正常为数字相加,数字相加隐式转换类型成数字,false转化成数字是0,0加1等于1

var str = false + 1;

document.write(str); // 1


第二题

这题这样解读,判断 false == 1 是否符相等,结果赋给demo。false不等于1,所以应该是false

var demo = false == 1;

document.write(demo); // false


第三题

这道题是重点

if(typeof(a) && -true + (+undefined) + ""){

 document.write("基础扎实");
    
}

1. 首先变量a未定义,typeof(a) 不报错并输出字符串类型的"undefined",

    只有 typeof 这一种情况,用未定义的变量是不报错,而且返回字符串类型的 "undefined"

2.  -true 转化结果是 -1, +undefined 趋势是转为 Number,但转不了转化值是 NaN,

    隐式类型转换最多的是数学符号,凡是数学符号的趋势一般是转换成数字,转不了的一般是 NaN,但还是 number 类型。

3. -1 + NaN 还是NaN,NaN + ""(空字符串),结果是 "NaN" 的字符串

4. "undefined" || "NaN" 

    前面是字符串类型的"undefined",后面是字符串类型的"NaN",

    "字符串类型"转化成布尔值都是 true,所以并且判断一定能通过的。

5. 判断能通过,可以打印出 "基础扎实"


第四题

if(11 + "11" * 2 == 33){

  document.write("基础扎实"); 

}

1.  "11" * 2   不管乘法两边是什么,都要转化成数字的,字符串 "11" 转成成数字 11,再乘2结果是数字类型的22

2. 数字 22 在加 11 等于 33

3. 33 == 33 打印基础扎实


类型转换除了加号以外,除、减、莫尔、乘等等都是把运算符两侧的东西转换成数字的,比如

document.write("11" - "2"); // 数字类型 9


第五题

这个题惊悚一点

!!" " + !!"" - !!false || document.write("你觉得能打印你就是猪");


首先知道一点"空字符串"和"空格字符串"不是一回事,

空格字符串转化是 true

空串转化是 false

document.write(Boolean("")); // false

document.write(Boolean(" ")); // true


 !!" "   非非空格字符串变成布尔值是 true

 !!""   非非空字符串转换成布尔值是 false

 !!false  转换成布尔值是 false

true + false - false 转换数字 1 + 0 - 0 = 1

 1 || document.write("你觉得能打印你就是猪");   前面是true就不往后看了,所以后面的不可能打印出来

如果你觉得能打印,你就是 pork


2、写出下面程序的执行结果

var x = 1;

if(function f(){}){
  x += typeof(f);
}

console.log(x);

上面题没学到立即执行函数做不了,看下面这题


3、请写出window.foo的值(百度外卖的题)

这题是一个很简单的或运算符,但是或运算符暗藏杀机,但是这题考的挺缺心眼的

(window.foo || (window.foo = "bar"));


这题如果下面这样写,这道题就报错

(window.foo || window.foo = "bar");


首先清楚一点,这是一个优先级的问题,"或运算符"的优先级比赋值(等号)高


比如,下面 a 的值是 1,"或"是运算符要先计算的

var a = 1 || 2;


所以要是这样写 ( window.foo || window.foo = "bar" ) 成了,这两个 window.foo || window.foo 先计算然后在赋值,是坚决不可以的,会报错Uncaught ReferenceError:……

所以这题出的很精准,在里面加了一个小括号 ( window.foo || ( window.foo = "bar" ) );


解题顺序

1. 外面的括号,写不写没什么区别,反正先计算里面小括号里的

2. 先看里面括号 (window.foo = "bar"),先把bar赋值给 window.foo

3. 然后在看或运算符前面的 window.foo 的值是"bar",是"bar"就不往后读了

4. 最后是 window.foo 的结果是 bar


这题缺心眼,就是往错了分析也能答对

先看 window.foo 是 undefined,是 undefined 就往后看 (window.foo = "bar") 又变bar了,分析对分析错结果都是 bar

但是正确的理解一定是先看(window.foo = "bar"),先把bar赋进去


4、记录几个笔试题

1. css中display的属性有几种,分别是什么?block、none、inline、linline_block……

2. css中list-style的属性有几种,分别是什么?

3. 下面代码中,box_1和box_2平行排列均分父级的区域且没有边距,就是各占50%

<div class="box">
  <div class="box_1"></div>
  <div class="box_2"></div>
</div>


4. 写出下面程序的执行结果

var b = function a(){
  return 23;
}

typeof a();



Leave a comment 0 Comments.

Leave a Reply

换一张