文章目录
一、函数类型的认识
-
函数的概念
函数是具备某个功能的工具,是完成某个功能的一段代码
-
函数的定义及调用
// 第一种定义方法:声明式/直接创建式 // 定义 function 函数名(){ 代码段 } //调用 函数名() // 第二种定义方法:赋值式/匿名函数 // 定义 var 变量名 = function(){ 代码段 } // 调用 变量名() // 案例:定义函数求两个数字的和 function fn(){ var a = 1,b = 2,c = a+b; console.log(c); } fn();
函数的注意点:
- 注意单词、格式、语法、符号
- 注意执行函数的位置,不同的创建方式可执行的位置不同
- 注意行为调用函数时的格式
- 注意不要概念复杂化
函数的优点:
- 实现了代码的可重用性
- 实现了模块化编程
函数的调用执行拓展:通过事件来调用匿名函数(绑定事件)
<body> <button onclick="fn()">点击调用函数</button> </body> <script> function fn(){ var a = Number(prompt("输入第一个数")) var b = Number(prompt("输入第二个数")) alert(a+b) } </script>
二、函数的参数应用
function fn(a){//形参:形式上的参数
console.log(a)
}
fn(10)//实参:实际上的参数
//需要根据参数不同显示不同的乘法表
function fn(num) {
document.write('<table>')
for (var i = 1; i <= num; i++) {
document.write('<tr>')
for (var j = 1; j <= i; j++) {
document.write('<td>' + j + 'x' + i + "=" + i * j + '</td>')
}
document.write('</tr>')
}
document.write('</table>')
}
var num=Number(prompt("输入一个数字"))
fn(num)
参数的概念
- 形参:形式参数,作用是接收参数
- 实参:真实参数,作用是传递参数
参数使用注意点
- 函数的参数有多个的时候,需要用逗号隔开
- 在形参中书写变量,表示在函数体内声明了变量,此时还没有赋值,数据类型为undefined
- 当传入实参的时候,表示给形参的变量赋值
参数数量的问题
- 原则上,函数的形参和实参的个数和数据类型都要一一对应的
- 如果实参的数量比形参数量少,从左到右依次选择,没有参数的形参默认是undefined
- 如果实参的数量比形参数量多,从左到右依次选择,多的形参对函数体内的结果不产生任意的影响,但此时函数会默认提供一个内置对象
函数内置对象的拓展【类数组/伪数组】
- arguments.length 获取传入参数的个数
- arguments[索引] 通过索引获取对应的参数
三、函数的返回值
函数运行结束之后得到的结果
// 案例1:定义一个让指定数字递增的函数
function increase(x){
var y = x+1;
return y//return 程序结束后,后面的代码就不会执行了
}
// 1. 让2递增,得到的结果赋值给变量a
var a = increase(2);
document.write(a%2);//1
// 2. 让5递增,得到的结果赋值给变量b
var b = increase(5);
alert(b/2);//3
// 3. 让10递增,得到的结果赋值给c
var c = increase(10);
console.log(c+a-b);//11+3-6 = 8
// 案例2:定义三个变量,先求和再求平均数
var a = Number(prompt("输入第一个数"))
var b = Number(prompt("输入第二个数"))
var c = Number(prompt("输入第三个数"))
function sum(a,b,c){
return (a+b+c)
}
function avg(a,b,c){
return (a+b+c)/3
}
document.write('和是'+sum(a,b,c)+'<br>')
document.write('平均是'+avg(a,b,c))
函数的嵌套【拓展】
由于函数的调用可以写在页面的任意地方,在另外一个函数里面调用执行一个函数也可行
用函数嵌套的方式实现上面的案例2
function sum(a,b,c){ return (a+b+c) } function avg(a,b,c){ document.write('和是'+sum(a,b,c)+'<br>') return (a+b+c)/3 } document.write('平均是'+avg(a,b,c))
四、函数的预解析
-
浏览器中有一段程序专门用来解析JS代码,叫做JS解析器。js解析器在执行js代码的时候,分两步进行:
- 预解析js代码:预解析的过程,就是查找代码中的var和function这两个关键字,找到以后,将变量和函数存在了一个地方,并给他们赋一个初始值,变量的初始值为undefined,函数的初始值为代码段
- 开始按顺序一行一行解读代码:解读代码的时候,仓库中的变量和函数的值会随着代码的解读而发生变化,也就是变量的赋值和函数的调用
-
预解析分为变量的预解析和函数的预解析,也就是代码在执行之前先进行解析,这步是不可见的
-
总结:预解析就是将变量和函数的声明放在当前作用域的最前面,也可以叫做变量提升
变量赋值和函数的调用保留在原来的位置不动,其他代码正常运行
// 案例1:变量提升
console.log(a)
var a = 10
//undefined 提升的是定义,不是赋值
// 案例2:声明式函数提升
fn()
function fn(){
console.log('Hello')
}
//正常输出
// 案例3:赋值式函数提升
fn()
var fn = function(){
console.log('Hello')
}
//报错 提升的是var fn,没有赋值。调用fn()的时候报错,不会再往下运行
变量提升总结:
- 用var关键字声明的变量,将变量的声明提升到当前作用域的最前面,赋值不提升
- 自定义的函数整体提升到当前作用域的最前面
- 函数同名,后面的会覆盖前面的
- 变量和函数同名,函数优先提升
// 第一题
console.log(fn)
fn()
var fn = function(){
console.log(123)
}
// undefined,报错
// 赋值函数,提升的是var fn,此时fn是undefined
// 第二题
fun()
var fn = function(){
console.log('我是fn')
}
function fun(){
console.log('我是fun')
}
fn()
fn = 100
fn()
// 我是fun,我是fn,报错(fn赋值了100,不是函数了)
五、函数的作用域
- 全局作用域:不在任何函数中
- 局部作用域:在函数的内部中
1. 局部作用域
function fn(){
var a = 1//局部变量
console.log(a) //1
}
fn()
console.log(a)//在函数体的外面,并没有声明定义a变量,所以得到的结果就是报错
局部作用域总结:
- 在函数内部定义的变量叫做局部变量,只能在函数内部使用
- 全局变量不可以访问局部变量
2. 全局作用域
var b = 1 //全局变量
function fn(){
var b = 2 //局部变量
}
fn()
console.log(b)
全局作用域总结:
- 在函数外面定义的变量叫做全局变量
- 当函数内部声明变量没有使用var的时候,情况比较特殊
-
特殊情况
//特殊情况:函数内部声明的变量也是全局变量 var c=1 function fn(){ c=2//当函数内部声明没有使用关键字的时候是会往上级查找的,可以理解为是全局变量 //这种声明方式是“隐式声明” } console.log(c)
六、函数访问赋值规则
1. 访问规则
function f1(){
var a = 2
function f2(){
console.log(a)//2
}
f2()
}
f1()
访问规则的总结
- 如果当前作用域有定义的时候就会直接使用当前作用域的变量
- 如果当前作用域没有定义就会往上级查找,上级没有就继续往上,直到找到全局
- 如果全局有就输出,没有就报错
2. 赋值规则
//案例1
function f1(){
function f2(){
a = 1
//1. 加了var关键字代表局部变量
//2,不加var关键字 隐式声明,全局作用域
}
f2()
}
f1()
console.log(a)
//案例2
function f1(){
var a = 1
function f2(){
a = 2
console.log(a)
}
f2()
console.log(a)
}
f1()
console.log(a)
// 2 2 报错
赋值规则总结
- 往上级查找,上级存在可以重新赋值,上级没有就一直往全局查找
- 如果全局有就使用全局中的变量
- 如果全局没有定义变量,查找到全局的时候,会自己在全局定义一个变量
// 练习1
var num = 10;
fn1();
function fn1(){
console.log(num);
var num = 20;
}
//undefined
// 练习2
fn2();
var a = 18;
function fn2(){
var b = 9;
console.log(a);
console.log(b);
}
// 练习2 预解析过程
var a
function fn2(){
var b
b=9
console.log(a)
console.log(b)
}
fn2()
a=18
//undefined 9
// 如果a的赋值在函数调用之前,就会是18
// 练习3
fn3();
console.log(c);
console.log(b);
console.log(a);
function fn3(){
var a=b=c=9;
console.log(a);
console.log(b);
console.log(c);
}
// var a=b=c=9; 理解为 var a=9;b=9;c=9 所以a是局部作用域
//9 9 9 9 9 报错
// 练习4
var a = 4;
console.log(a);
a = 6
console.log(a);
function a(){
console.log('哈');
}
a();
a = 10;
console.log(a);
// 练习4 预解析
var a
function a(){
console.log('哈')
}
a=4
console.log(a);
a=6
console.log(a);
a();//此时a是6 以函数的方式调用,会报错
a=10;
console.log(a)
// 4 6 报错
七、递归函数
- 在函数中调用自己
- 类似于循环
- 使用递归函数要注意返回,否则会造成死递归
- 递归比较消耗性能,尽量少用
//递归的语法
function fn(){
fn()
}
fn()
//常见的递归
function fn(a){
if(a==1){
return 1
}
return fn(a-1)
}
var res = fn(10)
console.log(res)
//案例1:使用递归求1-5的和
function fn(a){
if(a==1){
return 1
}
return (a+fn(a-1))
}
var res = fn(5)
console.log(res)
// 案例2:斐波那契数列
function fn(a){
if(a==1||a==2){
return 1;
}
return fn(a-1) + fn(a-2)
}
var res=fn(10)
console.log(res)
八、事件的了解
事件触发函数
<body>
<button id="btn1">
声明式函数触发
</button>
<button id="btn2">
赋值式函数触发
</button>
</body>
<script>
//声明式函数触发
function fn(){
alert("我是声明式函数")
}
btn1.onclick=fn
//赋值式函数触发
btn2.onclick=fn(){
alert("我是赋值式函数")
}
</script>
<body>
<input type="text" placeholder="请输入一个数">
<button>
计算
</button>
</body>
<script>
// 案例:触发事件求阶乘
// 1. 计算阶乘;2. 给标签绑定id,事件方式触发函数;3. 获取用户输入的值 DOM.value
function fn(num){
if(num == 1){
return 1
}
return num * fn(num-1)
}
fn(5)
</script>
九、对象数据类型
对象的概念:用来储存数据,可以理解为容器
1. 对象的表现形式
var obj = {} //对象
var arr = [] //数组
var none = null //空
2. 对象的创建方式
//第一种:声明式/直接创建式
var obj = {}
//第二种:构造函数方式/new方式
var obj = new Object()
3. 对象存储数据的格式
//第一种:声明式/直接创建式
var obj = {
键1:值1,
键2:值2,
...
}
//第二种:构造函数方式
var obj = new Object({
键1:值1,
键2:值2,
...
})
对象存储数据的语法规则:键值对
- 键(key):也叫做对象的属性,必须是字符串,但是这个字符串在不包含连字符的时候可以不加引号,建议加上。如果键有多个单词有连接符的时候,要注意加上“”,或者使用驼峰命名
- 值(value):可以是任意类型,包括:数字、字符串、布尔、对象、函数
- 键值对之间使用逗号隔开
- 键值对之间没有顺序,是一个无序的数据集合
4. 对象的访问
var person = {
"name":"小明",
"age":10,
"background-color":"green"
}
console.log(person.name,person.age)
console.log(person['name'],person['age'],person['background-color'])
console.log(preson['aa'])
对象访问的注意点
- 两种方式都可以访问对象中的数据,通过键获取得到的就是键值对的值
- 对象.键 这种的获取方式,如果键有连接符号的时候会报错,获取不到
- 如果键有连接符号的时候,需要使用 对象[“键”] 这种方法获取
- 对象中没有这个键的时候,返回的是undefined
5. 对象的遍历
for(var attr in obj){
//输出需要的数据
console.log(attr)
console.log(attr[i])
}
//attr表示对象属性名,obj表示这个对象
对象遍历的注意点
- 遍历出来的数据是一个变量 对象.键 这种方式获取不到
- 遍历出来的数据是一个变量 对象[键] 这种方式是可以获取得到的,并且引号不用添加
- 如果使用for in这种方式遍历对象的话,建议使用 对象[键] 这种方式进行获取数据
6. 对象的方法
- 对象中的值可以是任意的数据类型
- 值可以是一个对象,还可以是一个函数(function=>方法)
//1.定义一个对象
var person = {
name:'小明',
age:26,
sex:'男',
// 2. 在对象中再嵌套一个对象
brother:{
name:'大明',
age:30,
}
// 3. 对象中的值还可以是一个函数,可以理解为对象中的方法
eat:function(){
console.log("吃饭")
}
}
console.log(person.brother.name)
//1. 之前调用函数的方法是:函数名称()
//2. 在对象中调用函数方法:键值对中的键()
person.eat()
7. 对象的操作
- 增加 对象.键的名字 = 值
- 修改 对象.键的名字 = 值
- 删除 delete 对象.键的名字
- 查询 对象.键的名字 || 对象[‘键的名字’]
var obj = {
name = '小明',
age = 18,
gender = '女'
}
obj.money = 0 //增加
obj.money = 2000 //修改
delete obj.money //删除
obj = {} //置空
for(var i in obj){ //置空
delete obj[i]
}
console.log(obj)
8. 构造函数【拓展】
只要使用了new方式创建的方法都是构造函数。任意的数据类型都可以用构造函数去创建
//1.使用构造函数创建对象
var obj = new Object()
console.log(obj)
//2.利用构造函数方式创建函数
function Fn(){}
var a = new Fn()
console.log(a)
//3.利用构造函数创建数据类型
var num = new Number()
console.log(num)
//指向window 默认情况下,this指向全局
console.log(this)
总结:关于构造函数和普通函数的区别
- 有没有new关键字
- 关于this指向的问题
- 首字母是否大写是区别构造函数和普通函数的最直观的表现