第八章:函数

函数创建

        函数的最大作用是提供代码复用,将需要重复使用的代码块定义成函数,提供更好的代码复用。函数可以有返回值,可以没有返回值。

        函数的创建有三种方式,分别为 使用Function的构造函数 函数声明函数表达式
        ①  Function构造函数
        这种方式是 直接new出来一个Function 实例 ,通过使用Function的构造函数进行创建函数。Function构造函数可以接收任意多个参数,但是 最后一个参数会被认为是函数体 ,前面的所有参数当做被创建出来的函数的参数。
let test = new Function("a","b","return a + b");
//参数a和b,函数体 return a + b
console.log(test(1,2));//3

        ② 函数表达式

        这种方式是创建的常见方式之一:
let test = function(a,b){
    return a + b;
}
console.log(test(1,2));

        ③ 函数声明

        这种方式和C语言中的很类似,这种是最常见的一种创建函数的方法。是通过关键字function直接声明:
function test(a,b){
    return a + b;
}
console.log(test(1,2));    //3
console.log(test.name);    //test

        函数表达式和函数声明的区别在于使用函数声明这种方式会使函数的声明提前,类似变量申明的提前。也就是说,使用函数申明方式,可以将函数的声明放在调用函数代码的后面,因为解析器会在代码执行之前将函数的声明提升到顶部,而函数表达式方式则会报错:

console.log(test(1,2));//3
//创建函数(函数申明方式)
function test(a,b){
    return a + b;
}

console.log(ntest(1,2));
//创建函数(函数表达式方式)
let ntest = function (a,b){return a + b;}
//报错:ReferenceError: ntest is not defined

函数调用

        Javascript一共有四种调用模式:
        ①  方法调用模式
        ②  函数调用模式
        ③  构造器调用模式
        ④ call/ apply调用模式
        调用模式不同,对应的隐藏参数this值也会不同。
        ①  方法调用模式
        函数作为对象的属性时,称为方法 。此时函数(即方法)中的this对应是该对象。
let myObject = {
    value : 3,
    func : function(){
        console.log(this.value);
    }
};
// 方法调用模式,this对应的是myObject对象
myObject.func(); //3

        ② 函数调用模式

        函数调用模式即通常的 函数调用 ,属于 全局性调用 ,此时this对应的是全局对象,即Window对象。
function add(a, b) { 
    return a + b; 
}
this.add(3,4); //函数调用模式 7
//或者:
window.add = function(a, b) { return a + b; }
this.add(3,4);    //7

        ③ 构造器调用模式

        如果函数或方法调用之前带有关键字new,它就构成了构造函数调用,在不需要传参时可以忽略小括号,构造函数调用创建一个新的空对象,这个对象继承自构造函数的prototype属性。构造函数试图初始化这个新创建的对象,并将这个对象用作其调用上下文,因此构造函数可以使用this关键字来引用这个新创建的对象。
function add(a,b) { 
    this.sum = a + b;
}
// 构造器调用模式
let obj = new add(3,4);
console.log(obj);            //add {sum: 7}
console.log(obj.sum);        //7

        ④ call/apply调用模式

        在第七章对象中的this关键字小节中有讲到,这里不做赘述。

函数的形参和实参

        参数有 形参(parameter) 实参(argument) 的区别, 形参相当于函数 中定义的变量,实参是在运行函数时调用传入的参数 。即形参就是函数声明时的变量,实参是调用该函数时传入的具体参数。并且 参数的传递是可以不一致的 ,即函数定义了3个形参但实际只传入了1个实参,这是被允许的。
传值方式
        在JavaScript中,有两种传值方式,按值传递和引用传递,函数参数的传递 也可分为按值传递和引用传递
let num_1 = 10;
let arr_1 = ["Tom", "Peter", "Smith"];
let setInfo = function(num, arr){
    num = 20;
    arr[1] = "Jackson";
console.log(num);
console.log(arr);
}

