Head First HTML5 Programming 学习笔记

1.HTML5 提供了本地存储,2D绘图,离线支持,套接字和线程等诸多特性。

2.问:这在老式的浏览器上也能用?就像这些新的doctype, meta之类的标记?老式浏览器怎么处理这些新语法呢?

   答:对,靠点聪明,也靠点运气。就拿link和script标记上的type属性为例,如今在HTML5中去掉这个属性是合理的,因为CSS和JavaScript现在已经成为标准(当然也是默认的样式和脚本样式)。不过,事实上,浏览器早已经假定默认使用CSS和JavaScript。所以标准一致,真是碰巧,浏览器多年前就已经对这个新标记标准提供了支持。doctype和meta标记也是一样。

3.问:那么新的doctype呢?看起来现在是不是太简单了?甚至连版本或DTD都没有。

  答:恩。看起来确实有些神奇,使用了这么多年复杂的doctype之后,现在我们居然可以简单到只指出“我们在用HTML”。事情是这样的:HTML原先基于一个名为SGML的标准,这个标准要求提供复杂格式的doctype和DTD。新标准对SGML有所调整,目的是简化HTML语言,使它更加灵活。所以我们不再需要复杂的格式。不仅如此,正如前面所说,这里还有点运气,几乎所有浏览器都只是在doctype中查找HTML,来确保在解析一个HTML文档。

4.问:你说它不会再变了,不是开玩笑吧?我觉得对于浏览器来说版本非常重要。为什么不使用< !doctype html5 >呢?将来还是有可能有HTML6的吧?

  答:doctype的使用意味着浏览器制造商要使用doctype告诉浏览器采用它们自己的“标准模式”表现内容。既然我们已经有了真正的标准,HTML5的doctype完全可以告诉所有浏览器这个文档是标准HTML,不论它是版本5还是版本6,或者是其他任何版本。

5.问:这很重要吗?我完全可以输入一个不带doctype和meta标记的页面,它也能很好地工作。我何必操心它是不是完全正确呢?

  答:是的,浏览器很擅长忽略HTML文件中的小错误。不过,通过包含正确的doctype和meta标记,就能确保浏览器确实知道你想要什么,而不用乱猜。另外,对于那些使用老式浏览器的人来说,新doctype意味着他们将使用标准模式,这正是你要的。要记住,标准模式是指:浏览器会认为你编写的HTML符合一个标准,所以它会使用相应的规则来解析你的页面。如果你没有指定一个doctype,有些浏览器可能会进入“古怪模式”,以为你的Web页面是为老式浏览器编写的,如果标准不符合,就可能不正确地解析你的页面(或者认为它编写得不正确)。

6.问:XHTML怎么了?好像好多年前就说它是未来发展方向。

  答:原先确实是。不过,后来灵活性超越了严格语法,在这个过程中,XHTML(准确地讲是XHTML 2)夭折了,从而人们编写Web页面的方式(以及浏览器表现页面的方式)来讲,HTML5更能让人接受。不过也不用担心,因为了解XHTML能让你更好地编写HTML5内容。顺便说一句,如果你真的喜欢XML,还有一种办法可以用严格的格式写HTML5.

7.HTML5是HTML的一个超集,对于那些老式的浏览器,我们的目标肯定是只写一个HTML页面,而不是写两个版本的Web页面。

8.HTML5的底层设计原则之一就是允许你的页面妥善地降级。这说明,如果用户的浏览器未能提供一个新特性,你就应当提供有一个有意义的候选功能。

9.问:Web页面和Web应用有什么不同吗?怎么才算是一个Web应用?

  答:这两个词之间没有绝对的差别;换句话说,要把一个用HTML,JavaScript和CSS编写的页面变成一个Web应用,并不需要你做任何特殊的工作。所以只是看法不同而已。

         如果一个页面表现得像是一个应用,而不只是一个静态文档,我们就可以认为它是一个Web应用,而不仅仅是Web页面。我们认为应用应该具备一些特点,比如维护大量状态,管理与用户复杂的交互,无需页面刷新就能显示不断更新的动态数据,或者甚至可以完成更复杂的任务或计算。

