JS进阶学习笔记(3)函数

函数

基本概念

1、 一段js代码,只定义一次,但是可以执行或调用无数次。

2、 js函数是参数化的:函数定义时会包含一个形参,是局部变量;

3、 函数调用会为形参提供实参的值;

4、 函数使用实参的值来计算返回值,成为该函数调用表达式的值;

5、 除实参外,每次调用还会拥有另一个值(本次调用的上下文),这就是this关键字的值

6、 方法:如果函数挂载在一个对象上,作为对象的属性,称其为对象的方法。
当通过这个对象来调用函数时,该对象就是此次调用的上下文,也就是该函数的this 值
7、 构造函数:用于初始化一个新创建的对象的函数

8、 函数就是对象。Js可以把函数赋值给变量,或者作为参数传递给其他函数。
因为函数就是对象,所以可以把它们设置属性,甚至调用它们的方法

9、 闭包:函数可以嵌套在其他函数中定义。从而访问他们被定义时所处的作用域中的任何变量。

函数定义

定义的构成

1、名称标识符。名称:函数声明语句必须部分。
对函数定义表达式来说,这个名字是可选的。但若存在,则只存在于函数体中,并指代该函数对象本身。

2、()。包括0或多个用逗号隔开的标识符组成的列表。这些标识符是函数的参数名称,就像函数中的局部变量一样

3、{ }。包括0或多条js语句。调用及执行。

函数的定义方法

1、

// 函数声明语法
            function add(num1,num2){
                return num1+num2;
            }

2、

// 函数表达式
            let sub=function (num1,num2){
                return num1-num2;
            }

区别:以函数声明的方法定义的函数,函数可以在函数声明之前调用(变量提升);
而函数表达式的函数只能在声明之后调用
以函数声明的方法定义的函数,函数名是必须的。
如果函数表达式声明的函数有函数名,那么这个函数名就相当于这个函数的一个局部变量,只能在函数内部调用。(递归时最好加个名字)

let su=function s(num1,num2){
                return num1-num2;
            }
            s(1,2);
            //s is not defined

3、

// 箭头函数(es6新加)
            let mu1=(mun1,mun2) => num1*num2;
            let mu2=(mun1,mun2) => {
                return mun1*mun2;
            }
            // 相当于mu1的定义
let f1 = () => {
                console.log("arrow function");
            }
            // 若无参数,括号必须加
            let f2 = x => x**2;
            // 若有参数,可以无括号
            let xx=((x) => x**x)(4);
            //立即执行函数。
            //  (x) => x**x是参数为x的函数,后面(4)相当于为函数传入参数4,最后计算出来的结果赋值给xx

4、

// function构造函数
            let sum=new Function(
                "num1",
                "num2",
                "let return=num1+num2; return result;"
            );

构造函数接收任意多个字符串参数,最后一个参数始终会被当成函数体,而之前的参数都是新函数的参数;
不推荐使用,影响性能。

函数命名

函数名称:合法标识符。通常是动词或以动词为前缀的词组
第一个字符为小写
当包含多个单词时:用下划线分隔:like_this()
除第一个单词外的单词首字母大写:likeThis()
有一些函数是用做内部函数或私有函数:以_为前缀

函数参数
特征:既不关心传入的参数个数,也不关心这些函数的数据类型 -> 定义时接受两个并不意味着调用时就要传两个。

原因:因为ES函数的参数在内部表现为一个类数组,函数被调用时总会接收一个数组,但函数并不关心这个数组中包含什么,传递函数的每个参数都被包含在arguments对象(类数组)中

// 参数默认值
            function f1(name,age){
                name=name?name:"User";
                // 等价于name=name||"User"
                age=age?age:0;
                console.log(name,age);
            }
            f1();
            f1("Tom",12);
            function f2(name="User",age=0){
                console.log(name,age);
            }
            f2();
            f2("Tom",21);
// 扩展参数
            function sum(){
                let r=0;
                for(let i=0; i<arguments.length;i++){
                    r+=arguments[i];
                }
                return r;
            }
            let nums=[1,2,3,4,5];
            console.log(sum.apply(null,nums));
            // 15
            console.log(sum(...nums));
            // 15
