TASK 0002 已经发布,初级班的任务时间是从4月24日至5月7日,中级班为4月18日至4月25日。
TASK 0002 内容:https://github.com/baidu-ife/ife/tree/master/task/task0002
我做的:https://github.com/DIYgod/ife-work/tree/master/task0002
在线Demo: https://www.anotherhome.net/file/ife/task0002/task0002_1.html
本次任务累计花费时间17天(4.19-5.6 )
1. JavaScript 的性能优化 (参考 JavaScript 的性能优化:加载和执行)
· Web 开发人员一般习惯在 <head> 中加载外链的 JavaScript,接着用 <link> 标签用来加载外链的 CSS 文件或者其他页面信息。然而这种常规的做法却隐藏着严重的性能问题——脚本会阻塞页面其他资源的下载。因此推荐将所有<script>标签尽可能放到<body>标签的底部,以尽量减少对整个页面下载的影响。
· 由于每个<script>标签初始下载时都会阻塞页面渲染,所以减少页面包含的<script>标签数量有助于改善这一情况。
· 减少 JavaScript 对性能的影响其他方法见参考。
2. ==
与 ===
(参考 Javascript中双等号“==”和三等号“===”的区别 JavaScript里面三个等号和两个等号的区别 JavaScript编码规范)
==
:等于运算,但是不比较值的类型;===
:完全等于运算,不仅比较值,而且还比较值的类型,只有两者一致才为真。
百度JavaScript编码规范也规定:在 Equality Expression 中强制使用类型严格的 ===
。仅当判断 null 或 undefined 时,允许使用 == null
。因为使用 ===
可以避免等于判断中隐式的类型转换。
3. 类型检测 (参考 JavaScript编码规范 Javascript数组类型检测:编写更强壮的isArray函数 Javascript 判断函数类型完美解决方案)
通用的简易做法:类型检测优先使用 typeof
。对象类型检测使用 instanceof
。null 或 undefined 的检测使用 == null
// string
typeof variable === ‘string’
// number
typeof variable === ‘number’
// boolean
typeof variable === ‘boolean’
// Function
typeof variable === ‘function’
// Object
typeof variable === ‘object’
// RegExp
variable instanceof RegExp
// Array
variable instanceof Array
// null
variable === null
// null or undefined
variable == null
// undefined
typeof variable === ‘undefined’
判断数组类型:得到对象的字符串表示,然后对比此字符串是否是’[object Array]’
function isArray(arr) {
return Object.prototype.toString.call(arr) === ‘[object Array]’;
}
9.20更新:下面这样也可以
function isArray(arr) {
return Array.isArray(arr);
}
判断函数类型:首先保证测试的对象存在,并将其序列化成含有“function”的字符串,这个是我们检测的基础(fn.constructor != String,fn.constructor != Array, and fn.constructor != RegExp)。另外,我们需要保证声明的函数不是一个DOM节点(fn.nodeName)。然后,我们就可以作toString测试。如果我们将一个函数转换成字符串,在一个浏览器中(fn+””)给我们的结果就像这样“function name(){…}”。现在,判断它是否为函数就很简单,仅仅只需要判断字符串中是否包含单词“function”。这很神奇,对于任何有问题的函数,在所有浏览器中都能得到我们所需要的结果。这个函数较之于传统的方法,运行速度有些不尽人意,作者(John Resig)建议我们保守使用。
function isFunction(fn) {
return !!fn
&& !fn.nodeName
&& fn.constructor != String
&& fn.constructor != RegExp
&& fn.constructor != Array
&& /function/i.test(fn + ‘’);
}
9.20更新:上面那种太麻烦,可以类似Array那样判断
function isFunction(fn) {
return Object.prototype.toString.call(fn) === ‘[object Function]’;
}
复制Array的简单做法:
var a1 = [1, 2, 3]
var a2 = a1.slice(0);
4. 类型转换 (参考 JavaScript编码规范)
number -> string:
num + ‘’;
string -> number:
+str;
var width = ‘200px’; // 要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt
parseInt(width, 10);
-> boolean:
var num = 3.14;
!!num;
number 去除小数点:
var num = 3.14;
Math.ceil(num);
5. 对有序集合进行遍历时,缓存 length (参考 JavaScript编码规范)
虽然现代浏览器都对数组长度进行了缓存,但对于一些宿主对象和老旧浏览器的数组对象,在每次 length 访问时会动态计算元素个数,此时缓存 length 能有效提高程序性能。
for (var i = 0, len = elements.length; i < len; i++) {
var element = elements[i];
// ……
}
6. document.body.scrollTop与document.documentElement.scrollTop获取滚动条滚动的距离的坑 (参考 document.body.scrollTop与document.documentElement.scrollTop兼容 用Javascript获取页面元素的位置 火狐、谷歌、IE关于document.body.scrollTop和document.documentElement.scrollTop 以及值为0的问题)
参考阮一峰的教程写了下面一段:
if (document.compatMode == “BackCompat”){
var elementScrollLeft=document.body.scrollLeft;
} else {
var elementScrollLeft=document.documentElement.scrollLeft;
}
没想到遇到了坑,结果elementScrollLeft总是0,调试结果如下:
好坑啊,说好的如果有文档声明(即网页第一句的docType)的情况下,document.compatMode 的值等于 “CSS1compat”,标准浏览器是只认识documentElement.scrollTop的啊。
另外试了一下IE和Firefox,均可认documentElement.scrollTop,chrome的错!
还好document.body.scrollTop与document.documentElement.scrollTop两者有个特点,就是同时只会有一个值生效。比如document.body.scrollTop能取到值的时候,document.documentElement.scrollTop就会始终为0;反之亦然。所以可以这样写:
var scrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft;
var scrollTop = document.body.scrollTop + document.documentElement.scrollTop;
7. 获取所有DOM元素
突然脑洞大开想到的
var ele = document.getElementsByTagName(‘*’);
8. 真假判断
对象总为true,基础类型看是否为空
!![] // true
!!{} // true
!!’’ // false
!!0 // false
容易把空数组误以为是false!
容易把空数组误以为是false!
容易把空数组误以为是false!
重要的事情要说三遍!
9. 递归获取所有子元素
这个是有问题的,只能获取到下一级的子元素:
var childs = function (element) {
var allchilds = [];
var childn = element.childNodes;
if (childn.length !== 0) {
for (var i = 0, len = childn.length; i < len; i++) {
allchilds.push(childn[i]);
allchilds.concat(childs(childn[i]));
}
}
return allchilds;
}
但如果改成这样就能获取所有子元素了:
var allchilds = [];
var childs = function (element) {
var childn = element.childNodes;
if (childn.length !== 0) {
for (var i = 0, len = childn.length; i < len; i++) {
allchilds.push(childn[i]);
allchilds.concat(childs(childn[i])); //或者 childs(childn[i]);
}
}
return allchilds;
}
另外调试过程中发现的诡异现象:
解释下:递归调用时返回值allchilds是正确的,但返回到上一层时并没有加到上一层的allchilds中。
月月 告诉我这句话有问题!
allchilds.concat(childs(childn[i]));
w3school如是说:concat() 方法用于连接两个或多个数组。
该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。仅仅会返回被连接数组的一个副本。仅仅会返回被连接数组的一个副本。
一切都说通了。。
所以根本不关allchilds声明位置的事,debug的方向一直都不对,要这样改:
var childs = function (element) {
var allchilds = [];
var childn = element.childNodes;
if (childn.length !== 0) {
for (var i = 0, len = childn.length; i < len; i++) {
allchilds.push(childn[i]);
allchilds = allchilds.concat(childs(childn[i]));
}
}
return allchilds;
}
万万没想到,在 月月 李胜 蛋炒饭 的帮助下千辛万苦终于完成了这个小函数。。。
后续:突然想到这样就能轻易获取到
element.getElementsByTagName(‘*’);
已吐血
10. 数组合并 (参考 JavaScript concat() 方法)
重要的事情要另外单独说。因为被坑惨了。
concat() 方法用于连接两个或多个数组。
该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
错误用法:
arr1.concat(arr2);
正确用法:
arr1 = arr1.concat(arr2);
11. 事件代理 (参考 javascript事件代理)
在编程中,如果我们不想或不能够直接操纵目标对象,我们可以利用delegate创建一个代理对象来调用目标对象的方法,从而达到操纵目标对象的目的。毋庸置疑,代理对象要拥有目标对象的引用。
可以用事件代理的方法来优雅地用一个函数代理另一个函数,比如:
var delegate = function (method) {
return function() {
return method.apply(null, arguments);
}
}
var on = delegate(addEvent);
var un = delegate(removeEvent);这样就能优雅地用 on un 函数代理 addEvent removeEvent 函数了。这样实现了目标对象的隐藏,这对于我们保护一些核心对象是非常有用的。
我错了,直接这样就行了。。
var on = addEvent;
var un = removeEvent;
12. Object相关 (参考 js的Object到底是什么呢?)
Array、Boolean、Date、Function、Number等等对象,其实都是从Object来的,它们的祖先都是Object。它们表现不同的语言特性,比如Array有被自动管理的length属性,Boolean只有true或false取值,Date表示时间结构,Function可以被运行,都是它们的原始类型(valueOf)赋予它们的能力。
所以有些有趣的东西:
function f() {
alert(‘f1’);
}
f.c = function() {
alert(‘c1’);
}
f();
f.c();
可以给函数设置一个也是函数的属性。
13. JavaScript正则表达式分组 (参考 JavaScript 正则表达式 选择、分组和引用)
例:
function getCookie(cookieName) {
var re = new RegExp(cookieName + ‘=(.?)($|;)’);
return re.exec(document.cookie)[1];
}
其中re.exec(document.cookie)是一个数组,第一个元素是正则式所有匹配出的字符,第二个元素是匹配的第一个分组,即第一个括号里的内容:(.?)
分组就就是正则表达式中的子表达式,可以用来获取正则式匹配出的字符串中的特定部分。
9.20更新:当使用构造函数创造正则对象时,需要常规的字符转义规则(在前面加反斜杠 \)。比如,以下是等价的:
var re = new RegExp(“\w+”);
var re = /\w+/;
14. JavaScript跨域 (参考 JavaScript跨域总结与解决办法)
JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。但在安全限制的同时也给注入iframe或是ajax应用上带来了不少麻烦。
具体限制及解决办法见参考。
15. JavaScript月份多出一 (参考 JS 中 new Date 怎么就多一个月了? JavaScript——搞甘特图使用 Date 对象时遇到的问题)
明明设置的时5月,结果却是6月。
实际中,我们数月份不是从 0 开始,但是 JavaScript 却是从 0 开始。JS中的0月是我们的1月,JS中的1月是我们的2月…
所以设置月份时记得减一,获取月份时记得加一。。
15. 计时器不会阻塞代码执行
如图
我的本意是执行完 a() 再输出b,没想到b先输出了,从这里也可以推测出,计时器并不会阻塞后面语句的执行。
☆ミ(o*・ω・)ノ完结散花 等待review