函数定义、函数属性和方法

函数

函数是定义一次但却可以调用或执行任意多次的一段js代码。通过函数可以封装任意多条语句,而且可以在任何地方任何时候调用执行。

函数本身没有运行功能,必须被调用才可以执行。

function fn() {//函数声明
    console.log("我只有被调用才可以执行!");
}
fn(); //调用

一、 函数定义

1、函数声明(常用方式):

ECMAScript中的函数使用function关键字来声明,后跟一组参数以及函数体.

语法如下:

function functionName(arg0, arg1,,, aegN() {
    statements(声明,表现);
} //括号结束位置不能有分号

示例:

function sayHi(name,message) {
    alert("hello" + name + message);
}

函数会在return语句执行完之后停止并立即退出.因此,位于return语句之后的代码永远都不会执行!

function sayHi(name,message) {
    return name + message;
    alert("hello");     //不会执行
}

2、 函数表达式

2.1、函数表达式
var test = function 函数名称(参数) {
    函数体
};  //括号结束位置必须有分号
2.2、 构造函数(不推荐使用)

构造函数从技术角度来讲,也是一个函数表达式。

构造函数,这种语法会导致解析两次代码(第一次是解析常规ECMAScript代码,第二次是解析传入构造函数中的字符串),从而影响性能。

var test = new Function("num1", "num2", "num3", "num4");

var sayHi = new Function("sName", "sMessage", "alert('Hello ' + sName + sMessage);");
sayHi("jzj,", "你好!");//Hello jzj,你好!
2.3、立即调用函数

新建作用域,防变量污染【养成使用立即调用函数的好习惯!!】

方式一:
    (function() {
        console.log(1);
    }());     //1       空括号在里面



(function(x,y) {
    console.log(x*y);   //6
}(2,3));

方式二:
    (function() {
        console.log(2);
    })();     //2        空括号在外面

此处不加括号会报错(语法上的错误).只能用一次

其他方式详见:《function与感叹号、一元操作符》

二、return返回值

函数会在执行完return语句之后停止并立即退出。因此,位于return语句之后的任何代码永远都不会执行。注意与break的区别:break只能用在循环和switch分支语句中

function fn() {
    return 234;
    return 78; //永远不会执行
    console.log("hi"); //永远不会执行
}
fn();

推荐的做法是要么让函数始终都返回一个值,要么永远都不要返回值。否则,如果函数有时候返回值,有时候又不返回值,会给调试代码带来不便。

三、参数

1、理解参数

ECMAScript函数不介意传入多少个参数,也不介意传入的参数是什么数据类型.即:即使定义了两个参数,在传入的时候,也可以传入一个,或三个甚至不传.原因是ECMAScript中的参数在内部是用一个数组表示的.函数接收到的始终是这个数组,而不关心数组中包含哪些参数.

没有传递值的命名参数,它的值将被自动赋予undefined值.

2、arguments对象

在函数体内可以通过arguments对象来访问这个参数数组,从而获取传递给函数的每个参数.arguments对象只是与数组类似(并不是Array实例无关),arguments[INDEX]来回去参数的值(不显示地使用命名参数)。

示例:

function sayHi() {
    alert("hello" + arguments[0] + arguments[1]);   //返回hello+传入参数的第一个和第二个
}
2.1、arguments有length属性,返回传入参数的个数.例如:
例:
function fn() {
    var marks = 0;
    for(var i = 0; i < arguments.length; i++) {
        marks += arguments[i];
    }
    return marks;
}
console.log(fn(1,3));
2.2、arguments对象也可以和命名参数一起使用.

argument对象的长度是有传入的参数个数决定的,不是有定义函数是的命名函数的个数决定的.

例如:

function sayHi(name,message) {
    if(arguments == 1) {
        alert(name + "hello");      
    } else if(argument == 2) {
        alert(name + message)
    }
}
sayHi("w");         //whello
sayHi("he","1");    //he2

name和arguments[0]的值相同,因此可以互换使用(message和arguments[1]也是如此).

2.3、argument[INDEX]和命名参数区别

它们的内存空间是独立的,但它们的值会同步.

四、重载

ECMAScript中的函数,没有像其他高级语言那种函数重载功能。因此如果出现同名的函数则后声明的函数将前面的函数覆盖掉。

所谓重载功能:即可以为一个函数编写两个定义,只要这两个定义的签名(接收的参数的类型和数量)不同即可。

例:
function fn(x) {
    return x + 100;
}
function fn(x) {
    return x + 200;
} //覆盖前面同名函数
console.log(fn(1)); //201

五、函数声明式和函数表达式

函数声明和函数表达式的区别:

解析器会率先读取函数声明,并使其在执行任何代码之前可用(可访问);而函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被执行。

六、函数作为返回值

function createComparisonFn(params) {  //传入对象属性的名称
    return function(obj1, obj2) { //obj1和obj2为数组的项
        var val1 = obj1[params]; //获取对象属性值
        var val2 = obj2[params];
        if(val1 > val2) {
            return 1;
        } else if(val1 < val2) {
            return -1;
        } else {
            return 0;
        }
    }
}

var arr = [{name: “zhangsan”, age: 12}, {name: “lis”, age: 38}, {name: “wanger”, age: 32}];
console.log(arr.sort(createComparisonFn(“age”))); //按age升序排列
console.log(arr); //按age升序排列

根据对象age属性进行排序。//sort()方法的比较函数接收两个参数,即要比较的值。

七、函数内部属性(arguments和this对象、caller属性)

在函数内部,有两个特殊的对象:

  • arguments; 保存函数的参数。arguments.callee指向有这个arguments对象的函数。
  • this; 指向函数据以执行的环境对象。

函数对象的属性:

  • caller。指向调用当前函数的函数(即当前函数的调用者)。

1、arguments

arguments的主要用途是保存函数参数,这个对象还有一个名叫 callee 的属性,该属性是一个指针,指向拥有这个 arguments 对象的函数

var fn = function(num) {
    if(num <= 1) {
        return 1;
    } else {
        return num * fn(num - 1);
    }
};
var total = fn;
console.log(total(3)); //6
fn = function() {
    return 1000;
};
console.log(total(3)); //3000 首先调用第一个fn进入第一个fn方法体内,return的时候3 * fn()调用的是第二个fn

定义阶乘函数一般都要用到递归算法;如上面的代码所示,在函数有名字,而且名字以后也不会变
的情况下,这样定义没有问题。但问题是这个函数的执行与函数名 factorial 紧紧耦合在了一起。为
了消除这种紧密耦合的现象,可以像下面这样使用 arguments.callee。

var fn = function(num) {
    if(num <= 1) {
        return 1;
    } else {
        return num * arguments.callee(num - 1);
    }
};
var total = fn;
console.log(total(3)); //6
fn = function() {
    return 1000;
};
console.log(total(3)); //6




var fn = function(num) {
    console.log(arguments.callee);
};
var total = fn;
fn(); //function(num);——指向第一个fn
total(); //function(num);——指向第一个fn
fn = function(a) {
    console.log(arguments.callee);
};
fn(); //function();——指向第二个fn
total(); //function(num);——指向第一个fn

2、this

函数内部的另一个特殊对象是this。this引用的是函数据以执行的环境对象——或者也可以说是this 值(当在网页的全局作用域中调用函数时,this 对象引用的就是 window。因为定义在全局中的所有变量或函数都是window对象的属性)。

color = "red"; //定义在全局中的变量
var obj = {color: "blue"};
var fn = function() {
    console.log(this.color);
};
obj.foo = fn;
fn(); //red
obj.foo(); //blue

由于在调用函数之前,this的值并不确定,因此this可能会在代码执行过程中引用不同的对象。

【注意:】函数的名字仅仅是一个包含指针的变量而已。因此,即使是在不同的环境中执行,全局的fn函数与obj.foo()指向的任然是同一个函数

3、caller

这个属性中保存着调用当前函数的函数的引用(即当前函数的调用者),
如果是在全局作用域中调用当前函数,它的值为 null。

window.onload = function() {
    var obj = {color: "blue"};
    var fn = function() {
        console.log(fn.caller); //指向调用当前函数的函数的引用
    };
    var fn1 = function() {
        fn();
    };
    fn(); //window.onload调用的fn,因此指向window.onload;
    fn1(); //fn1调用的fn,因此指向fn1
};



var fn = function() { //定义在全局中的函数fn
    console.log(fn.caller); //指向调用当前函数的函数的引用
};
fn(); //null

为了实现更松散的耦合,也可以通过 arguments.callee.caller
来访问相同的信息。
var fn = function() {
console.log(arguments.callee.caller); //指向调用当前函数的函数的引用
};

当函数在严格模式下运行时,访问 arguments.callee 会导致错误。ECMAScript 5 还定义了
arguments.caller 属性,但在严格模式下访问它也会导致错误,而在非严格模式下这个属性始终是
undefined。定义这个属性是为了分清 arguments.caller 和函数的 caller 属性。以上变化都是为
了加强这门语言的安全性,这样第三方代码就不能在相同的环境里窥视其他代码了。

严格模式还有一个限制:不能为函数的 caller 属性赋值,否则会导致错误。

八、函数属性和方法

每个函数都包括两个属性:length和prototype。
- length属性表示函数希望接收的命名参数的个数(即函数定义的形式参数的个数)。
- prototype属性;

1、length属性

var fn = function() {
    return ;
};
var fn1 = function(a) {
    return a;
};
var fn2 = function(a, b) {
    return a+b;
};
console.log(fn.length); //0
console.log(fn1.length); //1
console.log(fn2.length); //2

2、prototype属性

在 ECMAScript 5中,prototype 属性是不可枚举的,因此使用 for-in 无法发现。

3、apply()、call()和bind()方法

每个函数都包含两个非继承而来的方法:apply()和 call()。
这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
- apply(); 接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象。
- call(); 与apply方法作用相同,唯一不同的是除了第一个参数,其他参数都必须一一罗列出来。
- bind(); 绑定传入this的值

3.1、apply()
    var count = function(a, b) {
        return a + b;
    };
    var fn1 = function(a, b) {
        return count.apply(this, arguments);
    };
    var fn2 = function(a, b) {
        return count.apply(this, [a, b]);
    };
    var fn3 = function() {
        return count.apply(this, [10, 10]);
    };
    console.log(fn1(10, 10));
    console.log(fn2(10, 10));
    console.log(fn3());
3.2、call()

call()方法与 apply()方法的作用相同,它们的区别仅在于接收参数的方式不同。

    var count = function(a, b) {
        return a + b;
    };
    var fn1 = function(a, b) {
        return count.call(this, a, b);
    };
    console.log(fn1(10, 10)); //20

事实上,传递参数并非 apply()和 call()真正的用武之地;它们真正强大的地方是能够**扩充函数
赖以运行的作用域**。如下:

color = "red"; //定义在全局中的变量
var obj = {color: "blue"};
var fn = function() {
    console.log(this.color);
};
fn(); //red
fn.call(obj); //blue

前两个都是显示地在全局作用域中调用函数的方式,因此结果返回“red”。而fn.call(obj)将执行环境指向了obj,因此返回“blue”。在前面的例子中,先将obj对象的foo指向fn,在通过obj来调用它的,而在这个例子中就简化了许多。

使用call()或apply()来扩充作用域的最大好处就是:对象不需要与方法有任何耦合关系

3.3、bind()

ECMAScript 5还定义了一个方法:bind()。这个方法会创建一个函数的实例,其 this 值会被绑
定到传给 bind()函数的值。

color = "red"; //定义在全局中的变量
var obj = {color: "blue"};
var fn = function() {
    console.log(this.color);
};
fn(); //red
var foo = fn.bind(obj);
foo(); //blue

//这种技巧的优点请参考第 22章。
支持 bind()方法的浏览器有 IE9+、Firefox 4+、Safari 5.1+、Opera 12+和 Chrome。

4、toString()、toLocaleString()和valueOf()方法

  • toString()
  • toLocaleString()
  • valueOf()
    始终返回函数方法体。返回代码的格式则因浏览器而异——有的返回的代码与源代码中的函数代码一样,而有的则返回函数代码的内部表示,即由解析器删除了注释并对某些代码作了改动后的代码。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值