10.JavaScript命名方式,可以使用字母,美元符号和下划线创建变量名,不能有个空格,—,或 +,符号。

     例如:var  zip code;   var   first-name;     var  to+do;      不能这样命名

11.问:能从数组Array删除一个元素吗?如果能,其他元素的索引会有什么变化?

   答:当然可以从Array删除一个元素,而且有两种不同的方法来删除。可以将该索引的数组元素值设置为null。例如,myArray[2] = null。不过,这意味着Array的长度保持不变。或者还可以完全删除这个元素(使用函数splice)。在这种情况下,所删除元素后面的所有元素的索引都减1。所以,如果myArray[2] = "dog",myArray[3] = "cat",而你删除了"dog”,那么就会有myArray[2] = "cat",而且数组的长度比原来少1。

12.undefined和null是两个不同的值。undefined表示一个变量未赋值;null表示这个变量有一个空值。

13.Math.random会返回一个介于0和1的浮点数(但是不会为1)。

14.Math.floor把一个浮点数小数点后面的所有位去除,将它转换为一个整数。

15.以下是合法的语句

    var  s = "3-8";    var  n = 3 - "none";    var   $ = 21.30;

16.<input  type = "text"  id = "songTextInput"  size = "40"  placeholder = "Song  name" >,其中的placeholder指示这个输入域中要输入什么。

17.问:我向函数传递了一个变量——如果在函数中改变了相应行参的值,会不会也改变原来的变量?

   答:不会,传递一个基本类型值时,它会复杂到行参。我们把这称为“传值”。所以,如果在函数体中改变了行参的值,对原来的实参值没有任何影响。不过传递数组或对象是个例外。

18.问:掌握所有这些局部变量和全局变量的作用域很让人糊涂,那么为什么不都用全局变量呢?

  答:如果你写的代码很复杂,或者需要在很长一段时间之后维护,你就确实应当好好看一看如何管理变量。如果你过分热衷于创建全局变量,将很难跟踪到变量在哪里使用(以及在哪里对变量值做出了修改),而这可能会导致有bug的代码。与别人合作编写代码时,或者使用了第三方库时,这一点会更为重要(不过,如果这些库编写得很好,则它们应该会有合理的结构来避免这些问题)。所以说,可以在合适的地方使用全局变量,不过一定要适度,另外要尽可能使用局部变量。

19.要枚举一个对象的所有属性,我们使用一个for-in循环,注意属性的顺序是任意的,所以不能依赖一个特定的顺序。

20.可以向函数传入一个对象,就像传入其他变量一样。在函数中,可以正常地访问这个对象的属性,当然要使用行参名作为对象。

 function  bark( dog )

{

        if ( dog.weight > 25 )     alert ( "WOOF" );

        else           alert ( "yip" );

}

21.将一个对象赋至变量时,这个变量会包含这个对象的一个引用,而不是对象本身。可以把引用想象成是对象的一个指针。改变对象的一个属性时,你修改的是原对象的属性,而不是副本,所以不仅可以在函数内部看到对对象的所有改变,而且在函数外一样能看到所有改变。

22.window是个全局变量,所以即使没有在前面加上window,window的属性或方法名也能顺利解析。另外,你定义的所有全局变量都会放在window命令空间中,所以可以作为window.myvariable来引用。

23.JSON的可读性相当好,而且可以很快,很容易地解析为JavaScript对象。

     JSON.stringify();——解析为字符串

     JSON.parse();——转换为一个JavaScript对象

24.XMLHttpRequest不能发出跨域请求,跨域的服务器根本没有看到请求,在它看到请求之前,浏览器安全策略已经中止了这个请求。

25.由另一个域提供的JavaScript文件可以调用你的浏览器的一个函数。

26.从另一个域提供的JavaScript文件不仅可以在代码中调用它想要的任何函数,还可以为我们传递它希望的任何数据。我们在通过所引用的JavaScript文件传递数据,而不是使用XMLHttpRequest自己来获取。

27.JSONP是一种使用<script>标记获取JSON对象的方法。这也是一种获取数据的方法(同样的,采用JSON对象的形式),它可以避免XMLHttpRequest的同源安全问题。

28.JSON的工作方式

 

