中高级JavaScript易错面试题

写出下题的输出html

 

一、函数的实参与形参lengthpromise

 

var length = 10; function fn() { console.log(this.length); } var obj = { length: 5, method: function(fn) { fn(); arguments[0](); } }; console.log(obj.method(fn, 1));  // 0 2

 

 

咱们都知道,[1, 2, 3].length能够获得3,"123".length能够获得3,那么函数的length获得什么呢?浏览器

function test(a,b,c) {} test.length // 3
function test(a,b,c,d) {} test.length // 4

能够看到,函数的length彷佛返回了参数的个数,那么对于形参和实参有没有区别呢?答案是有。异步

 

function test() { console.log( arguments.length );} test(1,2,3); // 输出 3
test(1,2,3,4); // 输出 4

能够看到,在函数中,用arguments.length取到的是函数的实际参数的个数。async

另外,咱们要知道var length = 10 这样写是不行的,由于length是JavaScript内置的属性,不能用做变量名或函数名。戳这里http://www.runoob.com/js/js-reserved.html查看JavaScript有哪些保留关键字、内置属性等。编辑器

因此,当执行fn()时,this.length打印的是fn这个函数的形参的个数,为0;而执行arguments[0]()时,其实是obj.method()这个方法的arguments调用了fn函数,this.length的this指向的是arguments,他的实际参数个数为2。函数

 

 

二、函数的解析与预解析过程(变量提高)oop

 

function fn(a) {
    console.log(a);     // function a() {alert(1)}
    var a = 2;
    function a() {alert(1)}
    console.log(a);     //2
}
fn(1);

 

这道题仍是挺吊炸天的。。我也想了半天。。下面我来说一下,涉及到函数的解析和预解析过程。this

首先遇到function fn这样的函数声明,会进行函数预解析这么个过程,什么是函数预解析?通俗的说就是,从函数体里找变量和函数声明的过程,找到的变量(遇到var就找到了变量)不会去读具体的值,只会赋为undefined;找到的函数声明会赋值为整个函数体,这里有个知识点就是,若是找到的变量和声明同名,那么声明会覆盖变量(个人理解是,毕竟函数体比undefined的强嘛)。spa

好比此例中,预解析时找到了变量a,而且赋值为undefined,找到了声明function a(){alert(1)},为整个函数体;二者同名,因此声明覆盖了变量a的值,a再也不是undefined的,而是函数体。

预解析完成后调用了方法,开始一步一步走方法。首先console.log(a),这时打印出的是函数体;接着var a = 2,a的值从函数体被改为了 2 ;接着是个function a(){}函数声明,注意,声明不能改变变量的值,因此走完这一句,a的值仍是2,接着打印出了2。

 

 

有人确定有这样的疑惑,为何a=1传进去没起做用呢?这里有一个原则,就是局部变量优先,基于这个原则,咱们再来分析一下a的变化过程。预解析中,a=undefined,a=function(){alert(1)},此时参数有值等于1,本应该将a赋值为1,但却没有,缘由是此时的a已经等于局部函数声明function(){alert(1)},因此外部传进来的参数1并无取代a的值;假如本例没有function(){alert(1)}这一句,打印出的将是1,  2。

局部变量优先原则,原理同下:

var a = 5;
function fn(){
  var a = 10;
  console.log(a)  // 10,局部变量优先,在局部找到a后,不会再向外查找
}

 

 

三、变量提高、window的变量

if('a' in window) {   
    var a = 10; 
}  
console.log(a);  // 10

首先,if(){}的花括号并不像function(){}的花括号同样,具备本身的块级做用域,if的花括号仍是全局的环境。根据JavaScript的变量提高机制,var a会被js引擎解释到第一行,以下:

var a;
if ('a' in window) {
  a = 10;
}

接着有个知识点,全局变量是window对象的属性,因此'a'  in  window会返回true,答案就很直白了。

 

这道题我在作的时候踩了个坑,我在代码编辑器里写了以下代码:

window.onload = function(){
  if('a' in window){
    var a = 10;
  }  
 console.log(a)  // undefined
}

这时候,a这个变量是定义在匿名函数function(){}里的,属于该函数的局部变量,因此a再也不是window的对象。你们必定要注意细节。

 

 

四、基本类型无属性

var a = 10;
a.pro = 10;
console.log(a.pro + a);  // NaN var s = 'hello';
s.pro = 'world';
console.log(s.pro + s)  // undefinedhello

变量a与s都是基本类型,没法给他们添加属性,因此a.pro和s.pro都是undefined。

undefined + 10 获得NaN(not a number)。

undefined + 'hello' 获得undefinedhello,其中undefined被转化为字符串类型。

 

若是实在想给字符串添加属性,咱们须要将字符串定义为对象类型的字符串,以下:

var a= new String('objectString')
a.pro = "aaaaaaa"
console.log(a.pro)    // aaaaaaa

 

 

五、async与await的执行

async function sayHello() {
  console.log('Hello')
  await sleep(1000)
  console.log('world!')
}
function sleep(ms) {
   return new Promise(resolve =>  {
  console.log("666666");
  setTimeout(resolve, ms);
  console.log("888888")})
}
sayHello()  // hello 666666 888888 world!

 

async 表示这是一个async函数,await只能用在这个函数里面。

await 表示在这里等待promise返回结果了,再继续执行。

 

 

首先打出hello,到了await,会等待promise的返回,因此“world”不会马上打出,接着进入sleep函数,打出666,接着开了一个1秒的定时器,虽然js是单线程的,但setTimeout是异步的,在浏览器中,异步操做都是被加入到一个称为“events loop”队列的地方,浏览器只会在全部同步代码执行完成以后采起循环读取的方式执行这里面的代码,因此resolve被加入任务队列,先打印了888,一秒后执行了resolve,表示promise成功返回,打出了world。

 

 

以上每道题都是本渣本身的想法和理解,若有不正确的地方烦请读者指正,大佬轻喷~

相关文章
相关标签/搜索