函数
函数的概念
-
对于
js
来说,函数就是把任意一段代码放在一个 盒子 里面 -
在我想要让这段代码执行的时候,直接执行这个 盒子 里面的代码就行
-
先看一段代码
// 这个是我们以前写的一段代码 for (var i = 0; i < 10; i++) { console.log(i) } // 函数,这个 {} 就是那个 “盒子” function fn() { // 这个函数我们以前写的代码 for (var i = 0; i < 10; i++) { console.log(i) } } //函数分为两种 // 系统函数 比如 prompt() alert() // 自定义函数 当系统函数不满足我们的需求 我们可以自己定义函数
函数的两个阶段(重点)
-
按照我们刚才的说法,两个阶段就是 放在盒子里面 和 让盒子里面的代码执行
-
内存分为栈内存和堆内存
栈内存 基本数据类型 函数名在 栈内存
堆内存 复杂数据类型 引用数据类型 函数体 在堆内存
函数定义阶段
-
定义阶段就是我们把代码 放在盒子里面
-
我们就要学习怎么 放进去,也就是书写一个函数
-
我们有两种定义方式 声明式 和 赋值式
1-1 现在堆内存中开辟空间
1-2 把函数体内的代码 原样复制到空间内(我们写代码写在硬盘上)
1-3 把内存的地址 赋值给栈内存的函数名
// 变量的定义 // 定义在哪个作用域下 这个变量就是哪个作用域的私有变量 // 只能在这个作用域下或者这个作用域的子级作用域内使用 // 不能在父级作用域使用 = > var n1 = 100; function fn1(){ var n2 = 200; // n2是fn1的私有变量 只能在fn1下 或者fn1的子级下面使用 console.log(n2); console.log(n1); } fn1() // console.log(n2); //变量的访问 // 首先在自己的作用域查找 如果有 那么直接使用 // 如果没有自动到父作用域查找 父作用域有 那么就使用 // 如果还没有 再往上一级 直到全局作用域 发现还没有 那么 报错 // xx is not defined var n = 100; function fn2(){ var n = 200; function fn3(){ var n = 300; console.log(n); } fn3(); } //fn2() //fn3() fn2() // 变量的赋值 // 先在自己的作用域内部查找 如果有直接赋值 // 如果没有自动到父作用域查找 父作用域有 那么就使用 停止查找 // 父作用域如果没有再往上到父级 直到 全局作用域 // 那么就把这个变量定义为全局变量 再进行赋值 var n = 100; function fn1(){ var n = 200; console.log('前',n); n = 500 console.log('后',n); } console.log('全局',n); fn1() console.log('全局后',n);
声明式
-
使用
function
这个关键字来声明一个函数 -
语法:
<script> //声明式 function story(){ document.write('一言之美,贵于千金.——葛洪') document.write('人无忠信,不可立于世.——程颐') document.write('诚信为⼈之本。——鲁迅') document.write('⾔必信,⾏必果。——⼦路') }// function: 声明函数的关键字,表示接下来是一个函数了 // story: 函数的名字,我们自己定义的(遵循变量名的命名规则和命名规范) // (): 必须写,是用来放参数的位置(一会我们再聊) // {}: 就是我们用来放一段代码的位置(也就是我们刚才说的 “盒子”) </script>
赋值式
-
其实就是和我们使用
var
关键字是一个道理了 -
首先使用
var
定义一个变量,把一个函数当作值直接赋值给这个变量就可以了 -
语法:
<script> //赋值式 var storle=function(){ document.write('不信不⽴,不诚不⾏。——晁说之') document.write('读书贵精不贵多。——书摘') document.write('真诚是⼀种⼼灵的开放。——拉罗什富科') document.write('腹有诗书⽓⾃华,读书万卷始通神。——苏轼') }// 不需要在 function 后面书写函数的名字了,因为在前面已经有了 </script>
函数调用阶段
-
就是让 盒子里面 的代码执行一下
-
让函数执行
-
两种定义函数的方式不同,但是调用函数的方式都以一样的
2-1 按照函数名存的地址去找到对应的函数体
先判断栈内存中是否有这个变量
如果存在 也会判断里边是否存储的还一个函数体
2-2 在调用栈 内开辟一个新的 函数执行空间 (一个函数可以被多次调用为了避免一个调用出现的结果影响所有的调用
所以每次的调用都会申请一个执行空间)
2-3 在执行空间内 给形参赋值
2-4 在执行空间内 对函数体内的代码进行预解析
2-5 在执行空间内 对函数体内的代码 执行一遍
2-6 完成以后 这个执行空间被销毁 或者理解为空间被收回
调用一个函数
-
函数调用就是直接写
函数名()
就可以了// 声明式函数 function fn() { console.log('我是 fn 函数') } // 调用函数 fn() // 赋值式函数 var fn2 = function () { console.log('我是 fn2 函数') } // 调用函数 fn()
-
注意: 定义完一个函数以后,如果没有函数调用,那么写在 {} 里面的代码没有意义,只有调用以后才会执行
-
调用上的区别
-
虽然两种定义方式的调用都是一样的,但是还是有一些区别的
-
声明式函数: 调用可以在 定义之前或者定义之后
-
// 可以调用 fn() // 声明式函数 function fn() { console.log('我是 fn 函数') } // 可以调用 fn()
-
赋值式函数: 调用只能在 定义之后
// 会报错 fn() // 赋值式函数 var fn = function () { console.log('我是 fn 函数') } // 可以调用 fn()
函数的参数(重点)
-
我们在定义函数和调用函数的时候都出现过
()
-
现在我们就来说一下这个
()
的作用 -
就是用来放参数的位置
-
参数分为两种 行参 和 实参
// 声明式 function fn(行参写在这里) { // 一段代码 } fn(实参写在这里) // 赋值式函数 var fn = function (行参写在这里) { // 一段代码 } fn(实参写在这里)
行参和实参的作用
-
行参
-
就是在函数内部可以使用的变量,在函数外部不能使用
-
每写一个单词,就相当于在函数内部定义了一个可以使用的变量(遵循变量名的命名规则和命名规范)
-
多个单词之间以
,
分隔// 书写一个参数 function fn(num) { // 在函数内部就可以使用 num 这个变量 } // 书写两个参数 function fun(num1, num2) { // 在函数内部就可以使用 num1 和 num2 这两个变量 }
-
如果只有行参的话,那么在函数内部使用的值个变量是没有值的,也就是
undefined
-
行参的值是在函数调用的时候由实参决定的
-
-
实参
-
在函数调用的时候给行参赋值的
-
也就是说,在调用的时候是给一个实际的内容的
function fn(num) { // 函数内部可以使用 num } // 这个函数的本次调用,书写的实参是 100 // 那么本次调用的时候函数内部的 num 就是 100 fn(100) // 这个函数的本次调用,书写的实参是 200 // 那么本次调用的时候函数内部的 num 就是 200 fn(200)
-
函数内部的行参的值,由函数调用的时候传递的实参决定
-
多个参数的时候,是按照顺序一一对应的
function fn(num1, num2) { // 函数内部可以使用 num1 和 num2 } // 函数本次调用的时候,书写的参数是 100 和 200 // 那么本次调用的时候,函数内部的 num1 就是 100,num2 就是 200 fn(100, 200)
-
参数个数的关系
-
行参比实参少
-
因为是按照顺序一一对应的
-
行参少就会拿不到实参给的值,所以在函数内部就没有办法用到这个值
-
行参比实参多
-
因为是按照顺序一一对应的
-
所以多出来的行参就是没有值的,就是
undefined
函数的return(重点)
-
return
返回的意思,其实就是给函数一个 返回值 和 终断函数
终断函数
-
当我开始执行函数以后,函数内部的代码就会从上到下的依次执行
-
必须要等到函数内的代码执行完毕
-
而
return
关键字就是可以在函数中间的位置停掉,让后面的代码不在继续执行
返回值
-
函数调用本身也是一个表达式,表达式就应该有一个值出现
-
现在的函数执行完毕之后,是不会有结果出现的
-
return
关键字就是可以给函数执行完毕一个结果function fn() { // 执行代码 return 100 } // 此时,fn() 这个表达式执行完毕之后就有结果出现了 console.log(fn()) // 100
-
我们可以在函数内部使用
return
关键把任何内容当作这个函数运行后的结果
预解析
-
在代码执行之前 对代码进行通读并且解释
-
只解析两部分内容
-
var 声明的变量名; => 仅仅是对var 声明的变量提前声明 暂时不赋值
-
声明式的函数
=> 对函数名字提前声明 并且直接给这个函数名赋值了 一个函数
=> 赋值式的函数按照 var变量的规则进行 解释
会经历两个环节 解释代码 和 执行代码
注意点: 打开浏览器 只会解析全局代码 不对函数内的代码进行预解析 函数内的代码 仅仅会在函数调用的时候才会 预解析
<script> function fn(){ var num = 100; console.log(num) } // 如果仅仅写了上面代码 没有调用 也会预解析 // 声明变量 并且将 函数赋值给它 这时候不解析函数内部 // fn() // 当调用的时候才解析函数内容不 // 预解析 // 直接声明一个fn的变量 并且将一个函数赋值给它 //代码执行 // fn() // 函数内部 预解析 // 解析var 变量 仅仅声明 没有赋值 // 函数内部执行 // num = 100 // 输出到控制台 </script>
-
看下面一段代码
fn() console.log(num) function fn() { console.log('我是 fn 函数') } var num = 100
-
经过预解析之后可以变形为
function fn() { console.log('我是 fn 函数') } var num fn() console.log(num) num = 100
-
作用域
变量不是在所有地方都可以使用的,而这个变量的使用范围就是作用域
全局作用域
-
全局作用域是最大的作用域
-
在全局作用域中定义的变量可以在任何地方使用
-
页面打开的时候,浏览器会自动给我们生成一个全局作用域
window
-
这个作用域会一直存在,直到页面关闭就销毁了
/全局作用域 // 一个html 页面打开这就是一个全局作用域 // => window (了解) // 私有作用域 局部作用域 // 只有函数才有 if while do while for 他们都没有局部作用域 // 作用域的上下级关系 // 定义在哪个作用域内的函数 就是哪个作用域的子级作用域
// 下面两个变量都是存在在全局作用域下面的,都是可以在任意地方使用的 var num = 100 // 私人可以使用公家的东西 var num2 = 200 // 公家不能随便使用私人的东西
局部作用域
-
局部作用域就是在全局作用域下面有开辟出来的一个相对小一些的作用域
-
在局部作用域中定义的变量只能在这个局部作用域内部使用
-
在
JS
中只有函数能生成一个局部作用域,别的都不行 -
每一个函数,都是一个局部作用域
// 这个 num 是一个全局作用域下的变量 在任何地方都可以使用 var num = 100 function fn() { // 下面这个变量就是一个 fn 局部作用域内部的变量 // 只能在 fn 函数内部使用 var num2 = 200 } fn()
变量使用规则(重点)
-
有了作用域以后,变量就有了使用范围,也就有了使用规则
-
变量使用规则分为两种,访问规则 和 赋值规则
变量的定义
//定义在哪个作用域下 这个变量就是哪个作用域的私有变量 // 只能在这个作用域下或者这个作用域的子级作用域内使用 // 不能在父级作用域使用 = > var n1 = 100; function fn1(){ var n2 = 200; // n2是fn1的私有变量 只能在fn1下 或者fn1的子级下面使用 console.log(n2); console.log(n1); } fn1() // console.log(n2);
访问规则
-
当我想获取一个变量的值的时候,我们管这个行为叫做 访问
-
获取变量的规则:
-
首先,在自己的作用域内部查找,如果有,就直接拿来使用
-
如果没有,就去上一级作用域查找,如果有,就拿来使用
-
如果没有,就继续去上一级作用域查找,依次类推
-
如果一直到全局作用域都没有这个变量,那么就会直接报错(该变量 is not defined)
var num = 100 function fn() { var num2 = 200 function fun() { var num3 = 300 console.log(num3) // 自己作用域内有,拿过来用 console.log(num2) // 自己作用域内没有,就去上一级,就是 fn 的作用域里面找,发现有,拿过来用 console.log(num) // 自己这没有,去上一级 fn 那里也没有,再上一级到全局作用域,发现有,直接用 console.log(a) // 自己没有,一级一级找上去到全局都没有,就会报错 } fun() } fn() var n = 100; function fn2(){ var n = 200; function fn3(){ var n = 300; console.log(n); } fn3(); } //fn2() //fn3() fn2()
-
-
变量的访问规则 也叫做 作用域的查找机制
-
作用域的查找机制只能是向上找,不能向下找
function fn() { var num = 100 } fn() console.log(num) // 发现自己作用域没有,自己就是全局作用域,没有再上一级了,直接报错
赋值规则
-
当你想给一个变量赋值的时候,那么就先要找到这个变量,在给他赋值
-
变量赋值规则:
-
先在自己作用域内部查找,有就直接赋值
-
没有就去上一级作用域内部查找,有就直接赋值
-
还没有再去上一级作用域查找,有就直接赋值
-
如果一直找到全局作用域都没有,那么就把这个变量定义为全局变量,再给他赋值
function fn() { num = 100 } fn() // fn 调用以后,要给 num 赋值 // 查看自己的作用域内部没有 num 变量 // 就会向上一级查找 // 上一级就是全局作用域,发现依旧没有 // 那么就会把 num 定义为全局的变量,并为其赋值 // 所以 fn() 以后,全局就有了一个变量叫做 num 并且值是 100 console.log(num) // 100 var n = 100; function fn1(){ var n = 200; console.log('前',n); n = 500 console.log('后',n); } console.log('全局',n); //100 fn1() console.log('全局后',n);//100
-
递归函数
-
在编程世界里面,递归就是一个自己调用自己的手段
-
递归函数: 一个函数内部,调用了自己,循环往复
-
// 下面这个代码就是一个最简单的递归函数 // 在函数内部调用了自己,函数一执行,就调用自己一次,在调用再执行,循环往复,没有止尽 function fn() { fn() } fn()
-
其实递归函数和循环很类似
-
需要有初始化,自增,执行代码,条件判断的,不然就是一个没有尽头的递归函数,我们叫做 死递归
-
递归也是有限制的 不能无限的调用自己 注意:调用的次数
<script>//.求斐波那契数列列中第n个数的值:1,1,2,3,5,8,13,21,34.... 前两个相加 function feibo(n){ var a=1 var b=1 var c=0 if(n<=2){ return 1 }else{ while(n>2){ c=a+b a=b b=c n-- } return c } } document.write(feibo(6)) </script> <script> //递归版 function age(num){ if(num<=2)return 1 return age(num-1)+age(num-2) } document.write(age(6)) </script>
对象
-
对象是一个复杂数据类型 万物皆对象
-
其实说是复杂,但是没有很复杂,只不过是存储了一些基本数据类型的一个集合
var obj = { num: 100, str: 'hello world', boo: true }
-
这里的
{}
和函数中的{}
不一样 -
函数里面的是写代码的,而对象里面是写一些数据的
-
对象就是一个键值对的集合
-
{}
里面的每一个键都是一个成员 -
也就是说,我们可以把一些数据放在一个对象里面,那么他们就互不干扰了
创建一个对象
-
字面量的方式创建一个对象
var wangzhen = { 'username':'wangzhen', 'age':18, goodman:true, singing:function(){ console.log('窗外的麻雀在电线杆上撒粮'); }, love:function(){ console.log('过了这个村我在下个村等你') } } wangzhen.sex = 1; alert(wangzhen.sex) // alert(typeof wangzhen) // object alert(wangzhen.username) // 查看静态属性不用加() wangzhen.love() // 调用动态方法 需要加() // 使用内置的工具(函数)来创建对象
-
内置构造函数的方式创建对象
var yinghao = new Object(); yinghao.name = 'ahao'; yinghao.age = 1; yinghao.eating = function(){ console.log('正在吃') }
-
Object
是js
内置给我们的构造函数,用于创建一个对象使用的
-
建议使用第一种来创建对象
键的名字按照变量的命名规则 规范
不要有重复 后边 会覆盖前面
可以使用纯数字 作为键名 自动把数字排到最前面
也可以使用特殊符号作为键名
键的 可加单引号 可不加单引号 但是如果用特殊符号作为键名 必须加单引号
建议使用字符串作为键名
var jianzeng = {
name:'jiangjiang',
age:18,
ball:function(){
return '千锋白巧克力'
}
}
// 增
// jianzeng.height = '181cm';
// alert(jianzeng.height)
减 delete
// alert(jianzeng.age);
// delete jianzeng.age;
// alert(jianzeng.age);
// 改
//重新赋值就是改的过程
// alert(jianzeng.name);
// jianzeng.name = 'xiaojiang'
// // alert(jianzeng.name);
// //查
// alert(jianzeng.age) // 查看静态属性
// alert(jianzeng.ball())//调用动态方法
当对象的键名字中有 纯数字 特殊符号
这个时候 增删改查 要使用 中括号的方式巧用
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。