1.JS的立即执行函数的用法
(1)立即执行函数的定义
立即执行函数(Immediately Invoked Function)是在页面加载时立即被执行的函数。
也就是函数的定义与函数调用结合在了一起,是函数的一种定义方式,本质上就是函数表达式(命名的或者匿名的)在创建后立即执行;
(2)立即执行函数的书写方式
常见的主要有两种方式。
第一种方式:
(function () {
alert('watch out!');
}());
第二种方式:
(function () {
alert('watch out!');
})();
推荐使用第二种,因为在函数定义之后加上一对小括号(),这样看起来更像是在函数定义完成之后对函数的调用。
立即执行函数的名称和参数:
当然,立即执行函数的可以带有参数,可以有函数名,例如下面的例子:
(function lvlv(name){
alert("hello "+name);}
)("lvlv");
立即执行函数的返回值:
就像其它任何函数一样,一个立即执行函数也能返回值并且可以复制给其它变量:
var result = (function () {
return 2 + 2;
}());
另外一种实现相同的功能的方法是省略包裹函数的括号,因为当你将立即执行函数的返回值赋值给一个变量时它们不是必需的;
var result = function () {
return 2 + 2;
}();
这种语法是非常简单的,但它可能看起来有点令人误导;如果没有注意到函数结束的括号,一些人可能就会认为result指向一个函数;实际上result指向立即执行函数的返回值,在这种情况下是数字 4 。
(3)立即执行函数的使用场景
我们知道立即执行函数的特点就是在函数定义完成之后对函数实行了调用。所以下面给出立即执行函数的两个应用场景。
场景一:
在页面代码加载完成之后,不得不执行一些设置工作,比如附加时间处理器,创建对象等等,所有的这些工作只需要执行一次,所以没有理由创建一个可复用的命名的函数。
场景二:
立即执行函数也可以用来定义对象的属性。假如,你需要定义一个很可能在对象生命周期中都不会改变的属性,但在你定义之前,你需要去计算出正确的值。你可以使用立即执行函数去封装这些工作,并且立即执行函数的返回值将会成为属性的值,下面的代码:
var o = {
message: (function() {
var who = "me",
what = "call";
return what + " " + who;
} ()),
getMsg: function() {
return this.message;
}
};
// usage
o.getMsg(); // "call me"
o.message; // "call me"
在这个例子中,o.message是一个字符串类型的属性,不是一个函数,但它需要一个函数在脚本被载入时被执行并帮忙定义属性。
(4)立即执行函数优缺点
优点:
优点1:立即执行函数模式被广泛使用,它可以帮你封装大量的工作而不会在背后遗留任何全局变量。因为定义在立即执行函数体内的所有变量都会成员立即执行函数的局部变量,所以不用担心这些临时变量会污染全局空间。
优点2:
这种模式也可以让你将独立的功能封装在自包含模块中(self-contained modules)。
假如你的页面是稳定的并且在没有JavaScript情况下能正常工作,然后本着逐步加强的想法,你加入了一些代码加强页面某个方面;你可以将这些代码封装进一个立即执行函数中,并且确保页面没有它的情况下也能正常工作。然后你可以添加更多的加强模块,移除它们,单独测试它们,允许用户去禁用它们等等。
你可以使用下面的模板去定义一个函数模块,让我们叫它module1:
// module1 defined in module1.js
(function () {
// all the module 1 code ...
}());
缺点:
就是立即执行函数不能像其他的普通函数那样可以复用了。比如下面对立即执行函数的调用就是错误的。
(function lvlv(name,age){ alert("hello "+name+" "+age);})("lvlv");
lvlv("hjf"); //Uncaught ReferenceError: lvlv is not defined
2.JS的onclick事件为什么会导致页面刷新?
我的HTML文件布局是这样的,CSS样式写在了<head>
标签内,JS脚本写在了<body>
标签后,也就是说我的CSS样式和JS代码全部写在了HTML文件内,并未独立开来。
现在出现的问题是,我为<li>
标签添加了onclick事件,添加的事件如下:
var navLiList = document.getElementById('nav').getElementsByTagName('li');
navLiList[i].onclick=function(){
for(var i=0;i<navLiList.length;++i)
navLiList[i].className="";
this.className="active";
};
当我点击时,触发事件之后,页面好像重新刷新载入似的,写在页面内的JS脚本从最上面又开始重新执行。
使用百度用中文搜索了,未果,就尝试用google使用英文来搜索,接过一下就解决。解决办法就是在事件函数的最后加上return false;
。原文见:onclick event reloads page?。这说明了我们平时遇到问题,如果百度解决不了,就换成英文使用google来试试,或者换成英文来百度。
那么JS事件处理函数中使用return的作用是什么呢?原来JavaScript在事件中调用函数时用return返回值实际上是对window.event.returnvalue进行设置。而该值决定了当前操作是否继续。当返回的是true时,将继续操作。当返回是false时,将中断操作。而直接执行时(不用return),将不会对window.event.returnvalue进行设置,所以会默认地继续执行操作。
详细说明如下:
当在 <a href="abc.htm" onclick="return add_onclick()">Open</a>
中,如果函数 add_onclick() 返回 true, 那么 页面就会打开 abc.htm
否则(返回 false), 那么页面不会跳转到 abc.htm, 只会执行你的 add_onclick() 函数里的内容。 这也就是说,加上了return,可根据return的返回值来判断是否继续执行onclick事件的其它内容,比如对于<a>
标签的单击事件,浏览器的默认动作是进行页面跳转。
3.JS获取元素的left属性为NaN
我遇到的问题是在使用JS获取定位为relative的<li>
元素时,解析返回值是一个NaN。我获取left属性的代码如下。
function getStyle(obj,attr){
//for ie
if(obj.currentStyle)
return obj.currentStyle[attr];
else
return getComputedStyle(obj,false)[attr];
}
leftValue=parseInt(getStyle(obj,"left"));//leftValue=NaN
问题的原因是left和top属性默认是auto,parseInt(‘auto’)返回NaN,所以解决办法就是给元素的left和top显示设置初值,如0px。
错误截图:
4.元素浮动后的定位方式默认是什么?
我们知道CSS中定位方式有默认定位(static),相对定位(relative),绝对定位(absolute),固定定位(fixed)的区别?
默认定位就是元素正常出现在文档流中的静态位置,当使用float之后,元素会脱离文档流,向左或向右浮动,浮动停止的条件有如下三种情况:
(1)碰到包含框;
(2)同级的浮动框;
(3)包含有内容的框,但是框内的内容不会自我调整位置以防止被覆盖。比如<img>
元素是不能调整图片位置来防止被覆盖,但是<p>
可以调整文字内容,以防止被浮动的元素覆盖。
总之,浮动的元素是不会遮盖其它的元素内容的。大家可自行验证。
那么问题来了,浮动的元素的定位方式是什么呢?
我的个人理解是浮动的元素的定位方式就是浮动。其特点就是:脱离文档流,不会撑开父容器,可使用left、right、top和bottom属性进行定位,定位的参考对象就是设置外边距margin的参考对象,注意bottom的参考对象是自己的下边框。在Chrome47和IE9下测试验证,如有错误,后续更正。
5.CSS z-index无效
我遇到的实际问题是一个div作为页面的header,里面包括logo(图片)和导航标题(文字),结果header中的logo图片被背景图片给覆盖了,而导航标题的文字没有被覆盖,但是对div#header设置z-index没有效果。
原来z-index的用法如下:
**作用:**z-index 属性设置元素的堆叠顺序。拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面。
注释:
(1)元素可拥有负的 z-index 属性值。
(2)z-index仅能在非静态定位元素上奏效(例如position:absolute;position:relative;position:fixed;)!
6.JS事件函数定义的变量是局部变量还是全局变量
先看一段代码:
<!doctype html>
<html>
<head>
</head>
<body>
<div id="testDiv">
<span>lvlv0</span>
<span>lvlv1</span>
</div>
</body>
</html>
<script>
function closure(){
var localVar=1;
var div=document.getElementById("testDiv");
var spanArray=div.getElementsByTagName("span");
for(var i=0;i<spanArray.length;++i){
spanArray[i].onclick=function(){
var nestedLocalVar=i;
alert(nestedLocalVar);
};
}
alert(nestedLocalVar);//Uncaught ReferenceError: nestedLocalVar is not defined
}
closure();
</script>
alert访问事件函数中定义的变量nestedLocalVar会报Uncaught ReferenceError: nestedLocalVar is not defined
错误,所以JS事件函数定义的变量是局部变量。
JavaScript中循环给元素添加onclick事件局部变量的值均相同的怪异现象!
故事还没有结束,现在注释掉alert,然后单击任意页面中的span,你觉得会弹出什么值呢?0,1或者是2。结果可能让你诧异,全部都是2。上面不是说nestedLocalVar是事件函数的局部变量吗,为什么两次弹出的值相同而且都是诡异的2。
原因是暂时还未弄明白。但是有两个解决办法。
(1)使用立即执行函数
for(var i=0;i<spanArray.length;++i){
spanArray[i].onclick=(function(i){
return function(){
var nestedLocalVar=i;
alert(nestedLocalVar);
};
})(i);
}
(2)给span添加一个属性,点击时调取对象的属性值。
for(var i=0;i<spanArray.length;++i){
spanArray[i].index=i;
spanArray[i].onclick=function(){
alert(this.index);
};
}
7.JS函数参数是值传递还是引用传递
值传递还是引用传递对不同的数据类型有不同的效果。先说一下JS中的有哪些数据类型。
W3CSchool规定JS的数据类型有:
(1)数字(Number)
(2)布尔(Boolean)
(3)数组(Array)
(4)字符串(String)
(5)Null
(6)Undefined
(7)对象(Object)。
我们可以将上面的数据类型两类,基本数据类型和Object,上面的对象指的是数据类型,而不是数据类型的实例,切勿混淆。这里的Object(对象)这种类型指的就是JSON。除了Object,都是基本数据类型。
注意:
(1)JavaScript变量均为对象。当您声明一个变量时,就创建了一个新的对象。
(2)JavaScript拥有动态类型。这意味着相同的变量可用作不同的类型:
var x // x 为 undefined
var x = 6; // x 为数字
var x = "Bill"; // x 为字符串
那么不同数据类型的对象在传参时是值传递还是引用传递呢?下面一一分析。
(1)数字(Number)
var x=99;
//or
var x=new Number(99);
function setNumber(number) {
number=100;
return obj;
}
setNumber(x);
console.log(x); //99
不改变实参,很明显,值传递。
(2)布尔(Boolean)
var bool=true;
//or
var bool=new Boolean(true);
function setBool(bool) {
bool=false;
return obj;
}
setBool(bool);
console.log(bool); //true
不改变实参,很明显,值传递。
(3)数组(Array)
var array=["lvlv","ganshicheng"];
//or
var array=new Array("lvlv","ganshicheng");
function setArray(array){
array[0]="wanghu";
array=["cat","dog"];
console.log(array); //["cat","dog"]
}
setArray(array);
console.log(array); //["wanghu", "ganshicheng"]
console.log(array[0]) //wanghu
很明显数组的内容被改变,所以数组是引用传递。但是数组对象本身没有被改变。
(3)字符串(String)
var string="lvlv";
//or
var string=new String("lvlv");
function setString(string){
string[0]="g"; //此句要注意,不应该这么写
console.log(string[0]); //l
string="cat";
console.log(string); //cat
}
setString(string);
console.log(string); //String {0: "l", 1: "v", 2: "l", 3: "v", length: 4, [[PrimitiveValue]]: "lvlv"}
console.log(string[0]) //l
可见字符串的内容是不会被改变的,所以是值传递。
注意:
JavaScript语言规定:JS字符串定义后不可改变,因此没有办法让string的某个字符发生更改,所以不能使用下标来改变字符串的某个字符,即使这样写也不会报语法错误,只是没有效果。但是我们可以使用下标来访问字符串的某个字符。
(4)对象(Object)
var obj={lvlv:24,gan:25};
//or
var obj = new Object({lvlv:24,gan:25});
function setJSON(json){
json.lvlv=18;
json={cat:18,dog:19};
console.log(json); //Object {cat: 18, dog: 19}
}
setJSON(obj);
console.log(obj.lvlv); //18
console.log(obj); //Object {lvlv: 18, gan: 25}
可见,JSON对象同数组一样是引用传递,对象内容被改变,对象本身不会被改变。
对象本身不会被改变的原因是引用本质上存放的是变量的地址(CC++中是这样子的,JS有待考证,但按照这个就能解释JS对象本身为何不会被改变)。引用传递本质上传递的是变量的地址,因此地址指向的内容可以被改变,但因变量的地址是值传递,所以变量的地址不会被改变,也就是对象本身(对象的地址)不会被改变。
8.解决document.body is null的问题
在开发js中有用到document.body.appendChild的方法,提示”Uncaught TypeError: Cannot read property ‘appendChild’ of null”的错误,错误的意思就是无法访问null的属性appendChild,也就是说document.body is null。出现这种错误的原因是DOM没有加载完毕,JS代码就访问了DOM,很明显会出现上面的错误。
如果JS使用的JQuery框架,可使用document.ready来解决问题,作用是
$(document).ready(
function(){
//执行的内容
}
);
//或者简写成
$(function(){
//执行的内容
});
JQuery ready()的方法执行的时间就是指Dom Ready,他的作用或者意义就是:在DOM加载完成后就可以可以对DOM进行操作。
一般情况一个页面响应加载的顺序是:域名解析->加载html->加载外部JS和CSS->加载图片等其他信息。
那么DOM Ready应该在“加载JS和CSS”和“加载图片等其他信息”之间,就可以操作DOM了。
如果是原生JS,可以使用window.onload事件。
window.onload=function(){
//编写代码
}
//等价于JQuery中的写法
$(window).load(function(){
//编写代码
});
在网页中所有元素(包括元素的所有关联文件)完全加载到浏览器后才执行,即JavaScript 此时可以访问网页中的所有元素。
$(document).ready()与window.onload二者的区别:
$(document).ready()方法在DOM完全就绪时就可以被调用。这并不意味着这些元素关联的文件都已经下载完毕,举个例子:$(document).ready()方法只要知道DOM就绪就可以操作了,不需要等待所有图片下载完毕。而window.onload事件则需要HTML文档的所有内容与相关联的内容统统加载完成之后才能被触发。
9.为什么img的margin-top无效,margin-bottom有效?img已经设置为block
这个问题很奇怪,困扰了本人很久,在CSDN论坛发帖之后,终于找到了问题的症结。原来这是CSS的外边距合并特性。外边距合并指的是,当两个垂直外边距相遇时,它们将形成一个外边距。合并后的外边距的高度等于两个发生合并的外边距的高度中的较大者。W3CSchool有介绍,见CSS 外边距合并。
解决办法:既然CSS不允许同时设置img和img父元素的margin,那么可以对img父元素设置padding-top。
参考文献
[1]JavaScript学习笔记(十四) 立即执行函数
[2]js事件处理函数中return的作用
[3]CSS z-index 属性
[4]JavaScript中函数参数的值传递和引用传递
[5]解决document.body is null的有关问题
[6]