【转】 前端笔记之JavaScript(七)深入函数&DOM那点事
【转】 前端笔记之JavaScript(七)深入函数&DOM那点事
一、函数补充
1.1 arguments类数组对象
arguments 是一个对应于传递给函数的参数的类数组对象。
在函数中,使用特殊对象 arguments,开发者无需明确指出参数名,就能访问它们。
在其它编程语言中,比如java:如果一个函数被定义过两次,每次参数个数都不同。相当于定义了两个不同的函数,根据参数个数不同,会选择不同的函数执行。一个函数名定义了两个函数,称为“函数重载”(overloaded)。
复习一下函数,定义函数时,参数要罗列在圆括号中,这些参数,叫“形参”:
function fun(a,b,c){ console.log(a,b,c); } function fun(a,b){ console.log(a,b); } fun(1,2); fun(1,2,3); //合法的,参数多了和少了,都不报错。
调用函数时,传的参数叫“实参”,JS不要求形参和实参数量一样多。
实际上,这种叫做方法的重载。在Java中,同一个函数名,但是参数个数不一样,视为是两个函数。
也就是说,Java中能够定义两个同名函数。
同名的两个function,都是fun函数,java允许这样做,但是js没有这种操作。
JavaScript函数中有一个非常强大的参数,就是每一个函数内部,都可以使用arguments这个类数组对象。
这个arguments对象,就是涵盖了所有实参(调用函数时传入的所有实际参数)
function fun(a,b){ console.log(a,b); //等价于arguments[0]和arguments[1] //此时函数内部,arguments就有下标,依次等于实参的值 console.log(arguments); console.log(arguments[0]); console.log(arguments[1]); console.log(arguments[2]); arguments[5] = 88888; console.log(arguments[8]); console.log(arguments.length); //输出实参的个数 console.log(arguments.callee); //callee等价于函数本身 console.log(arguments.callee.length); //callee的length是形参的个数 console.log(arguments.callee.name); //函数的名称 } fun(10,20,30,40,50,88,99,100,999); // console.log(arguments); 由于是函数内部的对象,所以函数外是没有定义的,报错。
arguments的功能,可以模拟函数的重载,使用一个函数根据参数的个数不同,有不同的作用。
比如我们设计一个sum函数:
如果传进来一个参数,就得到当前值+1
如果传进来两个参数,就返回两个数的和
function sum(a,b){ switch(arguments.length){ case 1: return ++a; break; case 2: return a + b; break; } // if(arguments.length == 1){ // return ++a; // }else if(arguments.length == 2){ // return a + b; // return arguments[0] + arguments[1]; // } } console.log(sum(10)); console.log(sum(10,88));
设计一个无限参的函数:累加和
function sum(){ var count = 0; //累加器 for(var i = 0; i < arguments.length;i++){ count += arguments[i]; } return count; } console.log(sum(10,20,30,88,99,100,888,999));
设计一个函数,check如果只有一个参数,那么就检测这个数是否是偶数,如果有两个参数,检测这两个参数奇偶性是否相同:
function check(){ switch(arguments.length){ case 1: return arguments[0] % 2 == 0 ? true : false; break; case 2: return (arguments[0] + arguments[1]) % 2 == 0 ? true : false; break; default: // console.log("输入错误,请检测,只支持1到2个参数!"); throw new Error("输入错误,请检测,只支持1到2个参数!"); } } console.log(check(8)); console.log(check(8,8)); console.log(check(8,8,8));
1.2 IIFE
IIFE:Immediately-Invoked Function Expression,即时调用函数表达式。
如果一个函数,在定义的时候,就想直接调用它,就是一个IIFE。
函数执行方法:在函数名或变量名后面加()运算符。
函数关键字形式:定义时直接在后面加()执行。
function fun(){ console.log("哈哈"); }(); |
不能给函数关键字方法直接()调用。
函数表达式形式:能立即执行,函数在赋值给变量时就已经被矮化成表达式了,可以直接在后面加圆括号执行
var fun = function(){ console.log("哈哈"); }(); |
结论:如果函数能被矮化成表达式,就能在定义时直接调用,就能实现IIFE。
函数矮化为表达式的方法:在函数定义之前加运算符。
数学运算符中:+ - ()符号,其他不能用。
逻辑运算符中:!
其他:~
注意也可以不写函数名。
+function (){ console.log("哈哈"); }(); -function (){ console.log("哈哈"); }(); !function (){ console.log("哈哈"); }(); ~function (){ console.log("哈哈"); }(); (function (){ console.log("哈哈"); })(); (function (){ console.log("哈哈"); }());
IIFE还可以键函数传参,IIFE能够关住函数的作用域。在使用某个变量时,如果变量定义在函数内,或函数的参数,只会使用立即调用的变量值,不会对被人造成影响。
(function fun(a){ console.log(a); })(8); fun(5); //IIFE关住fun的作用域,外面找不到fun函数的定义
IIFE可以不写函数名,写匿名函数;IIFE关住函数的作用域,可以用于解决函数闭包带来的错误影响。
最标准常用的IIFE格式:所以IIFE里面的函数,都是匿名函数。
(function fun(a){ console.log(a); })(8); |
1.3结合数组观察闭包
我们说过,数组中,什么都可以放,比如String、Number、Boolean、undefined、function等。
通过动态的方法,遍历数组,给数组每一项添加一个函数:
var arr = []; //空数组接收函数的定义 for(var i = 0;i <= 10;i++){ //函数定义时,作用域全局的,全局中有个变量i //函数值只是定义给数组的每一项,并不会执行 //函数这个闭包,记住了全局作用域,里面有个i,记住了自己内部的语句 arr[i] = function(){ console.log(i); } } console.log(arr); arr[0](); //调用了数值中的函数,执行了console.log(i) arr[1](); //调用了数值中的函数,执行了console.log(i) ... arr[9](); //调用了数值中的函数,执行了console.log(i) arr[10]();//调用了数值中的函数,执行了console.log(i)
注意:上面的案例并没有实现想要的结果,我们希望每一项输出时,i对应的值应该与每次的下标相同,闭包记住仅仅是一个函数的定义,记住内部的语句输出i。
输出的都是11,而不是预想的0~10,原因是每个函数定义时,都产生闭包,函数只认识i,而不是把i这个值复制一份记忆住,而是动态的认识这个i。i调用时得几,这个函数的i就是几。
所以在调用函数时,i已经变成11,所以数组中每个函数都是11。
想要实现输出i与下标相同的值,解决方法:
var arr = []; //空数组接收函数的定义 for(var i = 0;i <= 10;i++){ //函数定义时,作用域全局的,全局中有个变量i //函数值只是定义给数组的每一项,并不会执行 //函数这个闭包,记住了全局作用域,里面有个i,记住了自己内部的语句 // (arr[i] = function(){ // console.log("下标:"+ i); // })(); (function(a){ arr[a] = function(){ console.log("下标:"+ a); } })(i); } // console.log(arr); arr[0](); //调用了数值中的函数,执行了console.log(i) arr[1](); //调用了数值中的函数,执行了console.log(i) ... arr[9](); //调用了数值中的函数,执行了console.log(i) arr[10](); //调用了数值中的函数,执行了console.log(i)
二、DOM
前期课程中,学习的ECMAscript语言核心部分,都是在控制台,天天console.log()、prompt()等。
之前学的属于语言,了解JS这个语言的特性,从今天开始要学习JS控制页面上的元素。回归到操作HTML和CSS。
2.1 DOM概述
DOM(Document Object Model,文档对象模型)描绘了一个层次化的节点树,允许开发人员添加、移除和修改页面的某一部分。使用JavaScript操作HTML,不是在操作字符串,而是在操作节点,极大地降低了编程难度。
DOM规范在1998年10月制定,称为“DOM1级规范”。随着ECMAScript的升级,DOM也发展出了2级规范、3级规范。另外,早于1998年的DOM也有事实上的标准,我们称为0级规范。
DOM对很多东西做了抽象,提供了丰富的API:取得元素、css样式、事件、运动、元素尺寸位置、节点操作。每个知识体系都非常庞大,千丝万缕。我们今天的课程,把一些线头都掐出来,日后的课程深入研究每个线头。
<body> <div id="box"></div> <img src="images/ssh.jpg" id="timg" title="邵老师" > </body> <script type="text/javascript"> //获取HTML标签元素 var timg = document.getElementById("timg"); var oBox = document.getElementById("box"); //给图片添加点击事件,让img标签修改src和title属性 timg.onclick = function(){ timg.src = "images/timg.jpg"; timg.title = "考拉吃树叶"; timg.width = "300"; } //点击盒子变色 oBox.onclick = function(){ oBox.style.backgroundColor = "skyblue"; } </script>
2.2获取HTML标签元素
HTML负责页面的布局、结构。JS要操作HTML标签,第一件事就是要得到这个标签。
说白了就是把HTML标签拿到JS里面来操作。
JS中,最基本的得到标签元素的方法有两个:
document.getElementById(); //通id获取一个标签元素 document.getElementsByTagName(); //通过标签名获取一组元素 |
JS通过document对象(表示文档集合),它表示整个页面,有很多属性和方法,包含绝大多数的特征和操作。
学习DOM说白了就是学习document对象
console.log(document.URL); //获取当前文件路径 console.log(document.title); //获取当前页面标题 document.title = "爱前端官网"; //设置当前页面标题 |
DOM操作,往往都是从某个HTML元素开始,然后对这个元素进行一些操作,所以得到元素是非常关键,得到元素的操作可以使用document对象的方法。
注意:
1、方法名称都是驼峰命名法,首个单词字母都是小写,后面的单词首字母都是大写。
2、方法需要通过参数获取元素,参数就是标签的id属性,必须写在引号内,不需要写#
3、获取元素,一定要在HTML元素位置后面,HTML有加载顺序,获取元素时,元素必须已经找到
4、id不能重复,页面中如果设置表单元素的name属性,也不能与id同名,如果重复,只会选择第一个出现。
注意代码书写位置,现在要用JS得到oBox元素,所以JS代码就要写在HTML标签后面,这样浏览器先渲染HTML节点,然后再执行到JS,否则会报错找不到元素。
<script type="text/javascript"> //获取HTML标签元素 var oBox = document.getElementById("box"); oBox.onclick = function(){ oBox.style.backgroundColor = "skyblue"; } </script> <body> <div id="box"></div> </body> |
如果页面上没有匹配id的HTML元素,将返回null,null就是空对象。
通过id获得的元素数据类型:
console.log(typeof oBox); //object |
获取的元素是对象数据类型。
IE7及低版本有一个怪癖,表单元素name属性也会被当做id,为了避免这个问题,所以页面上的name不要和id同名。
<input type="text" name="oInput" value="默认文本"> var oInput = document.getElementById("oInput"); oInput.value = "被js改了";
IE6、7都会把name当做id。
2.3操作HTML(内容和属性)
更多的是操作HTML标签属性,JS可以操作HTML任何属性,比如:src、href、title、style等
操作方法:获取标签属性,更改标签属性。
2.3.1操作HTML内容
通过innerHTML属性获取和修改标签内容:
var oBox = document.getElementById("box"); console.log(oBox.innerHTML); //获取div标签的内容 console.log(oBox.innerText); //获取div标签的文本内容 oBox.innerHTML = "通JS修改标签内容"; //通过=号给oBox对象赋值
2.3.2操作HTML属性
得到一个元素后,直接打点调用它的属性名,就能对HTML相应的属性进行修改。(有什么就点什么)
第一种:“点”语法,获取和更改,都是同元素打点调用。
//获取img标签元素 var oImg = document.getElementById("timg"); //获取标签属性的值 console.log(oImg.src); console.log(oImg.alt); console.log(oImg.title); console.log(oImg.id); console.log(oImg.width); //设置标签属性的值,和变量赋值一样 oImg.src = "images/2.jpg"; oImg.alt = "赵丽颖是大圆脸"; oImg.title = "赵丽颖还是吃货";
class属性,要换成className,因为class是JS的保留字,不能用。
console.log(oImg.className); //获取class类名 oImg.className = "abc"; //设置class类名 |
不仅是class属性要用className这种方式避讳一下,还有以下几个属性:
class 要写成className for 要写成htmlFor rowspan 写成rowSpan colspan 写成colSpan |
第二种:通过以下两个方法获取和设置,需要用元素打点调用方法:
getAttribute() 获取标签元素的属性 setAttribute() 设置标签元素的属性 |
//获取标签属性的第二种方法: var oImg = document.getElementById("timg"); console.log(oImg.getAttribute('src')); //获取图片src属性的路径 oImg.setAttribute('src','images/2.jpg'); //设置图片src属性的路径
getAttribute()、setAttribute()和“点”语法的区别。
第一:所有自定义属性,都不能通过点语法设置
console.log(oImg.data); //undefined自定义属性,不是w3c的属性,所以不能打点调用 console.log(oImg.getAttribute('data')); //自定义属性用getAttribute()方法可以得到 oImg.setAttribute('data','不知道写什么');//自定义属性用setAttribute()方法可以设置 |
第二:所有行内样式,点语法.style得到一个样式对象,我们可以通过.style.background继续得到小样式,但是getAttribute()得到的是字符串。
console.log(oBox.style.background); console.log(oBox.getAttribute("style")); console.log(typeof oBox.style); //object console.log(typeof oBox.getAttribute("style")); //String
第三:getAttribute()、setAttribute()设置和获取class等属性不需要避讳,直接写属性名:
console.log(typeof oBox.getAttribute("class")); typeof oBox.setAttribute("class","box"); //String |
点语法效率高于getAttribute()、setAttribute()
所以,如果要获取自定义属性或操作对象,会用到,除此之外都用点语法。
2.4操作CSS样式
通过给元素对象使用“点”语法调用style属性,得到的是一个所有行内样式组成的样式对象。可以继续去打点调用css样式的属性。
通过点语法调用style属性,调用的和设置的都是行内样式。
var oBox = document.getElementById("box"); console.log(oBox.style); //获取当前div的css样式集合 oBox.style.color = "red"; //设置文字颜色 oBox.style.backgroundColor = "skyblue"; //获取当前div的css样式集合 oBox.style.width = "200px"; oBox.style.height = "200px"; oBox.style.lineHeight = "200px"; oBox.style.textAlign = "center"; oBox.style.marginLeft = "100px";
设置时,添加的新样式都是添加在行内:
获取CSS属性:如果单一属性直接写点语法调用对应的属性名,如果是复合属性,必须用驼峰命名法:
1 font- line- background- padding- margin- border- 等等都要转为驼峰命名法 |
正确写法:
1 oBox.style.backgroundColor = "skyblue"; |
错误写法:
1 oBox.style.background-color = "skyblue"; |
三、事件监听
JavaScript制作交互效果,离不开事件,所谓的事件就是用户的某个行为,能够触发一个函数的执行。
今天只学DOM标准中的0级绑定事件方法。
绑定事件监听方法:如果给一个元素添加某个事件,事件就有自己要执行的事件函数。
事件的定义:当什么时候做什么事情 事件的作用:可以捕获用户的行为 |
事件分为三要素:
事件源:就是这个事件的源头 事件类型:指的是事件什么时候发生(点击、触摸等) 执行指令:匿名函数function(){} |
中文语法解释:
事件源.事件类型 = function(){ } |
语法:
var oBox = document.getElementById('box'); oBox.onclick = function(){ alert("点击事件执行"); //这里的代码要被点击才执行,否则永远不会被触发 }
DOM0级规范的事件类型:
onclick 鼠标单击事件 ondblclick 鼠标双击事件 onmouseover 鼠标移入事件 onmouseout 鼠标移出事件 onmouseenter 鼠标移入事件 onmouseleave 鼠标移出事件 onmousedown 鼠标按下不松手事件 onmouseup 鼠标抬起事件 onmousemove 鼠标移动事件 onfocus 获取焦点事件 onblur 失去焦点事件 onkeydown 键盘按下事件 onkeyup 键盘松开事件 onchange 当元素的值发生变化时触发 onresize 窗口或框架被重新调整大小。 onselect 标签的文本被选中 onload 加载事件(某个对象加载完后,触发的事件) |
所有事件都要被触发才执行,否则匿名函数中的代表永远都不会生效。
onload加载事件:给元素添加一个加载事件时,代码只要执行到这个位置,立即添加事件监听,直到浏览器的元素加载完后,才触发onload事件,不需要用户的行为参与,它是浏览器自己的加载事件。
window.onload = function(){ //这里的代码是当浏览器加载完HTML和CSS才回头执行代码 var oBox = document.getElementById("box"); oBox.onclick = function(){ oBox.style.backgroundColor = "skyblue"; } }
window对象的加载事件,触发条件:所有的HTML和CSS加载完后,才触发
特殊用途:帮我们实现事件函数内部的语句需要在HTML结构加载完后,才执行JS代码。
window 表示浏览器窗口 onload 表示加载后的事件 |
总结:其实就是整个页面都加载完后才执行JS代码,就是执行了一个页面加载完成后的事件。