今天在网上做了阿里的前端笔试题,题不多也不是很难,但是没有做完,还是不够熟练啊!下面来回顾一下题目。

选择题

  • 第一题问哪个单位不是CSS里的长度单位。选项中出现了 mm、pt、px、pc、%、rem等,其中rem应该不是CSS里的长度单位。

  • 第二题问考的是CSS,margin简写的顺序,已知:margin:5px 15px 25px 10px; 问左边距是多少。

  • 第三题问是关于事件的,主要靠mouseenter、mouseover的区别,我记得mouseenter是不冒泡的,且当鼠标从元素只有用的子元素进入元素不会触发mouseenter事件;而mouseover是会冒泡的,且鼠标从元素只有用的子元素进入元素不会触发mouseover事件。

  • 有一题考的是HTML5语义化,这个平时不是很注意。问time、date、mark、figure标签哪个不是H5的语义化标签。网上看了一下除了第二个date其余都是HTML5语义化标签。

选择就记得这么几个,另外记得两个填空题。

填空题

第一题是数组排序有关的,第二题是将两个字符串合并,并且颠倒顺序。

  • 第一个填空题:
    请将下面的数组从小到大排列

    1
    2
    3
    4
    var arr = [6,2,10,5,9,5];
    arr.sort(________);
    //我的答案:
    function ( a, b ) { return a - b; }
  • 第二个填空题:
    将两个字符串合并,并且颠倒顺序

    1
    2
    3
    4
    5
    6
    str1 = 'abcd';
    str2 = 'jjhhgg';
    str3 = str1.______(str2);
    str4 = str3.______('').reverse().concat('');
    //我的答案:
    concat和split //但是这个得到的结果是一个顺序颠倒的数组。
  • 第三题是考的是构造函数,题具体记不清了,记得这个题答错了。

编程题

一共记得三个编程题,最后一题比较简单,实现一个表格,要求表格奇数行背景为白色,偶数行背景为灰色,hover效果背景为黄色。下面是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//css
tr:nth-child(odd){
background-color: #fff;
}
tr:nth-child(even){
background-color: #999;
}
tr:hover{
background-color: #ffff00;
}
//html
<table>
<tbody>
<tr>
<td>11</td>
<td>12</td>
<td>13</td>
</tr>
<tr>
<td>21</td>
<td>22</td>
<td>23</td>
</tr>
<tr>
<td>31</td>
<td>32</td>
<td>33</td>
</tr>
<tr>
<td>41</td>
<td>42</td>
<td>43</td>
</tr>
</tbody>
</table>

另外一个大题是考JavaScript的,要求写一个 shuffle 程序,一开始不知道是个什么东西,其实就是将一个数组打乱。
可惜最后有了思路,就差一分钟没写完。我的思路是每次产生一个在数组长度内的随机正整数,然后从数组中取出这个随机数对应位置的元素存到一个临时数据中,最后将取出的元素从原数组中删除。下面是我实现的代码:

1
2
3
4
5
6
7
8
9
10
var arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
function shuffle (arr) {
var temp = [];
var len = arr.length;
for ( var i = 0; i < len; i++ ) {
temp[temp.length] = arr.splice( Math.floor(Math.random()*arr.length), 1 )[0];
}
return temp;
}
console.log( shuffle(arr) );

倒数第三道大题,问click时间在移动端和桌面端有什么区别,并给出解决方案。这个题完全不会啊!因为根本没有看过移动端的内容。希望知道答案的留个言,谢谢。

一共11个题,记住了9个。

vedio标签

在html5中加入了许多新的标签,video就是其中的一个。这个标签使我们可以像插入图片一样的插入视频。下面是一个插入视频的简单代码:

1
2
3
<video src="movie.mp4" controls="controls">
您的浏览器不支持 video 标签。
</video>

虽然主流浏览器都已经支持了video标签,但是很多情况为了兼容低版本的浏览器还是很麻烦。

兼容地板本浏览器

etianen开发了一个兼容video标签的js插件。插件地址:https://github.com/etianen/html5media。
使用该插件很简单,只需要在head中插入下面的代码就可以使用video标签了。

1
<script src="http://api.html5media.info/1.1.8/html5media.min.js"></script>

我们可以像下面这样插入一个视频:

1
<video src="video.mp4" width="320" height="200" controls preload></video>

该插件也支持audio标签:

1
<audio src="audio.mp3" controls preload></audio>

闭包的三种实现

  • with语句

    1
    2
    3
    with(obj){
    //这里是对象闭包
    }
  • 函数体

    1
    2
    3
    (function(){
    //函数闭包
    })()
  • catch语句

    1
    2
    3
    4
    5
    try{
    //...
    } catch(e) {
    //catch闭包 但IE里不行
    }

一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function User( properties ) {    
//这里一定要声明一个变量来指向当前的instance
var objthis = this;
for ( var i in properties ) {
(function(){
//在闭包内,t每次都是新的,而 properties[i] 的值是for里面的
var t = properties[i];
objthis[ "get" + i ] = function() {return t;};
objthis[ "set" + i ] = function(val) {t = val;};
})();
}
}

//测试代码
var user = new User({
name: "Bob",
age: 44
});

alert( user.getname());//BOb
alert( user.getage());//44