29.一般来讲,Web服务允许你指定希望如何命名函数。它的做法如下:指定URL时,在末尾增加一个参数,就像这样。

   <script src="http://gumball.wickedlysmart.com/?callback=updateSales"> </script>

  对方的服务器向你发回JSON对象之前,会用updateSales包装这个JSON格式化对象。一般地,Web服务将这个参数命名为callback,不过请查看你的Web服务文档,明确Web服务实际使用的参数名。

30.浏览器在加载页面时会进行控制,我们希望先加载页面,这样调用updateSales时就能更新DOM。要处理这个问题,我能想到的唯一办法就是把<script>放在页面HTML体的最下面。

31.应该记得,使用<script>元素时就是告诉浏览器需要获取JavaScript,完成解析并执行。这说明,它执行到你的uodateSales函数时,JSON已经不再是字符串形式了,而是一个真正的JavaScript对象。我们原先使用XMLHttpRequest时,数据采用字符串的形式返回。

32.编写代码,首先给出增加一个新script元素的代码,然后给出替换script的代码

 

 function handleRefresh()
        {
            var url = "http://gumball.wickedlysmart.com?callback=updateSales";
            var newScriptElement = document.createElement("script");
            newScriptElement.setAttribute("src", url);
            newScriptElement.setAttribute("id", "josnp");
            var oldScriptElement = document.getElementById("jsonp");
            var head = document.getElementsByTagName("head")[0];
            if (oldScriptElement == null) {
                head.appendChild(newScriptElement);
            }
            else {
                head.replaceChild(newScriptElement, oldScriptElement);
            }
        }

 

33.JSON还有一个优点,它直接基于JavaScript,而且JSONP能帮我们绕过跨域问题。

34.大多数浏览器都有一个有趣的特性,如果你反复地获取同一个URL,浏览器为了提高效率把它缓存起来,所以你会反复地得到同样的缓存文件。这不是我们想要的。很幸运,对于这个问题,有一个像Web一样老的简单“疗法”。我们只需要在URL的末尾增加一个随机数,就会诱使浏览器认为它是以前从来没有见过的一个新URL。例如:

 var url = "http://gumball.wickedlysmart.com?callback=updateSales" + "&random" + (new Date()).getTime();


35.JSONP并不比使用<script>元素链接到JavaScript库的做法更安全(或更不安全)。只要链接第三方JavaScript都要特别当心。

36.要指定<script>元素做出JSONP请求,可以把它直接增加到HTML,或者使用JavaScript将<script>元素写至DOM。

37.如果做出多次请求,可以在JSONP请求URL的末尾使用一个随机数,使浏览器不会缓存这个响应。

38.replaceChild方法会用另一个元素替换DOM中的一个元素。

39.问:为什么不用Flash,或者一个定制应用呢?

  答:Flash是一个很优秀的技术,你当然可以使用这个技术。不过,要知道现在整个行业都在转向HTML5。你要想在所有设备上运行你的Flash应用,可能会遇到麻烦,甚至包括一些非常流行的设备。如果你真的需要一种完全为设备定制的体验,实现定制应用可能是一个很好的选择。但是要记住,为各种设备分别开发一个定制应用成本可能很昂贵。利用HTML5,你可以得到移动设备和桌面浏览器的广泛支持,而且通常使用一种技术方案就可以创建普遍适合的应用。

40.我可以使用CSS设置画布的宽度和高度吗?而不是利用元素的width和height属性?

  答:这是可以的,不过它的做法可能与你想象中稍有出入。默认的,画布元素是300像素宽,150像素高。如果未在canvas标记中指定width和height属性,就会得到这个大小的画布。如果接下来在CSS中指定了一个大小,比如600px × 200px ,那么原来的300 × 150的画布就会扩展到这个大小,所以画布中绘制的所有内容也会随之扩展。这就行为一个图像指定新的宽度和高度时,如果比原图像的实际宽度和高度更大或更小时,就会将图像拉伸或缩小。如果将它放大,就会在图像中得到像素化的效果,对不对?画布也是一样。300px宽的画布变成600px宽时,同样数目的像素会扩展为原来大小的两倍,所以看起来会变得矮矮胖胖。不过,如果使用元素的width和height属性,会把画布的大小设置得比300 × 150更大(更小),但画布上的所有内容仍会正常绘制。所以我们建议还是在标记属性中指定宽度和高度,而不要在CSS中设置这些属性,除非你本来就打算缩放画布。

