**************************************************我是华丽的分割线**********************************************************

 

在博客园看到的这个问题,也想做一下。题目是这样的

房间里有100盏电灯,编号为1,2,3……100,每盏灯上有一个按钮,初始时灯全都是关的。编好号的100位同学由房间外依次走进去,将自己编号的倍数的灯的按钮全部按一次,例如第一位同学把编号是1的倍数的灯的按钮按一下(此时100盏灯全亮),第二位同学把编号是2的倍数的灯的按钮按一下(此时只有50盏灯亮着,50盏被这个人按灭了)……第100位同学把编号是100的倍数的灯(即编号为100的灯)的按钮按一下,请问依次走完后,还有多少盏灯亮着?

最简单的做法就是模拟一下100个同学进入房间的情景,从1号同学开始每个人都去按一遍开关,最后看一下哪些灯亮着,这个方法最简单也最容易想到,可是效率有点低。

 

 

  let count = {
    // 所有的灯
    getData: function () {
      let arr = [];
      for (let i = 1; i <= 100; i++) {
        arr.push(false);
      }
      return arr;
    },
    // 每个人都去操作自己能够操作的灯泡
    changeData: function () {
      let arr = this.getData();
      for (let i = 1; i <= 100; i++) {
        for (let j = 1; j <= 100; j++) {
          // 如果灯泡 去 取余   若是等于0  则表示可以去操作
          if ((j % i) === 0) {
            let index = j - 1;
            arr[index] = !arr[index];
          }
        }
      }
      return arr;
    },
    // 计算是true 的数据
    getTrueData: function () {
      let trueData = this.changeData().filter(item => item === true);
      console.log('亮灯泡的编号数组 :', trueData);
      console.log('亮灯泡的编号数组长度 :', trueData.length);
    }
  };
  count.getTrueData();

网上说其实可以计算一下每个开关被按下的次数,因为刚开始的时候所有的灯泡都是关着的,所以如果某个灯泡的开关被按了奇数次,那么这个灯泡最终就是开着的,否则就是关着的。那怎么计算每个灯泡开关被按下的次数呢?因为每个同学都会按下自己编号倍数的开关,即如果灯泡编号是同学编号的倍数,他就会按下开关,也就是说如果同学编号是灯泡编号的约数,他就会按下开关(这不是废话么)。那么统计一下灯泡编号约数的个数就可以了,约数个数为计数的灯泡最后开着,约数个数为偶数的灯泡最后关着。代码如下,(但是并没看出效率在那)

 let lamp = {
    main: function () {
      let result = 0;
      for (let i = 1; i <= 100; i++) {
        if (this.isOdd(this.getFactorNum(i))) {
          result += 1;
        }
      }
      console.log('return result :', result);
      return result;
    },
    //求n约数的个数
    getFactorNum: function (n) {
      let result = 0;
      for (let i = 1; i <= n; i++) {
        if (n % i === 0) {
          result += 1;
        }
      }
      return result;
    },
    //判断n是否为奇数
    isOdd: function (n) {
      return (n & 1) == 1;
    }
  }
  lamp.main(); // return result : 10

 

 又想了一下还有更简单的算法,因为一个数的约数都成成对出现的,也就是说如果n存在一个约数p,那么一定有一个q与之相对应且满足n=pq,所以n约数的个数一定是偶数,但是有一种情况例外,那就是p=q,所以只有编号为完全平方数的灯泡亮着。按着这个方法计算就简单多了

  let array = {
    getdata: () => {
      let arr = [];
      for (let i = 1; i <= 100; i++) {
        arr.push(i);
      }
      return arr;
    },
    trueData: function () {
      console.log('trueData :', this.getdata().filter(item => Math.sqrt(item).toString().indexOf('.') === -1));
      return this.getdata().filter(item => Math.sqrt(item).toString().indexOf('.') === -1);
    }
  }
  array.trueData(); // getFactorNum : (10) [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

 若是有问题请大佬赐教.

**************************************************我是华丽的分割线**********************************************************

 我们来一起看看来看那道大家再熟悉不过的前端面试题:

for (var i = 1;i <= 5;i ++) {

  setTimeout(function timer() {

      console.log(i)

  },i * 1000)

}

 问输入什么?