console.log(num_1);    //10
console.log(arr_1);    //["Tom", "Peter", "Smith"]
setInfo(num_1,arr_1);  //20    ["Tom", "Jackson", "Smith"]
console.log(num_1);    //10
console.log(arr_1);    //["Tom", "Jackson", "Smith"]
分析:
        num_1将数值本身传递给了形参num ,在函数内部修改num,不影响num_1的值。(值传递);
        arr_1将对数组的引用(即地址)传递给了形参arr,arr与arr_1指向的是同一个代码块,在函数内部修改arr时,实际上修改的是arr所指向的代码块,必然会影响arr_1。(引用传递
形参与实参的数目
        调用函数传递的实参与定义函数规定的形参是依次对应的 ,即第1个实参的值传递给第1个形参,第2个实参的值传递给第2个形参...超出形参数目的实参不传递其值。如果没有对应的实参(实参数目少于形参数目)传入,其值为 undefined 。调用函数时传递的实参数目可以大于形参数目,但是最好不要小于形参数目。
function m(x,y,z){
    return x + y + z;
}
m(1,2)     //NaN
m(1,2,3,4) //6

形参和实参的区别

        形参出现在函数定义中,在整个函数体内都可以使用, 离开该函数则不能使用。
        实参出现在主调函数中,进入被调函数后,实参变量也不能使用。
        形参和实参的功能是作数据传送 。发生函数调用时, 主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。

作为值的函数

        函数可以定义,也可以调用,这是函数最重要的特性。函数定义和调用是JS的词法特性,对于其他大多数编程语言来说也是如此。然而 在JS中,函数不仅仅是一种语法,也是值 ,也就是说, 可以将函数赋值给变量,存储在对象的属性或数组的元素中,作为参数传入另外一个函数等
//声明一个函数
function square(x){ return x*x; }
//函数对象作为值赋值给其他变量
let fun = square;
fun(4);                //16
let fun1 = square(4);
console.log(fun1);     //16

//将函数直接作为对象直接量的属性或者数组的元素
let o = {x : 4, square : function(x){ return x*x; }}
o.square(o.x);     //16

let a = [function(x){ return x*x; }, 'hello', 4]
a[0](a[2]) //16

//函数作为参数传入其他的函数
function add(x, y){ return x+y; }
function multiply(x, y){ return x*y; }
function operate(calculate, x, y){
    return calculate(x,y)
}
operate(add,21,15) //36
operate(multiply,7,9) //63

        函数不是原始值,是一种特殊的对象,所以它也可以有属性。当函数需要一个 “静态”变量来调用时保持某个值不变,最方便的方式就是给函数定义属性,而不是定义全局变量,显然定义全局变量会让命名空间变得更加杂乱无章。

闭包

function A(){
    function B(){
        console.log('Hello Closure!');
    }
    return B;
}
let C = A();
C();    // Hello Closure!

        这是一个最简单的闭包示例,在上述代码中函数A的内部函数B被函数A外的一个变量 C 引用,所以当一个内部函数被其外部函数之外的变量引用时,就形成了一个闭包

        在 Javascript 中,如果一个对象不再被引用,那么这个对象就会被 GC 回收,否则这个对象一直会保存在内存中。在上面例子中,B 定义在 A 中,因此B 依赖于 A ,而外部变量 C 又引用了 A , 所以B间接的被 C 引用。也就是说,B 不会被 GC 回收,会一直保存在内存中。
function A() {
    let count = 0;
    function B() {
        count ++;
        console.log(count);
    }
    return B;
}
let C = A();
C();// 1
C();// 2
C();// 3

       上面代码中, count 是函数A 中的一个变量,它的值在函数B 中被改变,函数 B 每执行一次,count 的值就在原来的基础上累加 1 。因此,函数A中的 count 变量会一直保存在内存中。当我们需要在模块中定义一些变量,并希望这些变量一直保存在内存中但又不会 “污染” 全局的变量时,就可以用闭包来定义这个模块

        再看看下面的代码,使你更加明白闭包的作用:

function A() {
    let count = 0;
    function B() {
        count ++;
        console.log(count);
    }
    return B();    //此处加上小括号
}
let C = A();       
C();    // Uncaught TypeError: C is not a function

let D = A;
D();    //1
D();    //1
D();    //1

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

length属性
        在函数体内, arguments.length表示传入函数的实参的个数 。而函数本身的length属性则有不同的含义。 函数的length属性是只读属性,它代表函数参数的数量,这里的参数指的的“形参” ,也就是函数定义时给出的实参个数,通常也是函数调用时期望传入函数的实参个数。
function sum(x, y, z) {
    return x + y + z;
}
sum.length //3

prototype属性

        每一个函数都包含一个prototype属性,这个属性指向一个对象的引用,这个对象称为“原型对象” 。每一个函数都包含不同的原型对象。当将函数用作构造函数的时候,新创建的对象会从原型对象上继承属性。(关于原型的知识点可以去阅读我笔记的第六章)
call()方法和apply()方法
        在 javascript 中,call 和 apply 都是为了改变某个函数运行时的上下文(context)而存在的,换句话说,就是为了改变函数体内部 this 的指向。(关于call 和 apply的知识点可以去阅读我笔记的第六章)
bind()方法
        bind()方法主要作用是将函数绑定至某个对象
//待绑定的函数
function f(y){ 
    return this.x + y;
}
let o = {x : 2}; //这个是将要绑定的对象
let p = f.bind(o); //将o中的x绑定到f()中的this.x,这里必须同名;
p(3); //输出的是 5

let q = {y : 2};
let r = f.bind(q);
r(3);    //NaN

        ES5中的bind()方法不仅仅是将函数绑定到一个对象,还附带一些其他应用:除了第一个参数之外,传入bind()的实参也会绑定到this:

let sum = function(x,y){
    return x + y;
};
let suc = sum.bind(null, 1); //this的值绑定到null,第一个参数x的值绑定到1
suc(2) //传入第二个参数得到计算结果 3

function f(y , z){
    return this.x + y + z;
}
//this绑定到对象{x : 1},第一个参数绑定到2
let g = f.bind({x : 1},2); 
g(3) //传入第二个参数得到计算结果 6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值