【js基础--1】一些基础性知识和技巧的记录
本文摘自MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/A_re-introduction_to_JavaScript
1、通过如下的规则,任何值都可以被转换成布尔型:
1)false
, 0
, 空字串(""
), NaN
, null
, 和undefined
都被转换为 false
2)其他值会被转换为 true
该特性可以直接应用于判断语句中,减少代码量。
2、在对象中定义一个对象
1 var obj = {
2 name: "Carrot",
3 "for": "Max",
4 details: {
5 color: "orange",
6 size: 12
7 }
8 }
3、array.length
并不是指数组中准确的元素的个数,如下所示:
var a = ["dog", "cat", "hen"];
a[100] = "fox";
a.length
101
4、遍历数组的高效率写法
传统方式上可以通过如下方式遍历一个数组:
for (var i = 0; i < a.length; i++) {
// Do something with a[i]
}
这样做效率稍微有些低,因为每循环一遍都要计算一次长度。改进的方法是:
for (var i = 0, len = a.length; i < len; i++) {
// Do something with a[i]
}
一种更好的写法是:
for (var i = 0, item; item = a[i]; i++) {
// Do something with item
}
在这里我们使用了两个变量。for循环中间部分的表达式仍旧是用来监测是否为真--如果成功,那么循环继续。因为i
每次递增1,这个数组的每个元素即被按顺序地传递给item变量。当一个“falsy”元素(如undefined
)被发现时,循环就结束了。注意,这个技巧只能在你确认数组中不包含“falsy”值时才可以使用。如果你想要遍历可能包含0或空字符串的数组,你应该使用i, j
的写法替代。
遍历数组的另外一种方式是使用for...in
循环。注意,如果有人向Array.prototype
添加新的属性,通过这样的循环它们也同样会被遍历:
for (var i in a) {
// Do something with a[i]
}
5、如果你希望在一个数组后面添加元素,最安全的方式是:
a[a.length] = item; // 与 a.push(item)等效;
a.length
是比数组的最大索引值大一的数组,这样就可以保证你在数组的最后分配了一个空的位置给新的元素。6、数组类包含了许多方法:
a.toString(), a.toLocaleString(), a.concat(item, ..), a.join(sep),
a.pop(), a.push(item, ..), a.reverse(), a.shift(), a.slice(start, end),
a.sort(cmpfn), a.splice(start, delcount, [item]..), a.unshift([item]..)
concat
返回一个新数组,将元素添加在结尾。pop
移除并返回最后一个元素。push
在数组的结尾添加一个或多个元素(类似于ar[ar.length]
)slice
返回一个子数组sort
对数组排序splice
把数组中的一部分去掉并用其它值取代unshift
将元素拼接到数组的开头
7、我们能知道可以给函数传递不定数量的参数,要想获取所有参数可以:
函数可以在函数体中存取一个名为arguments的内部对象,这个对象就如同一个类似于数组的对象一样,它包括了所有被传入函数的值。让我们重写一下上面的函数,使他可以接收我们所期望的任意数量的值。
function add() {
var sum = 0;
for (var i = 0, j = arguments.length; i < j; i++) {
sum += arguments[i];
}
return sum;
}
> add(2, 3, 4, 5)
14
8、匿名函数甚至可以直接这样:
(function() {
var b = 3;
a += b;
})();
9、如何处理匿名函数的递归调用(因为他们并没有实际的名称)?答案是通过arguments
对象,那个用来指代一系列参数的对象,它还提供了一个名为arguments.callee
的属性。这个东西通常是指向当前的(调用)函数,因此它可以用来进行递归调用:
var charsInBody = (function(elm) {
if (elm.nodeType == 3) { // TEXT_NODE
return elm.nodeValue.length;
}
var count = 0;
for (var i = 0, child; child = elm.childNodes[i]; i++) {
count += arguments.callee(child);
}
return count;
})(document.body);
10.通过原型(prototype)可以重写或添加对象方法及属性等,比如:
>var s = "Simon";
> s.reversed()
TypeError on line 1: s.reversed is not a function
> String.prototype.reversed = function() {
var r = "";
for (var i = this.length - 1; i >= 0; i--) {
r += this[i];
}
return r;
}
> s.reversed()
nomiS
11、关于apply()和call()方法,不常用到但是有必要了解一下,有时间再说。
12、闭包
下面我们将看到的是javaScript中不能不提到的功能最强大的结构之一:闭包。但它同时也有很多潜在的困扰。那么它究竟是做什么的呢?
function makeAdder(a) {
return function(b) {
return a + b;
}
}
x = makeAdder(5);
y = makeAdder(20);
x(6)
?
y(7)
?
makeAdder
函数的名字自身应该就揭示了很多秘密:它创建了一个新的'adder'函数,这个函数自身带有一个参数,它被调用的时候这个参数会被加在外层函数传进来的参数上。
这里发生的事情和前面介绍过的内嵌函数十分相似:一个函数被定义在了另外一个函数的内部,内部函数可以存取外部函数的变量。这里唯一的不同就是:外部函数被返回了,那么常识告诉我们局部变量“应该”不再存在。但是它们却仍然存在--否则adder函数将不能工作。也就是说,这里存在了makeAdder
的局部变量的两个不同的“副本”--一个是a
等于5,另一个是a
等于20。所以那些函数的结果就如下所示了:
x(6) // 返回 11
y(7) // 返回 27
这里就是实际发生了的事情。每当javascript执行一个函数的时候,会创建一个'范围对象',并用来保存在这个函数中创建的局部变量。它和被传入函数的变量一同被初始化。这与那些保存的所有全局变量和函数的全局对象(global object)相似,但有一些很重要的不同点是:第一,每次函数被执行的时候,就会创建一个新的,特定的范围对象;第二,与全局对象(在浏览器里面是当做 window 对象来访问的)不同的是,你可以从javascript代码中直接访问范围对象。目前并没有机制可以遍历当前的范围对象里面的属性。
所以当调用makeAdder
时,创建了一个范围对象,它带有一个属性:a
,被当做参数传入makeAdder
函数。makeAdder
然后返回一个新创建的函数。通常JavaScript的垃圾回收器会在这个点上清理掉makeAdder
创建的范围对象,但是返回的函数却保留着一个指向那个范围对象的引用。结果,这个范围对象将不会被垃圾回收器回收,直到指向makeAdder
返回的那个函数对象的引用计数为零。
范围对象组成了一个名为范围链的链。它类似于原形(prototype)链一样,被javascript的对象系统使用。
一个闭包就是一个函数和被创建的函数中的范围对象的组合。
闭包允许你保存状态--所以,它们通常可以代替对象来使用。
13、内存泄露