我想刚入门的童鞋或者对JS作用域闭包以及事件循环等概念不了解的童鞋会想当然的认为这道题的答案应该是:
第一次循环,隔一秒输出1;
第二次循环,隔两秒输出2;
第三次循环,隔三秒输出3;
第四次循环,隔四秒输出4;
第五次循环,隔五秒输出5;
或者还有同学预期的结果是分别输出数字1~5,每秒一次,每次一个。

但实际结果大家去控制台打印了都知道:以一秒的频率连续输出五个6!
相信对于很多童鞋第一次看到这个结果是懵的,包括我第一次看到结果是懵逼的!

然而还没等你反应过来,面试官又要求你改动一下代码,要它以一秒的频率分别输出1,2,3,4,5。如果你不了解或者没有深入理解JS中的作用域、闭包以及事件循环,那么就可以和面试官说拜拜了。

这道题涉及到的知识点 :作用域,闭包,事件循环.

for循环时setTimeout()不是立即执行的,它们的回调被push到了宏任务队列当中,而在执行任务队列里的回调函数时,变量i早已变成了6。那如何得到想要的结果呢?很简单,原理就是需要给循环中的setTimeout()创建一个闭包作用域,让它执行的时候找到的变量i是正确的。

下面给出5个解决方案🌰

(1)引入IIFE(立即调用的函数表达式)

for(var i = 0;i<5;i ++) {
  (function(i){
    setTimeout(function timer() {
      console.log(i)
    }, i * 1000);
  })(i);
}

(2)利用ES 6引入的let关键字

for(let i = 0;i<5;i++) {
  setTimeout(function timer(){
    console.log(i);
  }, i * 1000);
}

for 循环头部的let 声明还会有一个特殊的行为。这个行为指出变量在循环过程中不止被声明一次,每次迭代都会声明。随后的每个迭代都会使用上一个迭代结束时的值来初始化这个变量。

(3)利用ES 5引入的bind函数

for (var i=1; i<=5; i++) {
  setTimeout( function timer(i) {
    console.log(i);
  }.bind(null,i), i*1000 );
}

(4)利用setTimeout第三个参数

for (var i=1; i<=5; i++) {
  setTimeout( function timer(i) {
    console.log(i);    
   }, i*1000,i );
}

注:setTimeout函数第三个参数及以后的参数都可以作为timer函数的参数。

(5)把setTimeout用一个方法单独出来形成闭包

var loop = function (i) {
  setTimeout(function timer() {
    console.log(i);  
  }, i*1000);
};
for (var i = 1;i <= 5; i++) {
  loop(i);
}

**************************************************我是华丽的分割线**********************************************************

问, [] ==![] 返回什么?

这个题的考查js的基础知识

'!' 布尔操作符,如果操作数是一个空字符串,返回false.  所以 ![]  是false.

'==' 相等操作符,如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值---false 转换为0,而true转换为1.

所以上面的题可以一步一步的转换

  第一步: [] == ![]    因为有布尔操作符 转换为  [] == false 

  第二步: 因为有false 布尔值 .所以 要将其转换为 数值   [] 转为 0 , false 转为 0  .最终 0 == 0

  比较得为 true, 所以得出 [] ==![] 返回 true.

   整理一下相等操作符遵循的基本规则: (只是==,若是=== 则不会进行默认转换)

    • 如果有一个操作符是布尔值,则在比较相等性之前先将其转换为数值----- false 转换为 0 ,而true 转为 1 ;
    • 如果一个操纵符是字符串, 另一个操作符是数值,在比较相等性之前先将字符串转换为数值;
    • 如果一个操作数是对象,另一个操作数不是,则调用对象的vaOf() 方法,用得来的基本类型值按照之前的规则在进行比较;
    • null 和 undefined 是相等的;
    • 要比较相等性之前不能将null 和 undefined 转换为其他任何值;
    • 如果有一个操作数是NaN,则相等操作符返回false,而不相等操作符返回true。重要提示:即使两个操作数都是NaN,相等操作符也返回false。
    • 如果两个操作数都是对象,则比较他们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true;

**************************************************我是华丽的分割线**********************************************************

posted on 2018-12-25 18:04  xzqyun  阅读(262)  评论(0编辑  收藏  举报