Web前端高级编程学习笔记9:函数进阶

目录

    • 基本概念
    • 函数定义
      • 函数定义方式
        • 函数声明语法
        • 函数表达式
        • 箭头函数(ES6)
        • Function 构造函数
      • 函数命名
    • 函数参数
      • arguments对象
      • 参数默认值
      • 扩展参数
      • 剩余参数
      • 常见问题
    • 函数调用
      • 作为函数调用
      • 作为方法调用
      • 构造函数调用
      • 间接调用
      • 回调函数
    • 函数对象
      • 函数属性
        • length属性
        • prototype属性
        • 自定义属性
      • 函数方法

基本概念

函数,一段JS代码,它只定义一次,但可以被执行或调用任意次。

JS函数是参数化的。

  • 函数的定义会包括一个名为形参的标识符列表,这些参数在函数体内像局部变量一样工作。
  • 函数调用会为形参提供实参的值。
  • 函数使用实参的值来计算返回值,成为该函数调用表达式的值。
  • 除实参外,每次调用还会拥有另一个值(本次调用的上下文),这就叫this关键字的值。
  • 若函数挂载在一个对象上,作为对象的属性,则成为该对象的方法
  • 用于初始化一个新创建的对象的函数称为构造函数
  • 函数即对象,程序可以随意操控。
  • 函数可以嵌套在其他函数中定义,从而可以访问它们被定义时所处的作用域中的任何变量。称为闭包

函数定义

函数定义构成:函数名称标识符,一对圆括号,一对花括号。

function 函数名 ( ) {
       }

函数定义方式

函数声明语法

function add(num1,num2){
     
    return num1+num2;
}

函数表达式

let sub=function(num1,num2){
     
    return num1-num2;
}

区别:函数声明语法会提升,且可以在函数声明之前调用;而函数表达式只能在声明之后被调用,函数名是可写可不写,且在函数之外无法用函数名调用。

箭头函数(ES6)

let mul =(num1,num2) => num1*num2;
//函数内没有多余语句,即函数内只有return语句,才能省略写法。
let mul =(num1,num2) => {
     
    return num1*num2;
}
//没有参数,不能省略圆括号
let f1 = () => {
     
    console.log("abc");
}
//若只有一个参数,则可以省略圆括号
let f2 = x => x**2;
//立即执行函数,xx值为函数值传进去执行的结果
let xx=((x)=>x**x)(4)  //256

Function 构造函数

let sum = new Function(
    "num1",
    "num2",
    "let result=num1+num2;return result;"
);
//接受任意多个字符串参数,最后一个参数始终会被当成函数体,而之前的参数都是新函数的参数。

不推荐使用,会被解释两次,影响性能。

函数命名

函数名称建议使用以动词或以动词为前缀的词组。

通常函数名第一个字符为小写

若函数名包含多个单词时:

  • 将单词以下划线分隔
  • 驼峰命名法

有一些函数作为内部函数或私有函数,这种函数名通常以一条下划线为前缀。

eg. _ _ proto_ _

函数参数

特征:ECMAScript函数不关心传入参数个数,也不关心传入参数的数据类型。

因为ECMAScript函数的参数在内部表现为一个数组。

arguments对象

一个类数组对象,可以使用中括号访问其中元素。

arguments[0]、arguments[1]

要确定接收到的参数个数,可以访问arguments.length属性。

参数默认值

ES5

function f1(name, age) {
     
name = name ? name : "User";    //可以简化为  name || "User"
age = age ? age : 0;            //            age || 0
console.log ( name, age);
}
f1();
f1("Tom");

ES6

function f2(name = "User",age = 0){
     
console.log (name,age) ;
}
f2( );
f2( "Tom" ) ;

扩展参数

function sum(){
      };
let nums=[ ];
//将nums传入sum()
sum.apply(null,nums);   //ES5
sum(...nums);           //ES6

剩余参数

剩余参数语法允许我们将一个不定数量的参数表示为一个数组。

function sum2(name,...scores){
      
     let r=scores.reduce((x,y)=>x+y,0); 
     console.log(`${
       name}总分为:${
       r}。`);
}
sum2("Tom",80,90,100);

