做笔记是一个好习惯,做笔记不等于抄书,书上的内容不是一眼就能够看懂的,我们需要将书上的内容做一个“翻译”,翻译成自己一眼就能够读懂的文字。
P23
==与===的区别
var a = false;
var b = '';
a == b;//结果为true
a === b;//结果为false
区别:== 只进行值的比较,而 === 除了判断值相等,还会判断类型是否相等。这里flase和''具有相同的含义,但是它们类型不同,一个是boolean一个是String。
注:!= 与 !== 同理
P28
规范:变量用下划线分割各个单词,函数用驼峰命名法。这样便于一眼看出哪些是变量哪些是函数。
变量作用域:在函数内部,用var声明的变量是局部变量,反之则是全局变量。
function square(num) {
total = num * num;
return total;
}
var total = 50;
var number = square(20);
alert(total);//答案是400
P42
自己实现一个 getElementsByClassName 方法
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Shopping list</title>
</head>
<body>
<h1>What to buy</h1>
<p title="a gentle reminder">Don't forget to buy this stuff.</p>
<ul id="purchases">
<li>A tin of beans</li>
<li class="sale">Cheese</li>
<li class="sale important">Milk</li>
</ul>
<script>
function getElementByClassName(node, classname) {
var results = new Array();
var elems = node.getElementsByTagName("*");
for (var i = 0; i < elems.length; i++) {
if (elems[i].className.indexOf(classname) != -1) {
results[results.length] = elems[i];
}
}
return results;
}
var saleList = getElementByClassName(document, 'sale');
</script>
</body>
</html>
P51
事件处理函数的工作机制:给某个a标签添加onclick事件,onclick双引号里面的js代码返回true或false,如果返回的是true,那么onclick事件处理函数就会认为这个链接被点击了;反之,如果返回false,onclick事件处理函数就会认为这个链接没被点击。
可以通过下面的代码去验证这一结论:
<a href="http://www.baidu.com" onclick="return false;">Click me</a>
踩坑
<a href="http://baidu.com" onclick="show()">百度</a>
<script>
function show() {
alert("我没跳转");
return false;
}
</script>
执行完以上代码你会深刻领悟到js 的坑。
首先你要知道 当 onclick 后面的事件不是一个函数名时,它会被包裹到一个匿名函数中执行。
以上就相当于是
obj.onclick = function() {show();}
当点击链接的时候执行 onclick 里面的代码,而不是把show()当作事件处理程序,return false 只是在 show() 这个函数中返回了并没有在 onclick 事件中返回。
因此还会发生跳转
重点在这里:在事件处理函数的工作机制中,当在给某元素添加事件处理函数后,一旦事件发生,相应JavaScript代码就会执行,所调用的JavaScript代码的返回值被传递给事件处理函数。当我们给a标签添加onclick事件处理函数并点击a触发其后,如果相应JavaScript代码返回true,onclick事件处理函数就会认为这个链接被点击了,同样的若返回false即会认为链接未被点击。
所以 可以这样写
<a href="http://www.baidu.com" onclick="myjs(); return false;">百度 </a>
<a href="http://www.baidu.com" onclick="return false;">百度</a>
P70
对象检测:检测浏览器是否支持某个方法。
if (!getElementById || !getElementsByTagName) return false;
备注:对象检测是用来向下兼容浏览器的,由于我只是单纯的学习js,所以没必要考虑兼容性的问题。
P72
尽量少访问DOM
只要是查询DOM中的某些元素,浏览器都会搜索整个DOM树。
尽量减少文档中的标记数量
过多不必要的元素只会增加DOM树的规模,进而增加遍历DOM树的时间。
P73
合并js脚本
尽量把多个js脚本合并成一个,这样可以减少加载页面时发送的请求数量。而减少请求数量通常都是在性能优化时首先要考虑的。
<script>标签放置位置
一般是放在文档末尾,这样能够保证页面加载完毕后才开始下载脚本文件,网页的流畅度大大提升。
压缩脚本
把脚本中不必要的字节如空格和注释之类的统统删掉,缩减文本的大小。
有的精简程序甚至会重写你的部分代码,使用更短的变量名,从而减少整体文件大小。如
function showPic(whichpic) {
//取得图片的href属性
var source = whichpic.getAttribute('href');
//取得占位符
var placeholder = document.getElementById('placeholder');
//更新占位符
placeholder.setAttribute('src', source);
//使用图像的title属性更新文本描述
var text = whichpic.getAttribute('title');
var description = document.getElementById('description');
}
压缩之后的代码会变成下面这样
function showPic(a) {var b = a.getAttribute('href');document.getElementById('placeholder').setAttribute('src', b);a.getAttribute('title');document.getElementById('description');}
压缩代码工具
Douglas Crockford 的 JSMin
雅虎的 YUI Compressor
谷歌的 Closure Compiler
P79
原则:如果想用JavaScript给某个网页添加一些行为,就不应该让JavaScript代码对这个网页结构有任何依赖。
笔记:低耦合带来的好处是代码的适用性大大提高。
结构化程序设计中的一条原则:函数应该只有一个入口和出口。
但是不要过分拘泥于这项原则,否则的话代码会变得难以阅读。
P82
改良后的图片库
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h1>Snapshots</h1>
<ul id="imagegallery" placeholder="placeholder">
<li>
<a href="images/fireworks.jpg" title="A fireworks display">Fireworks</a>
</li>
<li>
<a href="images/coffee.jpg" title="A cup of black coffee">Coffee</a>
</li>
<li>
<a href="images/rose.jpg" title="A red, red rose">Rose</a>
</li>
<li>
<a href="imagess/bigben.jpg" title="The famous clock">Big Ben</a>
</li>
</ul>
<img id="placeholder" src="images/placeholder.gif" alt="my image gallery"/>
<script>
function prepareGallery() {
if (!document.getElementsByTagName) return false;
if (!document.getElementById) return false;
if (!document.getElementById('imagegallery')) return false;
var gallery = document.getElementById('imagegallery');
//获取 gallery 关联的 placeholder
var placeholder = document.getElementById(gallery.getAttribute('placeholder'));
var links = gallery.getElementsByTagName('a');
for (var i = 0; i < links.length; i++) {
links[i].onclick = function () {
showPic(this, placeholder);
return false;
}
}
}
function showPic(whichpic, placeholder) {
var source = whichpic.getAttribute('href');
placeholder.setAttribute('src', source);
}
prepareGallery();
</script>
</body>
</html>
用法:在<ul>
标签上添加id="imagegallery"
,然后再调用prepareGallery()
就将图片库初始化成功。
属性
placeholder
指定图片展示的位置
P82
网页加载完毕的时候执行某个函数
window.onload = prepareGallery;
执行多条函数
window.onload = function () {
firstFunction();
secondFunction();
}
这里还有一个弹性最佳的解决方案——不管你打算在页面加载完毕时执行多少个函数,它都可以应付自如。
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function () {
oldonload();
func();
};
}
}
如果这个处理函数上还没有绑定任何函数,就像平时那样把新函数添加给它。
如果这个处理函数上已经绑定了函数,就把新函数追加到现有指令的末尾。
补充
typeof:运算符把类型信息当作字符串返回。
下表总结了 typeof
可能的返回值
类型 | 结果 |
---|---|
Undefined | 'undefined' |
Null | 'object' |
Boolean | 'boolean' |
Number | 'number' |
String | 'string' |
Symbol (ECMAScript 6 新增) | 'symbol' |
宿主对象(由JS环境提供) | Implementation-dependent |
函数对象 | 'function' |
任何其他对象 | 'object' |
示例
// Numbers
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 尽管NaN是"Not-A-Number"的缩写
typeof Number(1) === 'number'; // 但不要使用这种形式!
// Strings
typeof "" === 'string';
typeof "bla" === 'string';
typeof (typeof 1) === 'string'; // typeof总是返回一个字符串
typeof String("abc") === 'string'; // 但不要使用这种形式!
// Booleans
typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(true) === 'boolean'; // 但不要使用这种形式!
// Symbols
typeof Symbol() === 'symbol';
typeof Symbol('foo') === 'symbol';
typeof Symbol.iterator === 'symbol';
// Undefined
typeof undefined === 'undefined';
typeof declaredButUndefinedVariable === 'undefined';
typeof undeclaredVariable === 'undefined';
// Objects
typeof {a:1} === 'object';
// 使用Array.isArray 或者 Object.prototype.toString.call
// 区分数组,普通对象
typeof [1, 2, 4] === 'object';
typeof new Date() === 'object';
// 下面的容易令人迷惑,不要使用!
typeof new Boolean(true) === 'object';
typeof new Number(1) === 'object';
typeof new String("abc") === 'object';
// 函数
typeof function(){} === 'function';
typeof class C{} === 'function'
typeof Math.sin === 'function';
typeof new Function() === 'function';
null
typeof null === 'object'; // 从一开始出现JavaScript就是这样的
在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null
代表的是空指针(大多数平台下值为 0x00
),因此,null
的类型标签也成为了 0,typeof null
就错误的返回了'object'
。
判断一个变量是否存在
这得说一下变量的几种状态(变量的生命周期?)
a; // 未声明
var b; // 声明但未赋值(类型为 undefined)
var c = null; // 赋值(虽然这个值是 null,但其类型为 object)
用 if 语句判断变量是否存在
if(!a){
alert("通过")
}
if(!b){
alert("通过")
}
if(!c){
alert("通过")
}
第一个直接报错,第二、三个都会弹出“通过”,所以 undefined
和 null
在 if 语句中,都会转换为 false。如果将判断的条件换成一下这样
if(typeof a != 'undefined'){
alert("通过")
}
if(typeof b != 'undefined'){
alert("通过")
}
if(typeof c != 'undefined'){
alert("通过")
}
结果只有第三个弹出“通过”,结论就是 null
和 undefined
值相同但类型不相同。
null == undefined // true
null === undefined // false
P93
- getElementById
- getElementsByTagName
- getAttribute
- setAttribute
这些方法都是 DOM Core 的组成部分。它们并不专属于 JavaScript。它们可以用来处理任何一种标记语言(比如 XML)编写的文档。
P108
在已有的元素前插入一个新元素
parentElement.insertBefore(newElement, targetElement);
我们不必搞清楚父元素到底是哪个,因为 targetElement 元素的 parentNode
属性值就是它。
var gallery = document.getElementById('imagegallery');
gallery.parentNode.insertBefore(placeholder, gallery);
将 placeholder 元素插入到 gallery 元素之前
在已有的元素后插入一个新元素
没有 insertAfter()
这个方法,我们得自己写一个
function insertAfter(newElement, targetElement) {
var parent = targetElement.parentNode;
if (parent.lastChild == targetElement) {
parent.appendChild(newElement);
} else {
parent.insertBefore(newElement, targetElement.nextSibling);
}
}
P115
XMLHttpRequest对象:Ajax技术的核心。这个对象充当浏览器中的客户端与服务器之间的中间人角色。以往的请求都由浏览器发出,而JavaScript通过这个对象可以自己发送请求,同时也自己处理响应。
创建 XMLHttpRequest
对象
IE浏览器(而且不同的版本创建方式也不同)
var request = new ActiveXObject('Msxml2.XMLHTTP.3.0');
其他浏览器
var request = new XMLHttpRequest();
为了兼容所有浏览器,我们需要一个脚本
function getHTTPObject() {
if (typeof XMLHttpRequest == 'undefined') { // IE浏览器
XMLHttpRequest = function() {
try {
return new ActiveXObject('Msxml2.XMLHTTP.6.0');
} catch (e) {
}
try {
return new ActiveXObject('Msxml2.XMLHTTP.3.0');
} catch (e) {
}
try {
return new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
}
return false;
}
}
return new XMLHttpRequest();
}
XMLHttpRequest 对象中有许多方法。其中最有用的是 open 方法。
open(method, url, async)
作用:初始化 HTTP 请求参数。
method:指定请求类型,如GET、POST 或 SEND。
url:请求路径。
async:是否异步方式发送和请求。
通过 Ajax 请求本地文件 example.txt
function getNewContent() {
var request = getHTTPObject();
if (request) {
request.open('GET', 'example.txt', true);
request.onreadystatechange = function() {
if (request.readyState == 4) {
var para = document.createElement('p');
var txt = document.createTextNode(request.responseText);
para.appendChild(txt);
document.getElementById('new').appendChild(para);
}
};
request.send(null);
} else {
alert('Sorry, your browser doesn\'t support XMLHttpRequest');
}
}
onreadystatechange 是一个事件处理函数,它会在服务器给 XMLHttpRequest 对象送回响应的时候被触发执行。
我们给它指定了一个处理函数:
request.onreadystatechange = doSomething
注意:在为 onreadystatechange 指定函数引用时,不要在函数名后面加括号。因为加括号标识立即调用函数,并且把函数执行结果赋值给 onreadystatechange,而我们在此只想把函数自生的引用(而不是函数结果)赋值给 onreadystatechange 属性。
只要 readyState 属性的值变成了4,就可以访问服务器发送回来的数据了。
访问服务器发送回来的数据:
responseText
保存文本字符串形式的数据
responseXML
保存 Content-Type 头部中指定为"text/xml"的数据,其实是一个 DocumentFragment 对象。
注意:在使用 Ajax 时,千万要注意同源策略。使用 XMLHttpRequest 对象发送的请求只能访问与其所在的 HTML 处于同一个域中的数据,不能向其他域发送请求。还有在谷歌浏览器中使用 Ajax 请求 file:// 协议的文件会报
Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
的错误消息,只需右键谷歌浏览器的快捷图标,然后点击属性并且在目标后面添加 --allow-file-access-from-files 就能够解决问题。
补充
XMLHttpRequest.send()
该方法的作用是发送 HTTP 请求。
一般情况下,使用 Ajax 提交的参数多是些简单的字符串,可以直接使用 GET 方法将要提交的参数写到 open 方法的url参数中,此时send方法的参数为null。如:
request.open('GET', 'login.jsp?user=XXX&pwd=XXX', true);
request.send(null);
此外,也可以使用 send 方法传递参数。使用 send 方法传递参数使用的是 POST 方法,需要设定 Content-Type 头信息,模拟 HTTP POST 方法发送一个表单,这样服务器才会知道如何处理上传的内容。参数的提交格式和 GET 方法中 url 的写法一样。设置头信息前必须先调用 open 方法。
request.open('POST', 'login.jsp', true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8');
request.send('user=' + username + '&pwd=' + password);
readyState 状态说明
(0)未初始化
此阶段确认 XMLHttpRequest 对象是否创建,并为调用 open() 方法进行未初始化作好准备。值为0表示对象已经存在,否则浏览器会报错--对象不存在。
(1)载入
此阶段对 XMLHttpRequest 对象进行初始化,即调用 open() 方法,根据参数(method,url,true)完成对象状态的设置。并调用 send() 方法开始向服务端发送请求。值为1表示正在向服务端发送请求。
(2)载入完成
此阶段接收服务器端的响应数据。但获得的还只是服务端响应的原始数据,并不能直接在客户端使用。值为2表示已经接收完全部响应数据。并为下一阶段对数据解析作好准备。
(3)交互
此阶段解析接收到的服务器端响应数据。即根据服务器端响应头部返回的 MIME 类型把数据转换成能通过 responseBody 、responseText 或 responseXML 属性存取的格式,为在客户端调用作好准备。状态3表示正在解析数据。
(4)完成
此阶段确认全部数据都已经解析为客户端可用的格式,解析已经完成。值为4表示数据解析完毕,可以通过 XMLHttpRequest 对象的相应属性取得数据。
概而括之,整个 XMLHttpRequest 对象的生命周期应该包含如下阶段:
创建-初始化请求-发送请求-接收数据-解析数据-完成
简化版的 readyState 状态说明
(0)未初始化:XMLHttpRequest 对象已经创建,但还没有调用 open() 方法。
(1)载入:已经调用 open() 方法,但尚未调用 send() 方法。
(2)载入完成:send() 方法已调用。
(3)交互:服务器正在发送响应。
(4)完成:服务器完成发送响应。
DocumentFragment 节点
DocumentFragment 节点不属于文档树。
当请求把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。这使得 DocumentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。它还有利于实现文档的剪切、复制和粘贴操作。
P159
获取下一个元素节点
function getNextElement(node) {
if (!node.nextSibling) return null;
if (node.nextSibling.nodeType == 1) return node.nextSibling;
return getNextElement(node.nextSibling);
}
P168
添加 class 属性
function addClass(element, value) {
if (!element.className) {
element.className = value;
} else {
newClassName = element.className;
newClassName += ' ';
newClassName += value;
element.className = newClassName;
}
}
P203
Modernizr
一个开源的 JavaScript 库。Modernizr 不会给你添加浏览器不支持的特性。
P206
实现当鼠标移到图片上时恢复彩色,鼠标离开图片时变为灰色的效果
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<img id="avatar" title="Jeffrey Sambells" alt="My Avatar" src="images/avatar.png"/>
<script>
function convertToGS(img) {
// 存储原始的彩色版
img.color = img.src;
//创建灰度版
img.grayscale = createGSCanvas(img);
img.onmouseover = function() {
this.src = this.color;
}
img.onmouseout = function() {
this.src = this.grayscale;
}
img.onmouseout();
}
function createGSCanvas(img) {
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var ctx =canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
var c = ctx.getImageData(0, 0, img.width, img.height);
for (i = 0; i < c.height; i++) {
for (j = 0; j < c.width; j++) {
var x = (i * 4) * c.width + (j * 4);
var r = c.data[x];
var g = c.data[x+1];
var b = c.data[x+2];
c.data[x] = c.data[x+1] = c.data[x+2] = (r + g + b) / 3;
}
}
ctx.putImageData(c, 0, 0, 0, 0, c.width, c.height);
return canvas.toDataURL();
}
window.onload = function() {
convertToGS(document.getElementById('avatar'));
}
</script>
</body>
</html>
P210
在每个影片容器中,音频和视频轨道都使用不同的编解码器来编码。编解码器决定了浏览器在播放时应该如何解码音频和视频。编解码器的核心就是一个算法,用于压缩和存储视频,以减少原始文件的大小,同时可能会也可能不会损失品质。
P212
用 html5 在页面上制作一个视频播放器
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>My Video</title>
<style>
.video-wrapper {
overflow: hidden;
}
.video-wrapper .controls {
position: absolute;
height: 30px;
width: 30px;
margin: auto;
background: rgba(0, 0, 0, 0.5);
}
.video-wrapper button {
display: block;
width: 100%;
height: 100%;
border: 0;
cursor: pointer;
font-size: 17px;
color: #fff;
background: transparent;
}
.video-wrapper button[paused] {
font-size: 12px;
}
</style>
</head>
<body>
<div class="video-wrapper">
<video id="movie" controls>
<source src="108478173_b67d02eae28fad85c96c5a38e460a847_1aeb3523af66_2.mp4"/>
<!--
<source src="movie.webm" type="video/webm; codecs='vp8, vorbis'"/>
<source src="movie.ogv" type="video/ogg; codecs='theora, vorbis'"/>
-->
<p>
Download movie as
<a href="108478173_b67d02eae28fad85c96c5a38e460a847_1aeb3523af66_2.mp4">MP4</a>,
<!--
<a href="movie.webm">WebM</a>,
or
<a href="movie.ogv">Ogg</a>.
-->
</p>
</video>
</div>
<script>
function createVideoControls() {
var vids = document.getElementsByTagName('video');
for (var i = 0; i < vids.length; i++) {
addControls(vids[i]);
}
}
function addControls(vid) {
vid.removeAttribute('controls');
vid.height = vid.videoHeight;
vid.width = vid.videoWidth;
vid.parentNode.style.height = vid.videoHeight + 'px';
vid.parentNode.style.width = vid.videoWidth + 'px';
var controls = document.createElement('div');
controls.setAttribute('class', 'controls');
var play = document.createElement('button');
play.setAttribute('title', 'Play');
play.innerHTML = '►';
controls.appendChild(play);
vid.parentNode.insertBefore(controls, vid);
play.onClick = function() {
if (vid.ended) {
vid.currentTime = 0;
}
if (vid.paused) {
vid.play();
} else {
vid.pause();
}
}
vid.addEventListener('play', function() {
play.innerHTML = '▐ਞ';
play.setAttribute('paused', true);
}, false);
vid.addEventListener('pause', function() {
play.innerHTML = '►';
play.removeAttribute('paused');
}, false);
vid.addEventListener('ended', function() {
vid.pause();
}, false);
}
window.onload = function() {
createVideoControls();
}
</script>
</body>
</html>
P217
检查浏览器是否支持某个输入类型的控件
function inputSupportsType(type) {
if (!document.createElement) return false;
var input = document.createElement('input');
input.setAttribute('type', type);
if (input.type == 'text' && type != 'text') {
return false;
} else {
return true;
}
}
P218
检查某元素是否有某属性
function elementSupportsAttribute(elementName, attribute) {
if (!document.createElement) return false;
var elem = document.createElement(elementName);
return ( attribute in elem );
}
如果浏览器不支持 placeholder 属性
if (!elementSupportsAttribute('input', 'placeholder')) {
var input = document.getElementById('first-name');
input.onfocus = function() {
var text = this.placeholder || this.getAttribute('placeholder');
if (this.value == text) {
this.value = '';
}
}
input.onblur = function() {
if (this.value == '') {
this.value = this.placeholder || this.getAttribute('placeholder');;
}
}
input.onblur();
}
P268
内容分发网络
对于库来说,如果有很多站点要使用同一个库,那么最好是把这个库托管到一个公共服务器上,以便所有站点共享和访问。这样,当用户从一个站点跳到另一个站点时,他们就不用再重复下载相同的文件了。
内容分发网络(CDN, Content Delevery Network)可以解决分布共享库的问题。CDN 就是一个由服务器构成的网络,这个网络的用途就是分散存储一些公共的内容。CDN 中的每台服务器都包含库的一份副本,这些服务器分布在世界上不同的国家和地区,以便达到利用带宽和加快下载的目的。浏览器访问库的时候使用一个公共的 URL,而 CDN 的底层则通过地理位置最近、速度最快的服务器提供相应的文件。
总结
这本书到尾声了,这是我看的第一本真正意义上的 JavaScript 书籍。我最初学习 js 的时候看的那些教材只是简单的讲解了一些 js 的语法,没有什么深度。虽然这本书中提到知识点不多,但是却讲了一些使用 JavaScript 时的良好的规范,这些都是编程里最重要的东西。好像有句话叫“代码规范是编程思想的体现”来着,具体在哪看到的我忘了。
平稳退化
这本书提到的一个非常重要的规范,就是在浏览器不支持 JavaScript 的情况下保证页面能够正常运行而不至于崩掉。虽然现在的浏览器对 js 的支持都非常的棒,但是万一用户禁用了 JavaScript,那么页面会发生什么我也不知道,因为我从来没有禁用过 JavaScript。我之前写的 js 代码能够实现一些炫酷的效果,但是我从来都没有校验过浏览器是否支持那些方法,因为它们在我的浏览器上都能正常的运行-_-||。我以前还为那些炫酷的效果洋洋自得,现在看来这些都是些糟糕的代码。我个人觉得要做到平稳退化还是挺难的,脱离了 JavaScript 的页面岂不是变得很单调。
渐进增强
在浏览器不支持 JavaScript 的情况下要保证网页的基本功能不受影响,然后再在这个基础上使用 JavaScript 来增强交互性。如果你的网页一开始就是基于 JavaScript 构建的,那这不是一个好的网页结构。
总之,养成良好的编码规范会使人终生受益。
网友评论