- 一 : 函数创建的三种方式
- 二 : 简介
- 三 : 函数的内部属性
- 四 : 没有重载功能
- 五 : 函数声明与函数表达式
- 六 : 作为值的函数
- 七 : 函数的属性和方法
- 八 : 函数参数个数问题
一 : 函数创建的三种方式
1. 函数声明方式
function box(){ (最常见)
alert("hello");
}
2. 函数表达式方式(不需要写函数名,最后有分号)
var h = function(){
alert('hello');
};
3. 构造函数方式(不推荐,会解析两次代码,影响性能。括号里边是函数体)
var box = new Funtion("num1","num2","return num1+num2");
二 : 简介
- 函数本身是对象,所以函数名是一个指向对象的指针,不会与某个函数绑定
- 由于函数名是指向函数的指针,因此函数名与包含对象指针的其他变量一样,也就是
一个函数可能有多个名字
举个栗子
function sum(n1,n2){
console.log(n1+n2);
}
var another = sum; // 他俩都指向实际的函数体,another和sum都是指针
another(10,20); // 30
sum(10,20); // 30
sum = null; // sum销毁,不指向函数体,而another还是指向的
another(20,30); // 50
sum(20,30); // TypeError,sum is not a function
示意图
通过函数可以封装任意多条语句
带参数和不带参数的函数都没有定义返回值,而是调用后直接执行的,实际上,任何函数都可以通过return语句跟后面的要返回的值来实现返回值。
function box(name,age){
alert(name+","+age); //undefined,undefined
alert(name); //undefined
alert(typeof(name)); //undefined
alert(typeof(age)); //undefined
}
box(); //假如不传递参数,表示不存在,返回undefined
// 有返回的函数
function box(){
return "hello javascript";
}
box(); //直接这样执行不会出现任何弹窗,因为这里return表示把这句话字符串返回回来,相当于box()="hello javascript"
alert(box()); //这里返回hello javascript
三 : 函数的内部属性
在函数的内部,有三个特殊的对象,arguments
,this
和caller
。
1. arguments对象是一个类数组对象(类似于数组,但是是一个对象),包含着传入函数中的所有参数,主要用途是保存函数参数。ECMAScript的函数不介意传递进来多少参数,也不会因为参数不统一而报错。他可以通过arguments对象来接收传递进来的参数,通过此对象可以获得动态的去传递参数,用length属性判断有多少参数
arguments对象有个callee属性
,该属性是一个指针,指向拥有这个arguments对象的函数,会影响浏览器的性能,目前此属性已经被废除,不建议使用。
扩展一点 : 类数组对象,就是类似于数组的对象,它的属性名是0,1,2,3....,而且有一个length属性,表示属性的个数
arguments和length的综合应用(返回多个参数相加减)
function box(){
var sum = 0;
for(var i=0; i
另一个实例
function box(num){
if(num<=1){
return 1;
}else{
return num*box(num-1);
}
}
alert(box(4));
这里,如果函数名改变,而函数体不变,就会导致错误
解决方案:
var factorial=(function f(n){
if(n<=1){
return 1;
}else{
return n*f(n-1);
}
});
var anotherF=factorial;
factorial=null;
console.log(anotherF(4));
目前,arguments已被抛弃,我们不要再使用它了。取而代之的是剩余参数(rest arguments),具体参见ES6文档
2. this对象——函数内部的另一特殊对象
this是函数调用语句所处的那个作用域
当在全局作用域中调用函数时,this对象引用的就是window
window是一个对象,而且是js里边最大的对象,是最外围的对象
alert(window); //[object Window] 表示全局
alert(typeof window); //object
alert(this); //[object Window]
alert(typeof this); //object 也就是说this就是window
var color = 'red'; //color是全局变量,而这个全局变量也是window的属性
alert(window.color); //red
alert(this.color); //red
window.color = "red";
alert(this.color); // red
var box = {
color:"blue", //这里的color是box下的属性,也就是局部变量
sayColor:function(){
alert(this.color); //这里的this,就是代表box
}
};
box.sayColor(); // blue
3. ES5的caller属性
在一个函数中调用另一个函数时,被调用的函数会自动生成一个caller属性,指向调用它的函数对象。如果该函数当前未被调用或者没有被其他函数调用,则caller为null。
结果:ƒ outer(){inner()}
在outer中调用inner,inner会自动生成一个caller属性,指向outer
四 : 没有重载功能
函数重载允许创建函数名称相同单数功能的输入输出类型不同的子程序,他可以简单的称为一个单独功能可以执行多项任务的能力
function box(a,b){
return a+b;
}
function box(a,b,c){
return a*b;
}
alert(box(2,3)); //6,
说明JS没有重载的功能,只能将名称相同的前一个函数覆盖掉,而且无关参数的有无和个数,只要函数名相同,就覆盖,参数一不一样无所谓
五 : 函数声明与函数表达式
解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问)
函数表达式,必须等到解析器执行到它所在的代码行,才会真正被解释执行
函数声明会被提升,函数表达式不会被提升
举个栗子
console.log(sum(1,2)); // 3,能返回正确结果
function sum(n1,n2){
return n1+n2;
}
原因 : 在代码开始执行之前,解析器已经通过函数声明提升(function declaration hoisting),读取并将函数声明添加到执行环境中。对代码求值时,JavaScript引擎在第一遍会声明函数并将他们放到源代码树的顶部。所以上面的代码会正确运行
console.log(sum(1,2)); // 出错
var sum = function(n1,n2){ // 这一行代码实际没有执行到
return n1+n2;
};
原因 : 函数位于一个初始化语句中,而不是一个函数声明。也就是在执行到函数所在的语句之前,sum不会保存有对函数的引用。而且,由于第一行出错,下面的sum也不会执行到。
六 : 作为值的函数
因为JavaScript中的函数名本身就是变量,所以函数可以作为值来使用。不仅可以像传递参数一样把一个函数传递给另一个函数,而且还可以将一个函数作为另一个函数的结果返回
把一个函数传递给另一个函数的栗子
function box(sum,num){
return sum+num;
}
function sum(num){
return num+10;
}
var result = box(sum(10),10); //这里传递的是函数的返回值,和普通的变量一样
alert(result); //30
七 : 函数的属性和方法
每个函数都包含两个属性 : length和prototype
length表示函数接受的命名参数的个数
function add(){
console.log(1+3);
}
console.log(add.length);// 0
function say(str){
console.log(str);
}
console.log(say.length);// 1
function box(name,age){
return name+age;
}
alert(box.length); // 2
prototype属性
,它用来保存所有实例方法的真正所在,也就是原型。诸如toString()方法,实际上都保存在prototype名下
prototype下的两个方法:apply(),call(),每个函数都包含这两个非继承而来的方法
这两个方法的用途都在特定的作用域中调用函数,实际上等于设置函数体内的this对象
function box(num1,num2){
return num1+num2;
}
function sum(num1,num2){
return box.apply(this,[num1,num2]); //this表示window作用域,[]表示传递的参数
return box.apply(this,arguments); //如果吧传递[],也可以传递一个类数组的arguments,效果相同
}
alert(sum(10,10)); //sum本身并没有相加的功能,但是他利用apply和call来冒充box函数进行相加
function box(num1,num2){
return num1+num2;
}
function sum(num2,num2){
return box.call(this,num2,num2); //call的唯一不同是传递的参数不同
}
alert(sum(10,10));
这里他们这样用并不大好,真正的使用地方时扩展函数赖以运行的作用域
通过冒充去改变作用域
var color = 'red'; //全局
var box = { //局部
color:'blue'
};
function sayColor(){
alert(this.color);
}
sayColor(); //red全局
//用call实现对象冒充,冒充box下,Window下
sayColor.call(window); //冒充Window,red
sayColor.call(this); //this就是window
sayColor.call(box); //冒充box,作用域就在box对象里边,blue
ES5新增的bind()方法:这个方法会创建一个函数的实例,其this值会绑定到传给bind()函数的值
window.color = 'red';
var o = {
color : 'blue'
}
function sayColor(){
console.log(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); // blue
八 : 函数参数个数问题
- arguments.length:实际传入函数中参数的个数
- 超出传入实参个数的index,与形参的值不共享,比如上边的c,我实际上并没有传入实际的c,所有arguments[2]就不共享
function foo(a,b,c){
// 参数个数,指的是实际传入的参数个数
console.log(arguments.length) // 2
// 指向拥有这个arguments对象的函数
console.log(arguments.callee === foo) // true
console.log(arguments[0] === a) // true
console.log(arguments[0]) // 10
arguments[0] = 11
console.log(arguments[0]) // 11
a = 12
console.log(arguments[0]) // 12
c = 40
console.log(arguments[2]) // undefined
arguments[2] = 44
console.log(c) // 40
// 超出传入实参个数的index与形参的值不共享,比如上边的c,我实际上并没有传入实际的c,所有arguments[2]就不共享
}
foo(10,20)
如果形参中有多个一样的值,那么它取的是最后一个值
function bar(a,b,c,b){
console.log(a)
console.log(b) // 这里打印的是最后一个b的值
console.log(c)
}
bar(1,2,3) // 1 undefined 3
这个例子中bar函数有两个形参b,那么打印的b实际上是最后那么b,由于调用函数时没有给最后的b赋值,所以为undefined
优先级问题
声明式函数优先级>函数形参的优先级>变量的优先级
function test(x){ // 形参
console.log(x)
var x = 20 // 变量
function x(){ } // 声明式函数
}
test(10) // f x(){}
在上边的例子中,形参,变量和声明式函数的名称重复了,所以按照优先级的顺序,先找声明式函数,再找形参的值,再找变量的值。