美文网首页
2018-04-26 面试题整理

2018-04-26 面试题整理

作者: 何学斌 | 来源:发表于2018-04-28 09:47 被阅读0次

    执行上下文、函数调用栈

    var a = 1;
    function A() {
      a = 3;
      return ;
      function a () { console.log(); };
    }
    A();
    console.log(a);
    

    聪明的你一定觉得这道题so easy,是吗?大笔一挥,答案a等于3.
    错!!!
    正确答案是1.
    函数A()执行时,创建函数A的执行上下文,此时在A的上下文中,因为函数声明提前,变量a被声明为一个函数对象,当代码执行阶段时,a被赋值为3,继续执行return ;函数A结束执行。而全局变量a并未被赋值修改,所以console.log(a);等于1.

    当调用一个函数时(激活),一个新的执行上下文就会被创建。而一个执行上下文的生命周期可以分为两个阶段。

    • 创建阶段 在这个阶段中,执行上下文会分别创建变量对象,建立作用域链,以及确定this的指向。
    • 代码执行阶段 创建完成之后,就会开始执行代码,这个时候,会完成变量赋值,函数引用,以及执行其他代码。 image.png

    变量、赋值

    let a = {v: 'temp'};
    let b = a;
    a = a.x = {v: 'temp'};
    console.log(a); 
    // {v: 'temp'}
    console.log(b);
    // {v: 'temp', x: {v: 'temp'}}
    

    说明:引用类型赋值时复制的是引用地址


    函数传参

    let a= 1;
    let b= {a: 1};
    function sum(m,n) {
        m= 2;
        n.a= 2;
    }
    sum(a,b);
    console.log(a);
    // 1
    console.log(b);
    // {a: 2};
    

    说明:函数的参数是值传递的,而引用类型赋值的是其引用的内存地址。所以sum函数内,m=1,n与b指向同一块内存地址,函数执行后,console.log(b)打印{a: 2}。


    函数调用栈和任务队列

    //请写出下面代码段的执行结果
    setTimeout( function () { console.log(0)},0);
    Promise.resolve().then( function () { console.log(5)});
    console.log(3);
    for( var i= 10; i< 15; i++) {
      setTimeout( function (){ console.log(i)}, i);
    }
    

    浏览器执行js代码永远是一个单线程,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。


    队列数据结构
    • 一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。
    • 任务队列又分为macro-task(宏任务)与micro-task(微任务),在最新标准中,它们被分别称为task与jobs。
    • macro-task大概包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
    • micro-task大概包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)
    • setTimeout/Promise等我们称之为任务源。而进入任务队列的是他们指定的具体执行任务。

    分析:
    浏览器执行js过程中,遇到setTimeout(fn,time)函数,调用浏览器定时线程计时,在time毫秒后将fn加入宏任务队列;
    浏览器继续执行后续代码,遇到Promise将.then(fn)中fn加入微任务队列;
    继续执行,打印3;
    for循环5次,定时器线程分别在10ms、11ms、12ms、13ms、14ms后将function () { console.log(i);}加入宏任务队列;
    for循环结束后,本次宏任务队列执行完毕,执行微任务队列中function () { console.log(5);},打印5;
    微任务队列执行完毕,继续执行宏任务队列,打印0, 15, 15, 15, 15, 15
    所以输出结果为:3, 5, 0, 15, 15, 15, 15, 15


    数组随机排序

    var a = [1,2,3,4,5,6,7,8,9,10];
    a.sort(function(a,b){ return Math.random() > 0.5;});
    

    Array.prototype.sort()
    语法
    arr.sort()
    arr.sort(compareFunction)

    • 参数
      compareFunction
      可选。用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的各个字符的Unicode位点进行排序。
    • 返回值
      返回排序后的数组。原数组已经被排序后的数组代替。
    • 说明
      如果没有指明 compareFunction ,那么元素会按照转换为的字符串的诸个字符的Unicode位点进行排序。例如 "Banana" 会被排列到 "cherry" 之前。当数字按由小到大排序时,9 出现在 80 之前,但因为(没有指明 compareFunction),比较的数字会先被转换为字符串,所以在Unicode顺序上 "80" 要比 "9" 要靠前。
      如果指明了 compareFunction ,那么数组会按照调用该函数的返回值排序。即 a 和 b 是两个将要被比较的元素:
      如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前;
      如果 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变。备注: ECMAScript 标准并不保证这一行为,而且也不是所有浏览器都会遵守(例如 Mozilla 在 2003 年之前的版本)
      如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 之前。
      compareFunction(a, b) 必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的。

    如上方法并不是真正意思上的乱序,一些元素并没有机会相互比较, 最终数组元素停留位置的概率并不是完全随机的。

    v8在处理sort方法时,使用了插入排序和快排两种方案。
    当目标数组长度小于10时,使用插入排序;反之,使用快速排序。
    其实不管用什么排序方法,大多数排序算法的时间复杂度介于O(n)到O(n²)之间,
    元素之间的比较次数通常情况下要远小于n(n-1)/2,
    也就意味着有一些元素之间根本就没机会相比较(也就没有了随机交换的可能),
    这些 sort 随机排序的算法自然也不能真正随机。
    其实我们想使用array.sort进行乱序,理想的方案或者说纯乱序的方案是数组中每两个元素都要进行比较,
    这个比较有50%的交换位置概率。这样一来,总共比较次数一定为n(n-1)。
    而在sort排序算法中,大多数情况都不会满足这样的条件。因而当然不是完全随机的结果了。
    作者:云中桥
    链接:https://juejin.im/post/5d004ad95188257c6b518056

    // Fisher–Yates算法,这个算法是由 Ronald Fisher 和 Frank Yates 首次提出的。
    // 这个算法其实非常的简单,就是将数组从后向前遍历,然后将当前元素与随机位置的元素进行交换。
    function shuffle(arr) {
        let m = arr.length;
        while (m > 1){
            let index = Math.floor(Math.random() * m--);
            [arr[m] , arr[index]] = [arr[index] , arr[m]]
        }
        return arr;
    }
    

    数组去重

    1. 利用数组的lastIndexOf()方法。
    arr= arr.filter( function (v,i,array) {
      if(array.lastIndexOf(v) === i) {
        return true;
      }
      return false;
    });
    

    filter不修改原数组,返回新数组。

    1. 利用ES6 新增Set对象实现
    //2.1
    arr = Array.from(new Set(arr));
    //2.2
    arr = [... new Set(arr)];
    
    1. 利用Object对象键名唯一性实现。
    var obj = {},
        del = [];
    arr.forEach(function (v) {
      if( !obj[typeof v+''+v]) {
        del.push(v);
        obj[typeof v+''+v] = 1;
      }
    });
    

    相关文章

      网友评论

          本文标题:2018-04-26 面试题整理

          本文链接:https://www.haomeiwen.com/subject/zvsplftx.html