zoukankan      html  css  js  c++  java
  • JS中函数的本质,定义、调用,以及函数的参数和返回值

    要用面向对象的方式去编程,而不要用面向过程的方式去编程


    对象是各种类型的数据的集合,可以是数字、字符串、数组、函数、对象……

    对象中的内容以键值对方式进行存储

    对象要赋值给一个变量

        var cat={
            "name":"喵1",
            "age":4,
            "family":["喵爸","喵妈"],
            "speak":function(){
                console.log("喵喵~");
            },
            "friend":{
                "name":"喵2",
                "age":5
            }
        }

    上面是对象的字面量的创建方式,简单直接

    除此之外,还有对象的构造函数的创建方式

        var cat=new Object();

    还有JavaScript5新增的一种方式

    该方式在老版本的浏览器中存在兼容性问题

        Object.create();

    获取、设置、添加、修改对象的值:

    对象.属性名=属性值

    对象[ 属性名 ]=属性值

        var cat={
            "name":"喵1",
            "age":4,
            "family":["喵爸","喵妈"],
            "speak":function(){
                console.log("喵喵~");
            },
            "friend":{
                "name":"喵2",
                "age":5
            }
        }
    
        cat.name="喵喵1";
        cat.age=6;
        cat.type="英短";
        console.log(cat.name);//喵喵1
        console.log(cat["age"]);//6
        console.log(cat.type);//英短

    删除对象的属性:

    delete 对象.属性

        var cat={
            "name":"喵1",
            "age":4,
            "family":["喵爸","喵妈"],
            "speak":function(){
                console.log("喵喵~");
            },
            "friend":{
                "name":"喵2",
                "age":5
            }
        }
    
        cat.type="英短";
        console.log(cat.type);//英短
    
        delete cat.type;
        console.log(cat.type);//undefined

    检测对象是否拥有某个属性:

    属性名 in 对象

        var cat={
            "name":"喵1",
            "age":4,
            "family":["喵爸","喵妈"],
            "speak":function(){
                console.log("喵喵~");
            },
            "friend":{
                "name":"喵2",
                "age":5
            }
        }
    
        console.log("name" in cat);//true
        console.log("type" in cat);//false

    对象的枚举,遍历对象中的各个属性

        var cat={
            "name":"喵1",
            "age":4,
            "family":["喵爸","喵妈"],
            "speak":function(){
                console.log("喵喵~");
            },
            "friend":{
                "name":"喵2",
                "age":5
            }
        }
    
        for(var p in cat){
            console.log(p);
            //name age family speak friend
        }
        var cat={
            "name":"喵1",
            "age":4,
            "family":["喵爸","喵妈"],
            "speak":function(){
                console.log("喵喵~");
            },
            "friend":{
                "name":"喵2",
                "age":5
            }
        }
    
        for(var p in cat){
            console.log(p+": "+cat[p]);
    
            console.log(p);//获取属性名
            //console.log(cat.p);// 写法错误
            console.log(cat[p]);//获取属性值 写法正确
    
            console.log(cat["n"+"ame"]);//喵1  []中可以添加字符串的拼接等操作
        }

    匿名函数,如:

    window.onload=function(){
    
    }

    函数一次执行完毕之后,会将局部作用域和局部变量销毁,因此外部无法调用到

    但函数本身并没有被销毁,可以进行多次调用执行


    为什么要使用函数:

    代码复用(自己的代码和别人的代码,如jquery)

    统一修改和维护

    增加程序的可读性


    函数的本质:对象

    定义方式:字面量定义、构造函数定义

    //字面量定义
    function add(n1,n2){
    
    }
    //构造函数定义
    new Function("n1","n2","....");

    函数和对象一样,可以添加属性和方法

    function person(){
        console.log("cyy");
    }
    //添加属性
    person.age=25;
    //添加方法
    person.speak=function(words){
        console.log(words);
    }
    console.log(person.age);//25
    person.speak("hh~");//hh~
    person();//cyy

    函数可以作为数据值使用:

    作为数据值保存在一个变量中

    var fn=function(){
        return "这是一个函数";
    }
    console.log(fn());//这是一个函数
    console.log(fn);
    /*
    ƒ (){
        return "这是一个函数";
    }
    */

    此时fn打印出来的就是函数本体

    函数也可以作为参数来使用:

    function fn(){
        alert(1);
    }
    setTimeout(fn,1000);//此处需要传函数本体
    //此处不能加括号,如果加了括号,会立刻调用,而不是等到1秒之后

    函数可以作为返回值使用:

    function fn(){
        return function(){
            console.log("fn中的fn");
        }
    }
    //调用
    var newFn=fn();
    newFn();//fn中的fn
    
    // 或者
    
    fn()();//fn中的fn

    函数的三种定义方式

    // 字面量方式
    
        // function 声明
        function add(){
    
        }
    
        // var 赋值表达式
        var add=function(){
    
        };
    
    //构造函数
        var add=new Function("num1","num2","return num1+num2");
        add();

    区别:

    字面量方式比构造函数方式简洁

    最重要的是预解析的区别

    funtion声明的函数,可以先调用,再创建

    函数预解析的时候会提前定义

        add();
        function add(){
            return 1;
        }

    用var赋值表达式创建的函数,如果先调用,再创建,会报错

    因为var在预解析时赋值为undefined

        add();
        var add=function(){
            return 1;
        };

    function声明和var赋值表达式声明,这两种都是很好的选择

    构造函数过于复杂,不推荐使用


    函数定义的位置

    全局作用域下的函数,在哪里都能调用

    add();
    function add(){
        add();
    }
    add();
    
    function fn(){
        add();
    }

    局部作用域下的函数

        //fn();  无法调用
        function add(){
            fn();
            function fn(){
                fn();
                function fn3(){
                    fn();
                }
            }
            function fn2(){
                fn();
            }
        }
        //fn();  无法调用

    里层可以访问外层的函数,外层不能访问里层的函数

    代码块中定义的函数:

    由于js中没有块级作用域,所以依然是处于全局作用域中

    都会出现预解析中函数被提前声明

    if(true){
        function fn1(){
    
        }
    }else{
        function fn2(){
            
        }
    }

    改成下面这样就可以实现按照条件进行声明,也是因为预解析的机制

    if(true){
        var fn1=function (){
    
        }
    }else{
        var fn2=function fn2(){
    
        }
    }

    对象中的函数

    使用对象.函数名进行调用

    var person={
        name:"cyy",
        setAge:function(age){
            this.age=age;//this指向当前对象
        }
    }
    person.setSex=function(sex){
        this.sex=sex;
    }
    person.setAge(25);
    person.setSex("girl");
    console.log(person.age);//25
    console.log(person.sex);//girl

    普通函数的调用:

    命名函数的调用

    function add(){
    
    }
    add();

    匿名函数的调用:

    如果直接在匿名函数后面加上括号进行调用,会报错

    function(){
        alert(1);
    }();//Uncaught SyntaxError: Unexpected token (

    解决方法是,将这段匿名函数执行的代码,赋值给一个变量

    var fn=function(){
        alert(1);
    }();//1

    第二种解决方法:

    将函数用括号括起来,实现匿名函数自执行

    (function(){
        alert(1);
    })();//1

    括号把整体括起来也能实现一样的效果

    (function(){
        alert(1);
    }());//1

    或者在function前加上合法的字符也可以,如!+-~

    !function(){
        alert(1);
    }();//1

    或者放在console.log里面

    console.log(function(){
        alert(1);
    }());

    以上这些方式的共同目的,就是不让匿名函数的function在开头位置出现


    递归调用:

    自己调用自己

    实现阶乘

    function fn(num){
        if(num<=1) return 1;
        return num*fn(num-1);
    }
    console.log(fn(5));
    /*
    return 5*fn(4)
    return 5*4*fn(3)
    return 5*4*3*fn(2)
    return 5*4*3*2*fn(1)
    return 5*4*3*2*1
    */

    匿名函数也是函数,当它自执行的时候,会创建自己的函数内部作用域,在执行完毕之后会被销毁,因此在外部无法访问到自执行的匿名函数内部

    //此处创建函数内部作用域
    (function add(n1,n2){
        return n1+n2;
    })();
    console.log(add(3,4));//在全局无法访问到函数内部的函数add

    方法的调用:

    对象中的方法,使用对象.方法名进行调用

    var operation={
        add:function(n1,n2){
            return n1+n2;
        },
        sub:function(n1,n2){
            return n1-n2;
        }
    }
    console.log(operation.add(3,4));//7

    以下这种也是方法,是点击浏览器时浏览器自动帮我们完成调用;

    也可以使用方法调用的方式来进行调用

        document.onclick=function(){
            alert(1);
        }
        document.onclick();//等同于点击屏幕的效果

    关于对象中的属性,什么时候加引号,什么时候不加引号

    对于合法的标识符,加不加引号都可以;

    不合法的标识符,必须加引号,否则会引起报错

    var operation={
        add:function(n1,n2){
            return n1+n2;
        },//合法的属性名可以不加引号
        sub:function(n1,n2){
            return n1-n2;
        },
        "@":function(){
    
        }//不合法的属性名,会引起报错,必须加引号
    }

    合法的标识符,调用时使用对象.方法名即可

    非法的标识符,调用时使用对象[ " 方法名 " ]

    var operation={
        add:function(n1,n2){
            return n1+n2;
        },//合法的属性名可以不加引号
        sub:function(n1,n2){
            return n1-n2;
        },
        "@":function(word){
            alert(word);
        }//不合法的属性名,会引起报错,必须加引号
    }
    console.log(operation.add(2,5));//7
    console.log(operation["@"]("hh~"));//hh~

    [ ] 加引号和不加引号的区别

    var operation={
        add:function(n1,n2){
            return n1+n2;
        },//合法的属性名可以不加引号
        sub:function(n1,n2){
            return n1-n2;
        },
        "@":function(word){
            return word;
        },//不合法的属性名,会引起报错,必须加引号
        key:function(n1,n2){
            return "key~";
        }
    }
    var key="add";
    console.log(operation.key(2,3));//key~
    console.log(operation["key"](2,3));//key~
    console.log(operation[key](2,3));//5

    方法的链式调用

    如jquery

    $("p").html("html").css("color","red")....

    对象中要使用链式调用,则方法中需要返回当前对象

    var operation={
        add:function(n1,n2){
            console.log(n1+n2);
            return this;
        },
        sub:function(n1,n2){
            console.log(n1-n2);
            return this;
        }
    }
    operation.add(5,3).sub(4,2);
    //要保证operation.add(5,3)能够返回operation对象
    //就需要添加return this

     构造函数的调用:

    构造函数命名时一般首字母大写

    调用时用new+函数名,返回值是一个对象

    function Person(){
    
    }
    var obj=new Person();

    js中内置的构造函数,常见的有:

    Object()
    new Object()
    
    Array()
    new Array()

    通过new关键字来调用

    用构造函数的方式定义对象和数组,并添加内容

    var person=new Object();
    person.name="cyy";
    
    var arr=new Array();
    arr[0]=1;

    函数的间接调用

    .call   第一个参数是改变this的指向,后面传递参数的方式就是一个一个传

    .apply   第一个参数是改变this的指向,后面传递参数的方式是通过数组来传递(或者类数组)

    var name="cyy";
    var person={};
    person.name="cyy2";
    person.getName=function(){
        return this.name;//此处的this指向person对象
    }
    console.log(person.getName());//直接调用  cyy2
    
    console.log(person.getName.call(window));//间接调用,此时this被指向了window,返回的是window.name  cyy
    console.log(person.getName.apply(window));//间接调用  cyy
    function add(n1,n2){
        return n1+n2;
    }
    console.log(add(1,2));//直接调用 3
    console.log(add.call(window,1,2));//间接调用 3
    console.log(add.apply(window,[1,2]));//间接调用 3
    function add(n1,n2){
        return n1+n2;
    }
    var arr=[4,6];
    console.log(add.apply(window,arr));//10

    只有函数拥有call和apply方法,两者唯一的区别在于它们的传参方式


    函数的参数

    参数传递的本质是将实参赋值给形参

    参数的个数

    1、形参个数=实参个数

    function add(n1,n2){
        return n1+n2;
    }
    console.log(add(3,5));

    2、实参个数 < 形参个数

    多用于有可选参数的情况

    function pow(base,pow=2){
        return Math.pow(base, pow);
    }
    console.log(pow(3));//9
    console.log(pow(3,3));//27

    3、实参个数 > 形参个数

    如果无法得知有多少个实参,可以使用arguments

    arguments是一个类数组,用于保存实参的信息

    通过arguments[index] 获取某一个参数

    arguments.length 实参的个数

    function add(){
        if(arguments.length==0) return;
        var sum=0;
        for(var i=0,len=arguments.length;i<len;i++){
            sum+=arguments[i];
        }
        return sum;
    }
    console.log(add());//undefined
    console.log(add(1,2,3,4,5));//15

    arguments 是类数组,实质上还是对象

    索引是数组下标,数字开头的变量名不合法,因此需要加引号

    {
        '0': 1,
        '1': 2,
        '3': 4,
        length: 3
    }

    可以通过arguments来修改参数的值

    function speak(m){
        arguments[0]="";
        return m;
    }
    console.log(speak("hh"));//

    arguments是每个函数中独有的,不会跨函数

    function fn1(){
        console.log(arguments);//Arguments [1, callee: ƒ, Symbol(Symbol.iterator): ƒ]
        function fn2(){
            console.log(arguments);//Arguments [2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
        }
        fn2(2);
    }
    fn1(1);

    arguments.callee  指代函数本身

    function add(){
        console.log(arguments.callee);
    }
    add();

    arguments.callee 常用于递归中

    function factorial(num){
        if(num==1) return 1;
        return num*factorial(num-1);
    }
    console.log(factorial(5));//120
    
    
    function factorial(num){
        if(num==1) return 1;
        return num*arguments.callee(num-1);
    }
    console.log(factorial(5));//120

    不过在严格模式下,不允许使用arguments.callee(也不允许不使用var声明变量)

    此时的解决方法就是将函数赋值给一个变量,这样函数本身的名字不会影响调用

    "use strict";
    
    var myfn=function factorial(num){
        if(num==1) return 1;
        return num*factorial(num-1);
    }
    console.log(myfn(5));//120

    实参的个数 arguments.length

    形参的个数 函数名.length 或者arguments.callee.length

    function add(n1,n2){
        if(arguments.length != add.length) throw new Error("请传入"+add.length+"个参数!");
    }
    console.log(add(5));//Uncaught Error: 请传入2个参数!

    什么做参数

    1、没有参数

    2、数字做参数

    3、字符串( 如选择DOM节点,$("p") )

    4、布尔值(保持函数的纯洁性,建议一个函数只做一件事情)

    5、undefined(可选参数必须放在最后)

    6、null

    7、数组 

    $.each(["a","b","c"],function(index,item)){
        console.log(index);//0 1 2
        console.log(item);//a b c
    }

    8、对象

    $.each({name:"cyy",age:24},function(index,item)){
        console.log(index);//name age
        console.log(item);//cyy 24
    }

    使用对象作为参数的好处(可以自由调换顺序)

    function fn(obj){
        var person={};
        person.name=obj.name||"cyy";
        person.age=obj.age||24;
        person.tel=obj.tel||110,
        person.addr=obj.addr||"China";
        return person;
    }
    var cyy={
        name: "cyy1",
        age:25
    }
    console.log(fn(cyy));//{name: "cyy1", age: 25, tel: 110, addr: "China"}

    9、函数

    回调函数,如 setTimeout(fn, time);


    函数的返回值

    return:

    表示函数结束

    将值返回

    什么可以做返回值:

    直接return ,返回值是undefined

    数字

    字符串 :alert() 输出的都是字符串,会默认调用.toString() 方法

    布尔值:常用于表单验证

    null 和 undefined

    数组

    function add(n1,n2){
        return [n1,n2,n1+n2];
    }
    console.log(add(5,6));//(3) [5, 6, 11]

    对象

    function fn(){
        return {
            name:"cyy",
            age:25
        }
    }

    注意return后面不要换行,否则默认是分号,到此结束;于是后面的会报错

    function fn(){
        //return会默认后面是分号,结束
        return 
        {
            name:"cyy",
            age:25
        }
    }

    函数

    需要用()()来调用


    document.write() 执行时会调用.toString() 方法,尝试将结果转换为字符串形式

    document.write([1,2,3]);//1,2,3
    document.write({
        name:"cyy"
    });//[object Object]
    document.write({
        name:"cyy",
        toString:function(){
            return "hh~";
        }
    });//hh~
    function count(){
        var num=1;
        return function(){
            return num++;
        }
    }
    //每次调用count()时,num都会被初始化
    //并且return num++ 是先返回num,再执行++
    console.log(count()());//1
    console.log(count()());//1
    console.log(count()());//1
    //count()只执行了一次,因此num只初始化一次
    //后面能够每次都进行递增+1
    var fn=count();
    console.log(fn());//1
    console.log(fn());//2
    console.log(fn());//3
  • 相关阅读:
    A/B test
    镜像下载python包
    Jupyter 快捷键汇总
    牛顿法(Newton's method)VS梯度下降法(Gradient Descent)
    L-BFGS算法详解(逻辑回归的默认优化算法)
    np.bincount()频率统计函数
    异常值检验实战3_NBA球员表现稳定性分析
    异常值检测方法(Z-score,DBSCAN,孤立森林)
    浅谈压缩感知(十六):感知矩阵之RIP
    浅谈压缩感知(十五):感知矩阵之spark常数
  • 原文地址:https://www.cnblogs.com/chenyingying0/p/12297122.html
Copyright © 2011-2022 走看看