user.setname("Mike");
alert( user.getname()); //Mike
alert( user.getage()); //44

user.setage( 22 );
alert( user.getname()); //Mike
alert( user.getage()); //22

闭包使用案例

让下面这三个节点的Onclick事件都能正确的弹出相应的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<ul>
<li id="a1">aa</li>
<li id="a2">aa</li>
<li id="a3">aa</li>
</ul>

<script type="text/javascript">

for(var i=1; i < 4; i++){
var id = document.getElementById("a" + i);
id.onclick = function(){
alert(i);//现在都是返回4
}
}
</script>

在上面这段代码中,通过for循环为三个li元素注册了click事件。在循环结束后i的值4,这个值是个全局变量。当触发click事件时,处理函数返回的是全局对象i,这个值与事件注册时的i值比已经发生了改变,所以实际返回值都是4.

解决方法:

  1. 使用闭包函数
    1
    2
    3
    4
    5
    6
    7
    8
    var lists = document.getElementsByTagName("li");
    for(var i=0,l=lists.length; i < l; i++){
    lists[i].onclick = (function(i){//保存于外部函函数
    return function(){
    alert(i);
    }
    })(i);
    }


1
2
3
4
5
6
7
8
9
var lists = document.getElementsByTagName("li");
for(var i=0,l=lists.length; i < l; i++){
lists[i].onclick = new function(){
var t = i;
return function(){
alert(t+1)
}
}
}

  1. 利用事件代理

    1
    2
    3
    4
    5
    6
    7
    8
    var ul = document.getElementsByTagName("ul")[0];
    ul.onclick = function(){
    var e = arguments[0] || window.event,
    target = e.srcElement ? e.srcElement : e.target;
    if(target.nodeName.toLowerCase() == "li"){
    alert(target.id.slice(-1))
    }
    }
  2. 将暂时变量保留于元素节点上

    1
    2
    3
    4
    5
    6
    7
    var lists = document.getElementsByTagName("li");
    for(var i=0,t=0,el; el = list[i++];){
    el.i = t++
    el.onclick = function(){
    alert(this.i)
    }
    }
  3. 使用with语句造成的对象闭包

    1
    2
    3
    4
    5
    var els = document.getElementsByTagName("li")
    for(var i=0,n=els.length;i<n;i++){
    with ({i:i})
    els[i].onclick = function() { alert(this.innerHTML+i) };
    }
  4. 使用try…catch语句构造的异常闭包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var lists = document.getElementsByTagName("li");
    for(var i=0,l=lists.length; i < l; i++){
    try{
    throw i;
    }catch(i){
    lists[i].onclick = function(){
    alert(i)
    }
    }
    }

作用域

JavaScript变有两种变量:全局变量和局部变量。
最外面的函数外面是全局作用域,函数内的是局部作用域。
在函数内可以读取全局变量:

1
2
3
4
5
var n=999;
function f1(){
  alert(n);
}
f1(); // 999

而在函数外面不能读取函数内的局部变量:

1
2
3
4
function f1(){
  var n=999;
}
alert(n); // error

此外函数内部给变量赋值的时候,一定要使用var命令声明。如果不用的话,你实际上声明了一个全局变量!

从外部读取局部变量的方法

只要在函数f1内定义一个内部函数f2,f1返回函数f2,而函数f2可以读取函数f1内的局部变量,只要通过f2就可以在函数f1外面读取函数函数f1局部作用域内的局部变量。

1
2
3
4
5
6
7
8
9
function f1(){
  var n=999;
  function f2(){
    alert(n);
  }
  return f2;
}
var result=f1();
result(); // 999

闭包的概念

上一节代码中的f2函数,就是闭包。

各种专业文献上的”闭包”定义非常抽象,很难看懂。可以认为闭包就是能够读取其他函数内部变量的函数。

由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成”定义在一个函数内部的函数”。

闭包的用途

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

怎么来理解这句话呢?请看下面的代码。

1
2
3
4
5
6
7
8
9
10
11
12
function f1(){
  var n=999;
  nAdd=function(){n+=1}
  function f2(){
    alert(n);
  }
  return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另一个值得注意的地方,就是”nAdd=function(){n+=1}”这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

使用闭包的注意点

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

思考题

如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。

代码片段一

1
2
3
4
5
6
7
8
9
10
var name = "The Window";
var object = {
  name : "My Object",
  getNameFunc : function(){
    return function(){
      return this.name;
    };
  }
};
alert(object.getNameFunc()()); //The Window

代码片段二

1
2
3
4
5
6
7
8
9
10
11
var name = "The Window";
var object = {
  name : "My Object",
  getNameFunc : function(){
    var that = this;
    return function(){
      return that.name;
    };
  }
};
alert(object.getNameFunc()()); //My Object

参考:学习Javascript闭包(Closure)

  1. 首先要确保本机sublime已经有安装包管理器,如果没有,安装方法:http://blog.chinaunix.net/uid-12014716-id-4269991.html 文中的第一步:安装包管理器;
  2. 安装乱码处理插件:
    调用ctrl+shift+p,输入:install package,回车,在稍后弹出的安装包框中搜索:ConvertToUTF8或者GBK Encoding Support,选择点击安装;
    从此带有中文的文件打开就不会有乱码了。