发表于: 2017-04-10 22:09:59

1 1152


今天完成的事情:(一定要写非常细致的内容,比如说学会了盒子模型,了解了Margin

 理解JS数组乱序

JS的键盘事件

明天计划的事情:(一定要写非常细致的内容) 

 JS根据需要显示和隐藏视图;

 JS对DOM样式和内容进行更复杂的操作

遇到的问题:(遇到什么困难,怎么解决的) 

 如何监视键盘输入?

<!-- 监视键盘事件 -->
<!DOCTYPE html>
<html>
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <meta http-equiv="X-UA-Compatible" content="ie=edge">
   <title>监视键盘事件</title>
</head>
<body onkeydown="keyEvent(event)" onkeyup="metaKeyUp(event)">
</body>
<script>
   var metaChar = false;
   var exampleKey = 16;
   function keyEvent(event) {
       var key = event.keyCode || event.which;
       var keychar = String.fromCharCode(key);
       if (key == exampleKey) {
           metaChar = true;
       }
       if (key != exampleKey) {
           if (metaChar) {
               alert("Combination of metaKey + " + keychar);
               metaChar = false;
           } else {
               alert("Key pressed " + key);
           }
       }
   }
   function metaKeyUp(event) {
       var key = event.keyCode || event.which;
       if (key == exampleKey) {
           metaChar = false;
       }
   }
</script>
</html>



收获:(通过今天的学习,学到了什么知识)

理解JS数组乱序 

给你一个数组,将其打乱,返回新的数组,即为数组乱序,也称为洗牌问题。

一个好的方案需要具备两个条件,一是正确性,毋庸置疑,这是必须的,二是高效性,在确保正确的前提下,如何将复杂度降到最小,是我们需要思考的。

当原数组a还有元素,每次 random 一个下标, 将原数组的元素存入新数组,并从原数组删除,直到所有元素都存入新数组中。

function shuffle(a) {
   var b =[];
   while (a.length) {
       var index = ~~(Math.random() * a.length);//~是二进制的按位取反,~~是取整的简写
       b.push(a[index]);//在数组b的末尾增加一个(原数组a索引1index的元素),并返回数组的新长度。
       a.splice(index, 1);//删除原数组a的索引(index)元素。
   }
   return b;
}


这个解法的正确性是没有问题。我们假设乱序 1w 次,并且将结果做成了图表,猛戳 http://hanzichi.github.io/test-case/shuffle/splice/ 查看,结果还是很乐观的。验证了正确性,还要关心一下它的复杂度。由于程序中用了 splice,如果把 splice 的复杂度看成是 O(n),那么整个程序的复杂度是 O(n^2)。


  1. 另一个为方法是 "巧妙应用" JavaScript 中的 Math.random() 函数。

function shuffle(a) {
   return a.concat().sort(function(a, b) {//返回一个由当前数组a随机乱序的新数组。
       return Math.random() - 0.5;
   });
}

同样是 [0, 1, 2 ... 10] 作为初始值,同样跑了 1w 组 case,结果请猛戳 http://hanzichi.github.io/test-case/shuffle/Math.random/

看平均值的图表,很明显可以看到曲线浮动,而且多次刷新,折现的大致走向一致,平均值更是在 5 上下 0.4 的区间浮动。如果我们将 [0, 1, 2 .. 9] 作为初始数组,可以看到更加明显不符预期的结果(有兴趣的可以自己去试下)。究其原因,要追究 JavaScript 引擎对于 Math.random() 的实现原理,这里就不展开了(其实是我也不知道)。因为 ECMAScript 并没有规定 JavaScript 引擎对于 Math.random() 应该实现的方式,所以我猜想不同浏览器经过这样的乱序后,结果也不一样。

什么时候可以用这种方法乱序呢?"非正式" 场合,一些手写 demo 需要乱序的场合,这不失为一种 clever solution。

但是这种解法不但不正确,而且 sort 的复杂度平均下来应该是 O(nlogn),跟我们接下来要说的正解还是有不少差距的。

关于数组乱序,最佳的解法是 Fisher–Yates Shuffle,复杂度 O(n)。遍历数组元素,将其与之前的任意元素交换。因为遍历有从前向后和从后往前两种方式,所以该算法大致也有两个版本的实现。

从后往前的版本:

function shuffle(array) {
   var _array = array.concat();
   for (var i = _array.length; i--; ) {
       var j = Math.floor(Math.random() * (i + 1));
       var temp =_array[i];
       _array[i] =_array[j];
       _array[j] =temp;
   }
   return _array;
}

underscore 中采用从前往后遍历元素的方式,实现如下:

// Shuffle a collection, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
_.shuffle= function(obj) {
   var set = isArrayLike(obj) ?obj : _.values(obj);
   var length = set.length;
   var shuffled = Array(length);
   for (var index = 0, rand; index < length; index++) {
       rand = _.random(0, index);
       if (rand !== index) shuffled[index] = shuffled[rand];
       shuffled[rand] =set[index];
   }
   returnshuffled;
};

将其解耦分离出来,如下:

function shuffle(a) {
   var length = a.length;
   var shuffled = Array(length);
   for (var index = 0, rand; index < length; index++) {
       rand = ~~(Math.random() * (index + 1));
       if (rand !== index)
           shuffled[index] =shuffled[rand];
       shuffled[rand] =a[index];
   }
   return shuffled;
}

跟前面一样,做了下数据图表,猛戳 http://hanzichi.github.io/test-case/shuffle/Fisher-Yates/

关于证明,引用自月影老师的文章

随机性的数学归纳法证明

对 n 个数进行随机:

  1. 首先我们考虑 n = 2 的情况,根据算法,显然有 1/2 的概率两个数交换,有 1/2     的概率两个数不交换,因此对 n = 2 的情况,元素出现在每个位置的概率都是 1/2,满足随机性要求。
  2. 假设有 i     个数, i >= 2 时,算法随机性符合要求,即每个数出现在 i 个位置上每个位置的概率都是 1/i。
  3. 对于 i +     1 个数,按照我们的算法,在第一次循环时,每个数都有 1/(i+1) 的概率被交换到最末尾,所以每个元素出现在最末一位的概率都是 1/(i+1)     。而每个数也都有 i/(i+1) 的概率不被交换到最末尾,如果不被交换,从第二次循环开始还原成 i 个数随机,根据 2. 的假设,它们出现在 i     个位置的概率是 1/i。因此每个数出现在前 i 位任意一位的概率是 (i/(i+1)) * (1/i) = 1/(i+1),也是 1/(i+1)。
  4. 综合 1.     2. 3. 得出,对于任意 n >= 2,经过这个算法,每个元素出现在 n 个位置任意一个位置的概率都是 1/n。

小结

关于数组乱序,如果面试中被问到,能说出 "Fisher–Yates Shuffle",并且能基本说出原理(你也看到了,其实代码非常的简单),那么基本应该没有问题了;如果能更进一步,将其证明呈上(甚至一些面试官都可能一时证明不了),那么就牛逼了。千万不能只会用 Math.random() 投机取巧!

Read More:

 

来自 <https://github.com/hanzichi/underscore-analysis/issues/15


JS的键盘事件

一、首先需要知道的是:

1、keydown()

keydown事件会在键盘按下时触发.

2、keyup()

keyup事件会在按键释放时触发,也就是你按下键盘起来后的事件

3、keypress()

keypress事件会在敲击按键时触发,我们可以理解为按下并抬起同一个按键

二、获得键盘上对应的ascII码:

$(document).keydown(function(event){
alert(event.keyCode);
});

$tips: 上面例子中,event.keyCode就可以帮助我们获取到我们按下了键盘上的什么按键,他返回的是ascII码,比如说上下左右键,分别是38,40,37,39;

三、实例(当按下键盘上的左右方面键时) 复制代码 代码如下:

$(document).keydown(function(event){
//判断当event.keyCode 为37时(即左方面键),执行函数to_left();
//判断当event.keyCode 为39时(即右方面键),执行函数to_right();
 if(event.keyCode == 37){
        to_left();
        }else if (event.keyCode == 39){
        to_right();
        }
        else if (event.keyCode == 38){
        to_top();
        }
        else if (event.keyCode == 40){
        to_bottom();
        }
});

function to_left(){ $(".abc").css({'left':'-=10'});}

function to_right(){ $(".abc").css({'left':'+=10'});}

function to_top(){$(".abc").css({'top':'-=10'});}

function to_bottom(){$(".abc").css({'top':'+=10'});}

 

来自 <https://segmentfault.com/a/1190000002405897




返回列表 返回列表
评论

    分享到