// 剩余参数
            function sum1(name){
                let r=0;
                for(let i=1;i<arguments.length;i++){
                    r+=arguments[i];
                }
                console.log(`${name}总分为:${r}。`);
            }
            sum1("Tom",80,90,100);
            // Tom总分为:270。
            // reduce计算数组元素相加后的总和
            function sum2(name,...scors){
                let r=scors.reduce((x,y) => x+y,0);
                console.log(`${name}总分为:${r}。`);
            }
            sum2("Tom",80,90,100);
            // Tom总分为:270。
            function sum3(name,...scors){
                let r=scors.reduce((x,y) => x+y);
                console.log(`${name}总分为:${r}。`);
            }
            sum3("Tom",80,90,100);
            // Tom总分为:270。x+y后面的0是传入的初始值

arguments对象

一个类数组对象(不是Array的实例),因此可以使用中括号语法访问其中元素.
要确定接收到的参数个数,可以访问arguments.length

function sum1(){
                return Array.from(arguments).reduce((x,y) => x+y,0);
            }
            // let sum2=() => {
            //     return Array.from(arguments).reduce((x,y) => x+y,0);
            // };
            // 报错。箭头函数内部不支持arguments对象(也没有this)
            console.log(sum1(1,2,3),sum2(1,2,3));
            let sum3=(...nums) => {
                return nums.reduce((x,y) => x+y,0);
            };
            console.log(sum3(1,2,3));
function f1(x,y,...nums){
                console.log(arguments);
                console.log(nums);
            }
            f1(1,2,3,4,5,6);
            // [3,4,5,6]
            // arguments对象包含剩余参数值,剩余参数没有接收到实参时为空数组
            f1(1,2);
            // []
            // function f2(x,...nums,y){
            //     console.log(nums);
            //     console.log(y);
            // }
            // f2(1,2,3,4,5);
            //报错。 剩余参数要位于参数列表末尾

函数调用

1、 作为函数
使用调用表达式可以进行蹼泳的函数调用,也可以进行方法调用
Eg:sum(1,2,3+6,getNum())
非严格的es5中,this的值是全局对象,严格模式下是Undefinde

2、 作为方法
一般情况下和普通函数的使用方式一致
方法调用和函数调用的区别:调用上下文
调用上下文:关键字this没有作用域限制,嵌套的函数不会从调用它的函数中继承this

3、 作为构造函数
调用前带new就是构造函数调用。
构造函数调用创建一个新的空对象,这个对象继承自构造函数的prototype属性。
构造函数可以使用this来引用这个新创建的对象。
不使用return,当前构造函数的函数体执行完毕时,会显式返回该对象

4、 函数对象的Call()、apply()方法间接调用
第一个参数指定调用上下文(函数内部的this);第二个参数给函数传递参数
Call()的第一个必备第二个可以没有
call() 方法分别接受参数。
apply() 方法接受数组形式的参数。

5、 回调函数
被作为参数传入另一函数,并在该外部函数内被调用,用以来完成某些任务的函数。

函数对象

函数属性
1、 length:只读属性。代表函数声明的实际参数数量
(arguments.length表示传入函数的实参个数)

2、prototype:
每一个函数都包含一个prototype属性,这个属性是指向一-个对象的引用,
这个对象称做“原型对象”(prototype object) 。
每一个函数都包含不同的原型对象。当将函数用做构造函数的时候,新创建
的对象会从原型对象上继承属性

3、 自定义属性

函数方法

1、call()

2、apply()
通过调用方法的形式来间接调用函数
cal1()和apply()的第一个实参是要调用函数的主体对象,它是调用上下文
在函数体内通过this来获得对它的引用。

3、bind():
将函数绑定至某个对象
当在函数f().上调用bind()方法并传入-一个对象。作为参数,这个方法将返回一个新的函数
(以函数调用的方式)调用新的函数将会把原始的函数f()当做o的方法来调用。
传入新函数的任何实参都将传入原始函数。
第一个参数是指定上下文,第二个参数才开始传参

尾调用

// 尾调用.函数的最后是不是一个函数.可以提升性能,比递归好
            function tail(x){
                console.log('tail',x);
            }
            function fx(x){
                return tail(x);
            }
            fx(123);// tail 123

你可能感兴趣的:(学习笔记,javascript,js)