欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  web前端

JavaScript中函数详解

程序员文章站 2022-04-25 16:04:01
...

(1).什么是函数?

具有特定功能的n条语句的封装体。只有函数是可执行的,其他类型的数据是不可执行的。函数也是对象。

(2).函数的作用

→ 提高代码复用

→ 便于阅读和交流

(3).函数的定义

方式一:函数声明(推荐使用)  function     函数名(参数列表) 
{  //执行代码  }方式二:函数表达式(推荐使用)   var 变量名 = function(参数列表) {(推荐使用)    //执行代码   }
 var 变量名 = function 函数名(参数列表) 
 {  //执行代码    }方式三:构造器(了解)
 function     函数名(参数列表) 
 {  //执行代码  }


function xiYiJi() {//函数声明
console.log("您好")
}
var fn = function(){//表达式
console.log("函数表达式")
};
var fn2 = function show(){//表达式
console.log(1);
}
var fn3 = new Function("console.log(4)");//构造器
fn3();

函数的使用:函数名(参数列表对应的实参);

注意:参数列表或参数可有可无,具体使用看需求。若定义的多个函数重名时,后面的函数会覆盖前面的函数。

(4).如何证明函数是对象

1.fn instanceof Object=== true
2.使用函数有的属性以及方法
属性:prototype/__proto__
方法:call()/apply()/bing()
3.可以添加新的属性/方法

function fn () {}
console.log(fn instanceof Object=== true)//true
//3.可以添加新的属性/方法
fn.a=3;
console.log(fn.a)


(5).函数有4种角色

一般函数(直接调用),构造函数(通过new调用),方法(通过对象调用),对象(通过调用内部属性/方法)

JavaScript中函数详解

(6).如何调用(执行)函数?

①test():直接调用

②new test():通过new来调用

注意:使用new关键字是将函数当作构造函数调用,即为构造对象,若没有人为的重写调用构造函数时返回的值,那么返回的对象是由解析器自己生成的。不使用new关键字调用函数,即为普通函数调用。

③object.test():通过对象来调用

④test.call/apply(obj):通过call来调用

        var obj1 = {x: "obj"};
//声明一个函数a1,控制台输出函数中的上下文(this)
function a1() {
console.log(this);
}
//函数a1调用call方法,传入obj1对象做上下文
a1.call(obj1);//Object {x: "obj"}

var obj2 = {x: "obj"};
function a2() {
console.log(this);
}
a2.apply(obj2);//Object {x: "obj"}

注意:函数中call()和apply()的作用:都是用来改this指向

4种方式的区别:是this的值不同

        var obj = {x: "obj"};
function fn1(){
console.log(this);
this.getColor=function (){
console.log(this);
return this.color;
}
}
fn1();//直接调用 输出第一个this-----》Window
var p = new fn1();//通过new来调用 输出第一个this-----》fn1{}(也就是输出p)
p.getColor();//通过对象来调用 输出第二个this-----》fn1{}(也就是输出p)
fn1.call(obj);//通过call来调用 输出第一个this-----》Object
fn1.apply(obj);//通过apply来调用 输出第一个this-----》Object

总结:

1.直接调用函数(fn1()):输出的是Window,但是有一个前提,就没有bind绑定对象。

bind不会改变原有函数的this,只是产生了一个新的函数,并绑定的this

2.new调用:就是新建的这个对象 -----则返回的是new 的实例对象fn1{}

3.通过对象来调用:就是调用方法的对象(这个对象 )--- 返回的是new 的实例对象fn1{}

4.test.call/apply(obj)来调用: 指定的那个对象 ----返回的是obj对象

注意:调用函数fn其中的test(),没有执行fn函数。只有使用call()/apply()调用,才执行fn函数

(7)方法和函数的区别

var arr = [1,2,3,4,5]
var a =12; // 变量:*的
arr.a= 5; //属性:属于一个对象
function show(){ //函数:*的
alert(‘a’);
}
arr.fn = function(){ //方法:属于一个对象
alert(‘b’);
}
其实方法就是函数,只不过方法是有所属的对象。

我们所熟知的,将函数绑定到 click 事件
语法:$(selector).click(function)

变量和函数声明

1.变量声明提升和函数声明提升

a.变量声明提升:通过var定义(声明)的变量,在定义语句之前就开业访问到,值为underfined

console.log(a);

var a=3;

//解析:在定义a=3,时,就可以访问a的值,只是a的值为underfined 这就是变量声明提升

//console.log(b);b=6;但是b就不可以了,是要通过var来声明

b.函数声明提升:通过function声明的函数,在之前就可以直接调用,值为函数定义(对象object)

fn();

function fn () { console.log('1') }

通过function声明的函数,在之前就可以直接调用,这个说明函数定义已经创建了。。

js引擎如何变量声明提升,函数声明提升?

是通过预处理。

浏览器中的两大引擎:浏览器之所以能够将我们写的html代码、css代码以及js代码转换成一个炫丽的网页界面,是因为两大引擎的作用。
→渲染引擎:解析html和css代码,转换成炫丽的静态界面

→渲染引擎:解析html和css代码,转换成炫丽的静态界面

→js引擎:解析并运行js代码,实现动态的交互效果。

js预解析:
js代码在解释执行之前,有一个“预备的过程”。这个预备的过程被称为“预解析”。
预备的过程做什么事情呢?

把用var关键字所声明的变量名(仅仅只是变量名) 和 用函数声明的方式定义的函数(函数整体)提升到【当前执行环境】的顶部。

console.log(a);
var a=122;
//这个会被解析成:
var a;console.log(a);a=122;
//所以不会报错,打印出undefined


//2.函数声明方式创建的函数
test1("hello");
function test1(x){console.log(x)}
//这个会被解析成:
function test1(x){console.log(x)}
test1("hello");
//所以不会报错,打印出hello

//3.函数表达式的方式 创建的函数
test2("hello js!");
var test2=function test1(j){console.log(j)}
//这个会被解析成:
var test2;
test2("hello js!");
test2=function test1(j){console.log(j)}
//所以会报错。test2没有定义函数。如果是这这样
var test2=function test1(j){console.log(j)}
test2("hello js!");
//则不会报错。打印出hello js!

关于函数声明,它最重要的一个特征就是函数声明提升,意思是执行代码之前先读取函数声明。这意味着可以把函数声明放在调用它的语句之后。如下代码可以正确执行:

sum(1,2); //3  
function sum(x,y){
alert(x+y);
}

2.函数的参数

→形参:在定义函数时,括号中的参数列表就是形参。
如:function 函数名(形参1,形参2,...){
//执行代码
}
→实参:在调用函数时,所传入的对应的实际的数据,就是实参。
如:函数名(数据1,数据2,...);

→函数体内的 arguments
在定义函数时,使用形参时,形参可以不写(不推荐),可以使用arguments 这个“伪数组”来获取所传入的实参。
arguments[索引]; 索引从0开始


如:function 函数名(形参1,形参2,...){
//执行代码
}
→实参:在调用函数时,所传入的对应的实际的数据,就是实参。
如:函数名(数据1,数据2,...);

→函数体内的 arguments
在定义函数时,使用形参时,形参可以不写(不推荐),可以使用arguments 这个“伪数组”来获取所传入的实参。
arguments[索引]; 索引从0开始

3.返回函数的函数---返回值

return作用:终止函数,并返回数据。
使用方式:函数里若没有显示的使用return时,函数执行完后,最终返回undefined;
return; //执行到该句代码时,函数会终止,并返回undefined。
return 数据;//执行到该句代码时,函数会终止,并返回指定的数据。

使用方式:函数里若没有显示的使用return时,函数执行完后,最终返回undefined;
return; //执行到该句代码时,函数会终止,并返回undefined。
return 数据;//执行到该句代码时,函数会终止,并返回指定的数据。

案例:简易的计算器

<script type="text/javascript">
var box = function(){
var a=1;
return function(){
alert(++a)
}
}
var newFunc = box();//因为上面返回return是函数体 所以box是函数
newFunc();//2
</script>
如果想让返回的函数立即执行,亦可以使用box()()来执行这段代码。

4. 作用域作用域,指的是变量使用的范围。
→全局作用域:函数之外的环境(全局执行环境)
全局变量:在全局执行环境中用var关键字所声明的变量就是全局变量,全局变量在程序中的任何地方都可以使用。
→局部作用域:一个函数就是一个独立的执行环境(函数体内就是一个局部执行环境)
局部变量:在函数中用var关键字所声明的变量 或 函数定义中的形参,仅仅只能够在本函数(本作用域中)中使用。
→注意:
情况一: 当全局变量 和 局部变量命名一样时,在函数中使用该变量时,会优先使用函数本身中定义的局部变量。
情况二:关于形参,其实就相当于在函数内用var关键字声明了变量。
如:function test(a,b){
//var a,b;
}

→ 再看变量声明提升:

var scope = 'global';
function f(){
console.log(scope);
var scope = 'local';
console.log(scope);
}
由于函数内声明提升,所以上面的代码实际上是这样的

var scope = 'global';
function f(){
var scope; //变量声明提升到函数顶部
console.log(scope);
scope = 'local'; //变量初始化依然保留在原来的位置
console.log(scope);
}
经过这样变形之后,答案就就非常明显了。由于scope在第一个console.log(scope)
语句之前就已经定义了,但是并没有赋值,因此此时scope的指是undefined.
第二个console.log(scope)语句之前,scope已经完成赋值为’local’,所以输出的结果是local。

→ JavaScript中没有块级作用域
就是在 选择语句 或 循环语句 中定义的变量不像是在函数体内一样定义的变量是局部变量,而是全局变量

5.匿名函数 和 自执行函数

①匿名函数(简称IIFE)

顾名思义就是没有名字的函数。

a.function(){}这样定义的函数在语法上是错误的(因为它没有函数名字)。

b. (function(){....})
!function(){....}

-function(){....}

var a = function(){......}

若是加上一些运算符的话,该函数就不会产生错误,此时的函数被称为“匿名函数”。

注意:(function(){//这里是块级作用域 })

以上代码的这种方式就是模仿了块级作用域(通常成为私有作用域);定义并立即调用了一个匿名函数。经函数声明包含在一对圆括号中,表示它实际上是一个函数表达式。而紧随其后的另一对圆括号会立即调用这个函数。

②自我执行函数(其实就是执行匿名函数):即定义和调用合为一体
(function(){ // (匿名函数)();第一圆括号放匿名函数,第二个圆括号执行
alert(1);
})();
匿名函数定义玩之后可以即刻执行。

→匿名函数的作用:

避免全局变量污染以及命名冲突。

补充:

//1.把匿名函数自我执行的返回值赋给变量:
var box = (function (){
console.log('Lee');
return 3;
})(); //打印”Lee”;
console.log(box); //如果没有return 3的话,打印出来就是 undefined

//2.自我执行匿名函数的传参
(function (age){
alert(age);
})(100); //弹出100
自执行函数的三种写法
var result = function (){
alert(2);
}();
另一种语法也可得到同样结果:
var result = (function () {
console.log(2);
})();
将函数返回值分配给变量:
var result = (function () {
return 2;
}());



三. 回调函数

回调函数具体的定义为:函数A作为参数(函数引用)传递到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。

→ 函数是一种实际的数据。

→ 在定义函数时,是可以在括号中定义形参的。

→ 在括号中的形参就相当于变量。而变量将来要介绍实际的数据,实参。

/*函数是一种数据。所以可以当做实参来使用*/
function fn(f){
f();
}
var a = function(){
alert("我是回调的函数");
}
fn(a);//fn是主函数,参数为a函数本身,所以a是回调函数


fn(function(){
alert("我是回调过来的");
})//在fn的函数里直接调用函数,这个被调用的函数也就是回调函数

b.将回调函数的参数作为与回调函数同等级的参数进行传递

JavaScript中函数详解

总结明了同时满足这三个条件就是回调函数:

*你定义的函数

*你没有直接调养

*但最终它会执行(在特定条件和时刻)

(2).常用的回调函数

*DOM事件函数

*定时函数

*ajax函数

*生命周期回调函数(组件,对象)

//DOM事件函数
document.getElementById('id').onclick=function(){
alert('点击事件。。')
}
//定时函数
setTimeout(function(){
console.log('到点了');
},10000)


(3).匿名函数

如果没有名称(函数表达式),就叫做匿名回调函数

作用:a.隐藏内部实现b.不污染外部命名空间

(function(){
var a = 123;
function foo(){
console.log(a)
}
})();

四.函数中this

1.this是什么?

*一个关键字,一个内置的引用变量

*在函数中都可以直接使用this

*this代表当前函数的调用对象

*在定义函数时,this还没有确定,只有在执行时才动态绑定的

记住:跟函数定义没关系,跟函数的执行有大大的关系。总之就是:函数在哪里调用才决定了this到底引用的是啥

2.如何确定this的值?

①test():直接调用
②new test():通过new来调用
③object.test():通过对象来调用
④test.call/apply(obj):通过call来调用

1.直接调用函数(fn1()),输出的是Window,但是有一个前提,就没有bind绑定对象。

bind不会改变原有函数的this,只是产生了一个新的函数,并绑定的this

2.new调用,则返回的是new 的实例对象fn1{}

3.通过对象来调用,返回的是new 的实例对象fn1{}

4.test.call/apply(obj)来调用,返回的是obj对象

注意:

(1).执行函数时:有步骤
a.执行函数定义(也就是执行这整块function Person(color){...}代码,把这个整块看成一句语句,就是函数定义):本质是创建函数对象
b.执行/调用 函数Person('red');
(2).函数对象:函数首先是一个对象,所以可以通过“ . ”来确定它内部的属性以及方法。
如我用" () " a() ,此时a则是函数,如a.yy();这时a就称函数对象。可以通过“ . ”来判断是否是函数对象
(3)通过对象调用的时候,称方法:p.getColor();其他的时候称函数(p.setColor.call(obj,'black');)p.setColor获取是一个属性值,而这个属性值就是函数

function Person(color){
console.log(this);
this.color=color;
this.getColor=function (){
console.log(this);
return this.color;
}
this.setColor=function (color){
console.log(this);
return this.color;
}
}
Person('red');//输出是第一个 console.log(this);-----Window
// this.getColor方法内的console.log(this)是不执行的,但是getColor函数定义出来了

var p = new Person("yello");//输出是第一个 console.log(this);-----Person {}(就是p)

p.getColor();
//输出是第二个 console.log(this);-----Person {color: "yello"}(就是p)

var obj={};
p.setColor.call(obj,'black');
//输出是第三个 console.log(this); ---obj 通过函数对象的call方法来执行函数

var text=p.setColor;//把p.setColor的属性值(函数)赋值给text
text();//直接调用 输出的都是 Window

function fun1(){
function fun2(){
console.log(this);
}
fun2();//直接调用 输出的都是 Window
}
fun1();


//注意:fn.bind()
function fn(){
console.log(this)
}
const obj2={}
const fn2=fn.bind(obj2);
//bind不会改变原有函数的this,只是产生了一个新的函数,并绑定的this
fn();//直接调用 输出的都是 Window
fn2();//输出的是 obj2

五.递归

1.递归的官方概念:

程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

2.递归的案例:
如一组有规律的年龄
10 、12、14、16、18、20、22、24......
求第n个人的年龄?

图解分析:
两个阶段:回推阶段(前进)、递推阶段(返回)
一个条件:边界条件;第一个人的年龄为10

JavaScript中函数详解

function getAge(n){
if(n==1){ //边界条件
return 10;
}else {
return getAge(n-1) + 2;
}
}

var age = getAge(5);
alert(age);

相关推荐:

JavaScript函数绑定用法解析

实例讲解JavaScript函数绑定用法

详细介绍JavaScript函数的作用域与this指向

以上就是JavaScript中函数详解的详细内容,更多请关注其它相关文章!