41.获取下拉框的值:查找选择了哪个元素,为此得到所选元素的索引,并把它的值赋给变量shape。

 

var selectObj = document.getElementById("shape");
var index = selectObj.selectedIndex;
var shape = selectObj[index].value;


42.<video>标签中属性poster的内容是不播放电影时显示的海报图片,可选属性type是向浏览器提供了一个提示,帮助它确定能不能播放这种类型的文件,codecs参数指定使用哪个编解码器对视频和音频编码,来创建编码的视频文件,注意单双引号。

 

    <video id="video" controls autoplay src="video/love.mp4" width="520" height="520" poster="images/love.png"></video>
    <video id="video" controls autoplay width="520" height="520" poster="images/love.png">
        <source src="video/love.mp4" type='video/mp4; codecs = "avc1.42E01E, mp4a.40.2"'   />
        <source src="video/love.webm" type='video/webm; codecs = "vp8, vorbis"' />
        <source src="video/love.ogv" type='video/ogv; codecs = "theora, vorbis"' />
        <p>Sorry, your browser doesn't support the video element.</p>
    </video>


43.编写代码处理视频像素,并把处理后的像素放入画布中显示,注意创建缓冲区。

 

 function processFrame()
        {
            var video = document.getElementById("video");
            //查看视频是否仍在播放。如果没有播放,说明没有工作可做,直接返回
            if(video.paused || video.ended)
            {
                return;
            }
            
            var bufferCanvas = document.getElementById("buffer");
            var displayCanvas = document.getElementById("display");
            var buffer = bufferCanvas.getContext("2d");
            var display = displayCanvas.getContext("2d");
            //drawImage方法,它取一个图像,把这个图像绘制到画布上,使它位于x , y位置,并有指定的宽度和高度
            //我们从视频得到一个图像。将视频指定为源,drawImage回得到一个视频源作为图像数据
            buffer.drawImage(video, 0, 0, bufferCanvas.width, bufferCanvas.height);
            //然后从画布上下文获取图像数据,把它存储在一个变量frame中,以便处理
            var frame = buffer.getImageData(0, 0, bufferCanvas.width, bufferCanvas.height);
            //首先得出数据帧的长度。注意:数据放在帧的一个属性frame.data中,每个像素都要4个值:RGBA
            var length = frame.data.length / 4;

            for (var i = 0; i < length; i++)
            {
                var r = frame.data[i * 4 + 0];
                var g = frame.data[i * 4 + 1];
                var b = frame.data[i * 4 + 2];
               //省略其他操作
            }

            //到了这里,帧数据已经得到处理,所以我们使用上下文的putImageData方法将这个数据放在显示画布上
            //这个方法取帧中的数据,把它写到画布上指定的x, y位置
            display.putImageData(frame, 0, 0);
        }


44.本地存储

 localStorage.setItem("sex", "boy");
 localStorage.getItem("sex");
 localStorage["sex"] = "girl";
 var girl = localStorage["sex"];

45.问:提到的每个浏览器上有5MB的存储空间,这5MB是所有应用共用的吗?

  答:不,实际上是每个域有5MB。

46.所有存储空间都在客户端管理。引入域是因为5MB会分配给来自同一个域的所有页面作为存储空间。www.baidu.com得到5MB;www.google.com得到5MB等等,所有这些都在你的机器上。

47.问:如果从我的计算机加载一个页面,那么我的数据源是什么?

  答:在这种情况下,你的数据源称为“本地文件”源,这很适合用来进行测试。如果你可以访问服务器,也可以在服务器测试你的文件,这种情况下数据源就是服务器的域。

48.一个域(如:apple.com)的所有页面会看到这个域中其他页面存储的各个数据项。这说明,如果我们对键的命名不在意,可能会与另一个采用不同方式使用相同键的页面发生冲突。所以,我们利用这种方法进行检查,在用它的值增加即时贴之前,先确保一个数据项确实是即时贴(而不是一个顺序号或者一个游戏等级)。