常见问题

  • 箭头函数不支持arguments对象。
  • 箭头函数支持剩余参数。
  • 剩余参数只能位于参数列表的末尾,有且只能有一个。

函数调用

构成函数主体的JavaScript 代码在定义之时并不会执行,只有调用该函数时,它们才会执行。
调用函数的方法:作为函数、作为方法、作为构造函数、通过它们的call( )和apply( )方法间接调用。

作为函数调用

sum( 1,2,3+6,getNum( ) );

对于普通的函数调用,瓯数的返回值成为调用表达式的值。
函数调用上下文(this的值)是全局变量。

作为方法调用

方法调用和函数调用有一个重要的区别,即 调用上下文

let calculator={
     
    oper1:10,
    oper2:20,
    add:function(){
     
        this.result=this.oper1+this.oper2;
    },
};
calculator.add();
console.log(calculator.result);    //30

调用上下文
关键字this没有作用域的限制,嵌套的函数不会从调用它的函数中继承this。
先设置一个变量保存当前this的值,以免下文this再次被使用而改变。

构造函数调用

如果函数或方法调用之前带有关键字new ,它就是构造函数调用。
构造函数通常不使用return关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回该对象。

间接调用

使用函数对象的call( )和apply( )方法可以间接调用函数。

第一个参数指定调用上下文(函数内部的this ) ,第二个参数给函数传递参数。

let obj1={
     
    x:100,
    y:200,
    show:function(n=1,m=1){
     
        return `(${
       this.x*n},${
       this.y*m})`;
    },
    concat:function(){
     
        let r=[this.x,this.y];
        for(let a of arguments){
     
            r.push(a);
        }
        return r;
    },
};
let obj2={
     x:111,y:222};
let r1=obj1.show.call(obj2);
let r2=obj1.show.call(obj2,10,100);
console.log(r1,r2);
//(111,222) (1110,22200)

let r3=obj1.concat.call(obj2,11,22,33);
let r4=obj1.concat.apply(obj2,[2,3,4,5]);
console.log(r3,r4);
//[ 111, 222, 11, 22, 33 ] [ 111, 222, 2, 3, 4, 5 ]

回调函数

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

函数对象

函数属性

length属性

在函数体里,arguments.length 表示传入函数的实参的个数。
而函数本身的length属性是只读的,它代表函数声明的实际参数的数量。

function fn(a,b){
     
    console.log(arguments.length);
    console.log(fn.length);
    console.log(arguments.callee.length);
};
fn(1,2,3,4,5);
//5
//2
//2

prototype属性

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

自定义属性

函数是一种特殊的对象,可以拥有属性。

function fx(a,b){
     
    if(fx.count) fx.count++;
    else fx.count=1;
    return a+b;
}
fx(1,2);fx(2,3);fx(3,4);fx(4,5);
console.log(fx.count);
//4

函数方法

call( )和apply( )方法

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

bind( )方法

  • 将函数绑定至某个对象。
  • 当在函数f( )上调用bind( )方法并传入一个对象o作为参数,这个方法将返回一个新的函数。
  • (以函数调用的方式)调用新的函数将会把原始的函数f( )当做o的方法来调用。
  • 传入新函数的任何实参都将传入原始函数。
  • 通过bind( )为函数绑定调用上下文之后,返回的函数不能通过call和apply修改调用上下文对象。
let obj={
     
    x:10,
    show:function(y){
     
        let r='';
        for(let i=0;i<y;i++){
     
            r+=this.x+'';
        }
        console.log(r);
    },
};
obj.show(4);
let ss=obj.show.bind({
     x:1000});
ss(3);
//10101010
//100010001000

ss.call({
     x:2000},3);
//100010001000
let sum=function(x,y){
     
    return x+y;
};
let succ=sum.bind(null,1);
console.log(succ(2));
let obj={
     };
obj.supAdd=sum.bind(obj,10);
//偏函数,固定一个函数的一个或者多个参数,返回一个新的函数,这个函数用于接受剩余的参数。
console.log(obj.supAdd(20));

//3
//30

偏函数的好处:

  1. 通过创建一个名称易懂的独立函数,调用是无需每次传入第一个参数,因为第一个参数通过bind提供了固定值。
  2. 当我们有一个很通用的函数,为了方便提供一个较常用的变体。

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