————————————————————
JavaScript语法结构
「预解析」「函数【重】」
————————————————————
预解析
概念
HTML、CSS、JS代码全部交由浏览器两个引擎,渲染引擎和JS引擎两个负责执行的。
JS引擎负责对JavaScript代码进行执行处理,通过两个步骤完成JS代码的执行任务,分别是预解析和代码执行
什么是预解析
在当前变量或者函数等作用域下,JS代码在执行之前,浏览器默认会将JS代码中有关var或者function声明的变量或者函数在内存中进行提前声明以及提前定义
// fun();
// function fun(){
// console.log(1);
// }
// 预解析的结果
// 函数的使用过程:1. 定义函数,2.调用函数。
// 函数的调用必须写在函数的定义之后
var fun;
fun();
fun=function(){
console.log(1);
};
- 变量提前:就是将所有的变量声明提前到当前的作用域最前面,不会将赋值操作提前到最前面
- 函数提前:将所有的函数声明提前到当前作用域最前面,不会将调用操作进行提前,函数在调用之前必须保证了函数的完善
函数【重】
函数的概念
在JS里面,在开发过程中经常会遇到一种情况,某种算法或某些代码要经常出现或使用多次,对于这种情况使用for循环不是一种比较好的办法,for循环只能解决一些简单的重复使用问题,而遇到复杂的情况则需要使用函数的概念
函数:将一段可能会被大量重复调用执行的代码段进行封装成一个整体,通过这个整体的名称(函数名)的调用就可以实现大量代码的重复调用
函数的定义方式
-
方式1 函数声明方式 function 关键字 (命名函数)
//函数的声明 function 函数名(参数...){ 函数体 } //函数的调用 函数名();
-
首先声明函数必须使用关键字function,必须是小写
-
起一个函数名,函数名称要严格遵循命名规则使用驼峰命名法
-
函数名后面跟一对小括号,小括号后跟一对花括号
-
到小括号为止称为函数签名
-
花括号内部称为函数体
注意:自定义函数的调用在函数声明之前调用可以,声明之后调用也可以
-
-
方式2 函数表达式(匿名函数)
// 匿名函数的声明方式 var 变量名称=function(参数...){ 函数体 }; // 匿名函数的调用 变量名称(参数...);
注意:
- 声明匿名函数在函数最后的花括号之后要用分号结束
- 剩余的函数声明以及函数调用和自定义函数一样
- 匿名函数的调用只能放在函数声明之后才可以调用
-
方式3 new Function()
var fun=new Function("a","b","alert(a+b)");
第三种方式里面的参数必须全部是字符串格式,并且方法的执行效果低,并且传递参数基本上都是通过字符串处理,不考虑使用
此处说明函数其实本质也是一种对象
函数的调用
-
普通函数的调用
通过 函数名称(实参)这种方式调用普通函数
- 调用函数的时候千万要记住使用"函数名称()"进行调用
- 函数不调用自己不会执行
-
对象内的函数调用
通过 对象名称.内部函数名称(实参) 这种方式调用对象内函数
-
构造函数的调用
通过 new 函数名称(实参)这种方式调用构造函数
-
绑定给事件进行调用,事件函数
btn.click=function(){},这种函数主要是通过事件进行调用
-
定时器处理函数
setInterval和setTimeout,这两个定时器来调用函数
-
立即执行函数
function(){}();直接调用函数
函数的封装
- 函数的封装就是将一个或者多个功能代码块打成一个包,封装起来,对外只会提供一个函数的接口、
- 简单理解封装就是类似于商场购物,最终将所有的物品装进一个购物袋里面,可以将所有的商品要么一起带走要么一起放下
函数的参数
参数
参考数据,给函数的功能添加一些可变因素,主要通过参数实现
形参:形式主义参数,没有任何实际的运算意义,只是告诉函数的调用者函数有这个参数
实参:现实主义参数,主要用于函数执行时参与运算的数据
作用
在函数内部某些数据不能固定使用,有可能会随着各种问题发生改变,那么这个时候使用函数的参数就可以实现解决
参数的使用
-
函数要使用参数先定义形参,形参是在函数定义的时候就写在了函数名之后的小括号中
-
形参的定义在函数签名的小括号内声明函数参数
function 函数名(形参1,形参2,形参3,......){ }
形参就类似于定义一个变量名称,不需要var关键字
形参的数量可以为0也可以任意多,完全根据函数的需求进行定义声明
多个形参之间使用逗号分隔开
-
实参的使用
函数名(实参1,实参2,实参3,....0);
实参是在函数调用的时候传入进函数名之后小括号中的参数
实参就类似于对函数的每一个形参赋值
同样多个实参之间也使用逗号分隔开
形参和实参的数量匹配
现实使用过程中常见问题:
- 实参个数和形参个数相等:输出正确结果
- 实参个数大于形参个数:从实参的第一个开始只拿到形参对应的个数为止,多余的实参无意义
- 实参个数小于形参个数:从实参的第一个开始匹配形参的第一个,依次往后,如果形参找不到对应的实参,则使用undefined进行填充,结果不可控
注意:
- 函数在使用参数的过程中,切记传递的实参的类型不可控。
- 函数的参数可以是任何数据类型
- 如果不明确一个函数的参数到底是要什么,要注意实参可能会引起函数执行异常
函数的返回值
返回值:函数调用整体返回的一个数据,函数执行完成之后可以通过return关键字将需求数据结果返回。
一般的函数默认是没有返回值,因此执行无返回值的函数,接收结果是接收不到任何结果所以实验的结果是undefined
如果在函数内部使用了return进行返回一个数据结果,那么调用这个函数则最终会拿到return返回的结果。
返回值的使用
function 函数名(参数....){
//函数体
return 数据结果;
}
- 有返回值的函数,务必要保证在函数执行结束之前必须要有一个返回结果
- 函数返回值必须写在return语句之后,如果return之后没有任何数据那么这时return的作用就起到一个逻辑控制效果
- 如果return之后有数据那么这个return起到两个作用,一个是结束当前函数,一个数返回这个数据
- 所以在返回值代码之后一定要谨慎,在返回值reutrn之后写代码一定要看清楚是否能执行到
返回值的接收
var 变量名=函数名(参数...);
有返回值的函数调用结果可以使用一个变量来接收函数的返回值
变量的作用域
作用域:变量的一个工作范围,只有在这个范围域内,变量才可以正常使用,超出了这个范围域则变量无法被找到,程序有可能抛异常。
- 变量的作用域取决于其声明的位置。
- 一般情况下变量的作用域从其声明的地方开始往后,到其所在的大括号结束括号为止
根据变量作用域不同将变量划分为:
- 全局变量:在js中作用于整个js文件的变量,但是因为使用内嵌js时使用了window的onload函数,因此可以理解为在onload函数中直接声明的变量叫全局变量,全局变量可以作用于整个js的onload内(从变量的声明往后算)
- 局部变量:在函数或者语法内部声明的变量称为局部变量
作用域冲突
变量作用域冲突并非是在同一个作用域内声明了两个相同的变量,这里的冲突指的是不同作用域内有了多个相同名称的变量
对于作用域冲突的变量,起到实际运算的数据根据就近原则处理
arguments的使用
- 针对于函数的参数数量不确定,此时的参数可以使用arguments来获取
- 在js中arguments实际上是当前函数的一个内置对象。所有的函数都默认有这样的一个对象。
- arguments这个对象主要作用是将函数传递时所有的实参进行封装成一个伪数据
- 因此arguments对象具有length属性,可以根据索引获取元素,但是不具备push等数组的函数
- 即使函数没有定义形参,只要在调用函数时提供了实参那么所有的实参都会装进arguments里面,和形参没有任何关系
this
函数内部的this指向
这些 this 的指向,是当我们调用函数的时候确定的。调用方式的不同决定了this 的指向不同,一般指向我们的调用者.
改变函数内部 this 指向【ES6】
call方法
call()方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this 指向
应用场景: 经常做继承.
apply方法
apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。
应用场景: 经常跟数组有关系
bind方法
bind() 方法不会调用函数,但是能改变函数内部this 指向,返回的是原函数改变this之后产生的新函数,如果只是想改变 this 指向,并且不想调用这个函数的时候,可以使用bind
应用场景:不调用函数,但是还想改变this指向
call、apply、bind三者的异同
-
共同点 : 都可以改变this指向
-
不同点:
- call 和 apply 会调用函数, 并且改变函数内部this指向.
- call 和 apply传递的参数不一样,call传递参数使用逗号隔开,apply使用数组传递
- bind 不会调用函数, 可以改变函数内部this指向.
-
应用场景
- call 经常做继承.
- apply经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
- bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向.
递归算法
递归的特点总结:
1、思想:自调用,就是在函数里面调用自己
2、最关键的一点!!就是一个递归必须明确结束条件,不然就会陷入无穷的死循环
3、缺点,就是消耗大量内存||优点,逻辑清晰
例:斐波那契数列 1 1 2 3 5 8…
function fei(n){
if(n<=0) return 0;
if(n<=2) return 1;
return fei(n-1)+fei(n-2)
}
s指向. 比如改变定时器内部的this指向.
递归算法
递归的特点总结:
1、思想:自调用,就是在函数里面调用自己
2、最关键的一点!!就是一个递归必须明确结束条件,不然就会陷入无穷的死循环
3、缺点,就是消耗大量内存||优点,逻辑清晰
例:斐波那契数列 1 1 2 3 5 8…
function fei(n){
if(n<=0) return 0;
if(n<=2) return 1;
return fei(n-1)+fei(n-2)
}