JavaScript函数基础

来源:互联网 发布:家庭摄像头破解软件 编辑:程序博客网 时间:2024/06/07 01:25

JavaScript函数基础

  • JavaScript函数基础
    • 函数基本规范
      • 函数的定义
      • 函数的调用
      • 函数的实参和形参
      • 作为值的函数
      • 作为命名空间的函数
    • 函数闭包
      • 变量作用域
      • 作用域链
      • 闭包
    • 函数属性方法和构造函数
      • 函数属性
      • 函数方法
        • call和apply
        • bind

先思考一下,函数是什么,目前我的认知:函数是一个方法,一个工具,用来封装我们需要进行的操作,以便于复用。函数接受一定的参数,这些参数用来是函数拿到我们想让它操作的东西,同时我们也需要取回经过操作后的成果,所以函数就需要有返回,这些东西定义了一个函数的构成,不论是哪种语言,函数都离不开这些。下面我们就来看看Javascript中的函数规范。
【注】:本篇文章的结构和知识内容来自JavaScript权威指南。

函数基本规范

函数的定义

要使用一个工具,得先设计一下这个工具,然后把它造出来,然后我们就可以用这个工具进行任何想要的操作,用多少次也没关系。

JS中,要让编译器知道这是一个函数而不是一个变量,就需要一个标注,也就是我们的声明语句,关键词为function,工具要有它的名字,要接受用户给的信息,当然内部的操作也要给工具,不然它知道应该干什么?

综上,一个函数的定义:

function f(para){    console.log("this is my operation!");    return "haha"+para;}

但我们需要使用这个函数时,拿着它的名字,给它信息,就可以尽情地使唤它了。
这边要注意一些地方:

- 为了方便别人看你写的东西,我们一般就需要把这个函数写的像一个函数,通常命名规范就是第一个字母小写,后面每个词首字母大写,即驼峰式

- 来看看函数声明的其它骚操作:

var square = function(x){return x*x};var square = function fact(x){if(x <= 1) return 1;else return fact(x-1)}}

这个是以函数表达式的方式来声明函数,这种函数只会被用到一次,所以给不给名字都不重要了,所以可以不写函数名,除了上面的递归调用等操作。但是我不知道第一种声明的意义何在。

函数的调用

工具已经做好了,接下来就是使用它了,也就是我们所说的函数调用。要调用函数,就要用对调用表达式 ,就像使用魔法得念对咒语,我们的基本的函数调用表达式就是:

f(0)Math.max(x,y,z)

那么第二行是什么,第二行中的max不是我们自己造的函数,是别人的函数,拿别人的函数用就要先声明这是谁的,才能用。这就是我们的方法调用方式。比如这个:

var calculate = {    operand1: 1,    operand2: 1,    add: function(){        this.result = this.operand1 + this.operand2;    }};calculate.add();calculate.result;//=>2

这里面有一个陌生的东西,calculate,它就是个对象,你可以把它看作是一个人,也可以看作是个盒子,总之他有一些私人的东西,变量也好,函数也好,想要用,就得先声明这是他的。还有平白冒出的this,这个this放在对象里面,他其实就是指代这个对象,直接写名字会搞混的,编译器会搞不清楚它是新的变量还是本对象,所以约定俗称this指代当前对象。于此同时我们有看见了另外一种函数的声明方式,为什么会有这种,我们在对象里再细说。

还有一种函数调用方式成为构造函数调用,构造函数通常是一个对象里用于初始化数据的一个方法,我们构造了一个对象就需要给它里面的那些数据一个最初的值,通常构造函数是默认执行的,所以当我们实例化一个对象时就会调用它的构造函数,构造函数和对象同名:

var o = new Object();var o = new Object;}

至于简介调用call()和apply(),之后再细讲。

函数的实参和形参

关于实参形参的问题再c++里接触了,简单来说,形参就是形式上的参数,就相当函数声明时,告诉你这里有一个空,需要你去用真正的数据也就是 实参来填,这个空就是形参。

