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

原文地址:https://www.cnblogs.com/chenyingying0/p/12297122.html

时间: 2024-10-06 16:29:19

JS中函数的本质,定义、调用,以及函数的参数和返回值的相关文章

Android调用远程Service的参数和返回值都需要实现Parcelable接口

import android.os.Parcel;import android.os.Parcelable; public class Person implements Parcelable{ private Integer id; private String name; private String pass; public Person() { super(); } public Person(Integer id, String name, String pass) { super()

Js中常用的字符串,数组,函数扩展

由于最近辞职在家,自己的时间相对多一点.所以就根据prototytpeJS的API,结合自己正在看的司徒大神的<javascript框架设计>,整理了下Js中常用一些字符串,数组,函数扩展,一来可以练练手,二来也锻炼下自己的代码能力.由于代码里面的注释自认为已经非常详细,所以就直接贴代码了. 1. 字符串扩展: ;(function() { var method, stringExtends = { /** * 删除字符串开始和结尾的空白 * @returns {string} */ stri

javascript学习笔记(二):定义函数、调用函数、参数、返回值、局部和全局变量

定义函数.调用函数.参数.返回值 关键字function定义函数,格式如下: function 函数名(){ 函数体 } 调用函数.参数.返回值的规则和c语言规则类似. 1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta chaset="UTF-8"> 5 <title></title> 6 </head> 7 <body

C++内联函数、宏定义和普通函数的区别

C++内联函数.宏定义和普通函数的区别? 宏定义:在预处理阶段进行简单的文本替换,不会进行参数类型检查: 内联函数:在编译器的时候进行代码插入,编译器会在每次调用内联函数的地方直接将内联函数的内容展开,这样可以省去函数压栈和出站的开销提高效率.会进行参数                  类型检查: 普通函数:在函数调用时,跳转到函数地址执行代码: 原文地址:https://www.cnblogs.com/dingou/p/10549225.html

Python(74)_编写装饰器,为多个函数加上记录调用功能,要求每次调用函数都将被调用的函数名写入文件

#-*-coding:utf-8-*- import os import time from functools import wraps ''' 1.编写装饰器,为多个函数加上记录调用功能,要求每次调用函数都将被调用的函数名写入文件 ''' def log(func): def inner(*args,**kwargs): with open('log1.txt','a',encoding='utf-8') as f: f.write(func.__name__+'\n') ret = fun

函数参数,返回值,递归函数

函数进阶 目标 函数参数和返回值的作用 函数的返回值 进阶 函数的参数 进阶 递归函数 01. 函数参数和返回值的作用 函数根据 有没有参数 以及 有没有返回值,可以 相互组合,一共有 4 种 组合形式 无参数,无返回值 无参数,有返回值 有参数,无返回值 有参数,有返回值 定义函数时,是否接收参数,或者是否返回结果,是根据 实际的功能需求 来决定的! 如果函数 内部处理的数据不确定,就可以将外界的数据以参数传递到函数内部 如果希望一个函数 执行完成后,向外界汇报执行结果,就可以增加函数的返回值

C++笔记(3):函数的参数和返回值

刚学C++那会,做课程设计的时候总是会去网上很找别人写好的程序来参考,那时候看到函数参数列表里各种复杂的类型和奇怪的写法就头大,后来总算是慢慢搞清楚了,在此对函数各种类型的形参以及函数的返回值进行一下总结. 1.普通形参 传递普通形参也就是值传递,传递的是实际参数的一个副本,当函数被调用时,形参复制实参,也就是说此时形参和实参的值是一样的,但形参在内存中拥有自己的地址.当函数结束时形参的生命周期终止,函数内部的操作不会影响到实参的值.经典的值交换函数代码如下: void swap1(int a,

javascript函数参数、返回值类型检查

实现带参数.返回值类型声明的js函数: 类型定义:window.Str = Type.Str = Type.define('STRING', Type.isStr);var Per = Type.define('PERSON', function(p){    return p && p.type === 'person' && p.name;}); 定义函数:var addStr = Str(function(a, b){  return a + b;}, Str, St

override(重写,覆盖) 1、方法名、参数、返回值相同。 2、子类方法不能缩小父类方法的访问权限。 3、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。 4、存在于父类和子类之间。 5、方法被定义为final不能被重写。 overload(重载,过载) 1、参数类型、个数、顺序至少有一个不相同。 2、不能重载只有返回值不同的方法名。 3、存在于父类和子

override(重写,覆盖) 1.方法名.参数.返回值相同. 2.子类方法不能缩小父类方法的访问权限. 3.子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常). 4.存在于父类和子类之间. 5.方法被定义为final不能被重写. overload(重载,过载) 1.参数类型.个数.顺序至少有一个不相同.   2.不能重载只有返回值不同的方法名. 3.存在于父类和子类.同类中. 方法的重写(Overriding)和重载(Overloading)是Java多态性的不同表现. 重写(O

存储过程输出参数、返回值、返回表及C#调用

存储过程中可以定义输出变量,返回值,执行存储过程还能获得结果集.每个存储过程的默认返回值为0.下面紧接着上文 SQL Server中存储过程Stored Procedure创建及C#调用 基础上写的一个新的Stored Procedure存储过程则包含了输出参数.返回值以及select结果. USE [db] GO /****** Object: StoredProcedure [dbo].[insert_persions] Script Date: 2/25/2015 11:14:11 AM