javascript深入理解函数

javascript深入理解函数

函数声明方法

1.function 函数声明语句

function add(a,b){
    return a+b;
}
console.log(add(1,2));

2.函数表达式

var add = function (a,b) {
    return a+b;
}
console.log(add(3,2));

3.构造函数

Function 关键字 前面参数为函数形参,最后一个参数为返回值

var add = new Function("a","b","return a+b");
console.log(add(5,6));

函数传参

函数调用时可以传入任意多个实参,函数定义时也可以定义任意多个形参

// 示例一
function func1() {
    // 通过arguments获取函数实参列表
    console.log(arguments);
}  
func1(1,"a",{"name":"apple"});

// 示例二
function func2(a,b,c) {
    // 当传入实参个数小于形参个数时,其余值为undefined
    console.log(a,b,c);
}
func2(1,2);

函数传参的类型

数据类型分为基本数据类型和引用数据类型
基本数据类型:变量中直接存储数据的值,例如Number、String、Boolean、undefined、null
引用数据类型:变量中存储的是数据的地址,获取变量内容时,通过数据的地址查找到相应位置的数据,例如Object等
函数传入实参为基本数据类型时,通过传值的形式,将数据的值复制一份给形参
函数传入实参为引用数据类型时,通过传址的形式,将数据的地址复制一份给形参

// 传值:函数内修改形参不影响函数外变量
var num = 0;
function setNum(n) {
    n = 100;
    console.log(n); // 100
}
setNum(num);
console.log(num); // 0

// 传址:函数内修改形参可能会影响函数外变量  
// 案例一  
var person = {
    name:"alex",
    age:18
}
function setAge(obj) {
    obj.age = 20;
    console.log(obj.age);  // 20
}
setAge(person); 
console.log(person.age); // 20

// 案例二
var person = {
    name:"alex",
    age:18
}
function setAge(obj) {
    obj = {
    name:"apple",
    age:33
}
    console.log(obj.age);  // 33
}
setAge(person); 
console.log(person.age); // 18

传址:函数实参person-》复制person地址给函数形参obj
下一步中
案例一:函数形参中存储的是person对象的地址,通过地址找到person对象,修改age
案例二:函数形参中存储的地址被修改为新对象的地址,故现在obj变量与person变量无关,obj变量存储的地址为新对象地址。

函数重名

js中没有函数重载一说法,因为形参和实参没有个数和类型限制
如果函数重名时,在非严格模式下最后定义的同名函数会覆盖前面定义;严格模式下回报错

function add(a,b) {
    console.log("add1");
    return a+b;
}
function add() {
    console.log("add2");
    return arguments[0]+arguments[1];
}
add();

函数重载案例

add函数可以求和,可以不传入参数,可以传入列表求和,可以传入参数求和

function add() {
    //1. 如果没有参数,不用计算
    if(!arguments.length){
        return 0;
    }else{
        var arr = null;
        //2. 如果第一个参数为一个数组,计算数组元素和
        if(Array.isArray(arguments[0])){
            arr = arguments[0];
        }else{
            arr = Array.apply(null,arguments);
        }
        // 求和:下面这一步等价于for循环求和
        // reduce作用 遍历arr数组,将所有值累加起来
        // total代表上一步总值,num当前元素值
        var result = arr.reduce(function(total,num){
            return total + num;
        })
        return result;
    }
}
console.log(add());
console.log(add([1,2,6,7]));
console.log(add(1,2,134));

函数返回值

函数中没有return语句时,默认返回undefined
return 可以返回任意值

// 没有return语句,默认返回undefined
function func1(a,b) {
    var c = a / b;
}
console.log(func1(1,2));

// return可以返回一个任意类型的值
function func2(a,b) {
    return a+b;
}
console.log(func2(1,3));

new语句的函数返回值

如果使用new一个函数对象,其return不为对象或者没有返回值时,默认为一个constructor构造函数对象。

// new一个函数时,返回值为一个对象
function func3(a,b) {  
    var c = a + b;
}
var func = new func3();  //这个括号可以省略
console.log(func); // 返回值为一个

function func4(a,b) {  
    var c = a + b;
    return {
        "a":111
    }
}
var func = new func4();  //这个括号可以省略
console.log(func); // 返回值为一个

特例

一般return语句指向完毕后,就不会执行后面语句
但是如果有try{}catch(){}语句时,会有特殊情况发生

// 案例1:当语句中有错误时,不会向后执行return语句
function func(){
    try{
        var a = 1;
        console.log(b);  // 报错,跳转到catch部分
        return 0;
    }catch(err){
        console.log(err);  // ReferenceError: b is not defined
        return 1;
    }
}
console.log(func()); // 1

// 案例二:有finally时,try语句中return不会生效
function func1() { 
    try{
        var a = 1;
        return 0;
    }catch(e){
        console.log(e);
        return 1;
    }finally{
        return 2;
    }
}
console.log(func1());  // 2  

函数调用方法

直接调用