- 当传入的实参比形参个数少时,剩下的形参的值都变为undefined;

- 当传入的实参比形参个数多的时候,这里要提到一个标志符,arguments,它是对实参对象的引用,通过arguments对象的length就可以知道到底传了几个实参,从而对实参溢出或缺失的情况进行判断和相应操作。
- 对于参数太多顺序易记混的情况,直接传一个对象实例,参数都是对象的属性,按名字取值。

作为值的函数

还记得函数表达式的声明方式吗?

var s = function square(x){return x*x};square(4);//=>16s(4);//=>16

就是这么用的。

作为命名空间的函数

函数体中的变量只在函数内部可见,函数外不可见,在程序内的很多中间变量就没必要声明称全局变量,我们可以写一个函数来进行一个操作,处理中间变量,以免它污染全局命名空间。

function mymodule(){    //模块代码    //这里面的变量都是局部变量}mymodule();

精简成匿名函数:

(function(){    //模块代码}())//结束函数并立即调用它

这样就可以避免一些多余的中间变量了。

函数闭包

变量作用域

都知道变量有局部变量和全局变量,全局变量在程序任何地方都可以定义和使用,但是局部变量只可以定义在函数体内,并且作用域是在函数体内。
那要是函数内的变量名和全局变量的变量名冲突怎么办,优先级当然是函数体内的高一些。
与一点值得注意的是函数体中的声明语句都是会被提到函数的顶部的:

var scope = "global";function checkscope(){    console.log(scope);//=>undefined    var scope = "local";    console.log(scope);//local}

上面的代码相当于:

var scope = "global";function checkscope(){    var scope;    console.log(scope);//=>undefined    scope = "local";    console.log(scope);//local}

作用域链

每一段代码(全局代码或者函数)都有一个与之关联的作用域链,这个作用域链是一个对象列表或者链表,大致是这个样子:

这里写图片描述

当JavaScript需要查找变量x的时候,会从链中的第一个对象开始查找,如果第一对象中不存在这个变量,就从第二个对象中查找,在不包含嵌套的函数体内,作用域链上有两个对象,第一个是定义函数参数和局部变量的的对象,第二个是全局对象。

闭包

函数对象通过作用域链关联起来,函数体内部的变量都可以保存在函数作用与内,这种特性在计算机科学文献中称为闭包

var scope = "global scope";function checkscope(){    var scope = "local scope";    function f(){return scope;}    return f();}checkscope();//=>local scope

改动一下:

var scope = "global scope";function checkscope(){    var scope = "local scope";    function f(){return scope;}    return f;}checkscope()();//=>?

分析一下,这个时候checkscope返回的是一个函数对象,那就相当于是在定义域外面调用了这个函数,那么它是返回 global scope 还是 local scope,这个时候我们就要知道,作用域链是在函数定义的时候创建的,也就是说子函数的局部变量就不会销毁,它还是会返回 local scope。
闭包貌似是一个很大的问题,但是目前看不到闭包的作用,若是在以后的项目中遇到相关问题,再来补充。

函数属性、方法和构造函数

函数属性

函数属性包括前文中的实参属性length,以及每个函数都包含一个prototype属性,这个在其他章节中有细讲,本章节不再赘述。

函数方法

call()和apply()

直接看这两个函数式怎么用的吧,call()和apply()的功能是一样的。
call()接受一个以上的实参,第一个参数是要调用函数的母对象,比如说

f.call(o);

的意思是:

o.m = f;//先将f存储为o的临时方法o.m();//调用fdelete o.m;//调用完后还会把它删除掉

call() 和apply()的区别就是接受参数的形式:

f.call(o,1,2);f.apply(o,[1,2]);

bind()

bind方法是ES5中提出的,这个在ES6中也有广泛应用,先挖个坑吧,日后再细说。

原创粉丝点击