49.问:我刚刚重新加载了我的页面,现在即时贴顺序居然不一样了!

  答:增加一个新的便利贴时,我们的做法是把它追加到便利贴列表的最后,所以它总是位于列表的末尾。重新加载页面时,这些便利贴会按在localStorage中找到的顺序来增加(要记住,不能保证采用某种特定顺序)。你可能认为这个顺序就是数据项增加到本地存储时采用的顺序,或者另外某种合理的顺序,不过,不能指望这一点。为什么呢?一个原因是规范并没有指定一个顺序,所以不同浏览器可能会以不同的方式实现这个顺序。如果你的浏览器确实按你可以理解的顺序返回数据项,那你确实很幸运,不过不能依赖于这个顺序,因为你的用户的浏览器可能会按不同的方式确定数据项的顺序。

50.JavaScript有很多好处,其中一条:它一次只能做一件事情。这就像我们所说的“单线程”。为什么这算一件好事呢?因为这样一来,编程就很简单。倘若同时执行大量线程,要想编写一个能正确工作的程序可能很有难度。

51.创建Web工作线程

 window.onload = function ()
        {
            //创建新的工作线程,“worker.js”,JavaScript文件中包含这个工作线程的相应代码
            var worker = new Worker("worker.js");
            var worke2 = new Worker("worker.js");
            var worke3 = new Worker("worker.js");
            var another_worker = new Worker("another_worker.js");
            //使用工作线程的postMessage方法向它发送一个消息。我们的消息是一个简单的字符串“ping”
            worker.postMessage("ping");
            //在这里,我们定义了一个函数,只要从这个工作线程接收到一个消息就会调用这个函数。来自工作线程的消息包装在一个事件对象中
            //传入处理程序的事件对象有一个data属性,其中包含工作线程提交的消息数据
            //从工作线程得到一个消息时,把它放在HTML页面上的一个元素中
            worker.onmessage = function (event) {
                var message = "Worker says " + event.data;
                document.getElementById("output").innerHTML = message;
            };
        }

        //下面为worker.js增加以下代码
        //将工作线程的onmessage属性赋为pingpong函数,要编写pingpong函数来处理所有到来的消息
        //工作线程从主代码接收一个消息时,会调用pingpong函数,并传入这个消息
        onmessage = pingpong;
        //如果这个消息中包含字符串“ping”,就发回一个消息“pong”。工作线程的消息将返回给创建这个工作线程的代码
        //主要工作线程也使用postMessage来发送消息
        function pingpong(event)
        {
            if (event.data == "ping")
            {
                postMessage("pong");
            }
        }


52.问:创建工作线程时能不能直接传递一个函数,而不是利用一个JavaScript文件?这样看起来好像更容易,而且与JavaScript通常的做法更一致。

  答:不,不能这么做。下面来告诉你为什么,要知道,工作线程的要求之一是它不能访问DOM(出于同样的原因,也不能访问主浏览器线程的任何状态)。如果可以向Worker构造函数传入一个函数,这个函数可能还包含DOM或主JavaScript代码的引用,这就会违反这个要求。所以,为了避免这个问题,Web工作线程的设计者选择的做法是只能传递一个JavaScript  URL。

53.问:在消息中向工作线程发送一个对象时,它会不会成为主页面和工作线程之间的一个共享对象?

  答:不会的,发送一个对象时,工作线程会得到这个对象的一个副本。工作线程做出的任何修改都不会影响主页面中的对象。工作线程在另一个环境中运行,这与页面所在的环境不同,所以你不能访问主页面所在环境中的对象。工作线程发出的对象也是如此,主页面只能得到工作线程所发送对象的一个副本。

54.问:工作线程可以访问localStorage或作出XMLHttpRequest请求吗?

  答:这是可以的。工作线程可以访问localStorage,也可以做出XMLHttpRequest请求。

55.利用HTML5,则有了一种从DOM选择元素的新方法,这是受jQuery的启发。现在通过使用document.querySelector方法,可以在JavaScript中使用CSS的选择器来选择DOM中的元素。例如:

 document.querySelector("#avatar");
 document.querySelector("p.level3");
 document.querySelector(".content>p");
 document.querySelector("div>p");
 document.querySelectorAll("div>p");



 

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。