直接使用函数名+小括号就可以调用函数

// ()括号调用
function add(a,b) {
    return a+b;
}
console.log(add(1,2));

// 对象方法调用
obj = {
    name:"alex",
    say:function(){
        console.log(this.name);
    }
}
obj.say();

// new调用时,返回一个函数对象
var myFunc = new add;
console.log(myFunc);

间接调用

主要有call()方法和apply()方法

// 间接调用
// call(this指向的对象,参数1,参数2...)        
function add(a,b) {
    return a+b;
}
// 如果第一个参数为null,则this指向默认
console.log(add.call(null,1,3));

// apply(this指向的对象,[参数列表])
function sum(a,b,c) {
    return a+b+c;
}
console.log(sum.apply(obj,[1,3,5]));

每个函数都有非继承的方法apply()和call()两个方法
当其第一个参数传入的为null,undefined时,函数this指向全局对象
严格模式下,必须指定值

函数属性

fn.length 返回函数形参的个数
fn.name 返回函数名
fn.prototype 返回函数原型,即函数的父对象

function add(a,b) {
    return a+b;
}
console.log(add.length); // 2
console.log(add.name); // add
console.log(add.prototype);  // {constructor: ƒ} 

// 函数表达式,name返回变量名
var sum = function (a,b) {
    return a+b;
}
console.log(sum.name); // sum

// 具名函数时,name返回函数名
var hi = function hello() {
    // hello 函数名只能在函数内部使用,不能在外部使用
    console.log(hello.name);
    return 0;
}
// hello();  // 报错:07函数的属性.html:22 Uncaught ReferenceError: hello is not defined
hi();  // hello
console.log(hi.name); // hello

函数方法

bind方法使用

用来给函数绑定this指向的对象

// find是用来绑定this对象
var name = "alex";
var obj = {
    name:"apple"
}
function fn() {
    console.log(this.name);  // 默认this指向window
}
fn();  // alex
var gn = fn.bind(obj);  // 将this指向obj对象
gn(); // apple

案例二

bind的第一个参数为this对象的指向,后面参数为函数的参数

function func1(name,age,fav) {
    var str = "my name is "+name+",I am "+age+",I like "+fav;
    console.log(str);
}
// 传入一个函数参数
var part1 = func1.bind(null,"alex");
part1(18,"comic");   // my name is alex,I am 18,I like comic
part1(22,"animation"); // my name is alex,I am 22,I like animation

// 传入两个函数参数
var part2 = func1.bind(null,"egon",22);
part2("html");   // my name is egon,I am 22,I like html
part2("javascript");   // my name is egon,I am 22,I like javascript

这是函数式编程的一种–函数柯里化
类似于python的偏函数

call和apply的应用

案例1:找出数组中的最大元素

// 1.找出数组的最大元素
function max(arr) {  
    // Math.max()  在传入参数中找到最大元素
    // 默认调用方法 Math.max(6,9,5)  返回9
    var res = Math.max.apply(null,arr);
    return res;
}
console.log(max([1,9,8,7,6,5])); // 9

2.将类数组转换成真正的数组

// 例如:arguments类似于数组,但是很多数组方法不能使用,转换成为真正的数组
function toArr() {  // 功能:传入任意数量参数,返回一个数组
    var arr = Array.prototype.slice.call(arguments);
    return arr;
}

数组原型上都有slice方法,slice切片可以获取数组一部分
例如:[1,2,3,4,5].slice(2,4) 返回[3,4]
不传入参数时,返回与原来一样元素的新数组(浅拷贝)
传入一个参数时,返回一个从该索引开始向后所有元素的数组
传入两个参数时,返回从起点到终点(不包括)之间的元素

3.数组追加

push方法,将参数一个一个插入到数组尾部
使用方法arr1.push(6,8) 等价于下面操作

// 3.数组追加:将一个数组中所有元素插入到另一个数组尾部
function extend(arr1,arr2) {
    // 等价于arr1.push.apply(arr2);  arr1就地改变
    return Array.prototype.push.apply(arr1,arr2);
}
arr1 = [1,2,3];
arr2 = [6,8];
console.log(extend(arr1,arr2)); // [1,2,3,6,8]

类似于实现python列表的extend方法

4.利用call和apply做继承

person = {
    name:"apple",
    age:18
}
function Info(name,age) {
    this.name = name;
    this.age = age;
    this.sayAge = function () {
        console.log(this.age);
    }
}
// 默认创建一个新对象
var egg = new Info("egg",15);
egg.sayAge()  // 15

// 使用call修改this指向,使其修改person对象
console.log(person);  // {name: "banana", age: 20, sayAge: ƒ}
Info.call(person,"banana",20);
console.log(person);
person.sayAge(); // 20  

5.apply方法修改函数调用方式

// apply方法修改函数调用方式
function Log() {
    console.log.apply(null,arguments); // 将该函数所有参数传递给console.log()去执行
}
Log("hello console.log"); //hello console.log
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页