笔记之javascript--07--Function
1、 Function类型是js中引用类型之一,每个函数实际上都是Function类型的实例对象,具有自己的属性和方法。正因为函数式对象,所以函数名实际上也是一个指向函数对象的指针。
2. 常用的函数定义方式
1. 函数声明:
1
2
3
|
function sum(a , b ){
return a+b;
} |
2. 表达式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
var sum = functiot (){
return a+b;
}; //注意分号
//两种方式的区别:
//解释器会率先读取函数声明,并使其在执行之前可以访问,
而使用表达式则必须等到解析器执行到它所在的代码行,才会真正被解释执行 (变量声明提前,而值留在原地). alert (sum (10 ,10)); function sum(a ,b){
return a+b;
} //↑上面的代码会正常执行,因为在代码执行前,解析器通过函数声明提升,读取并将函数声明添加到执行环境中,放到代码树的顶部 alert ( typeof sum);
alert(sum(10 , 10)); var sum = function (a ,b){
return a+b;
} //↑报错,原因在于函数位于一个初始化语句中,不是一个函数声明,不会被提前,而只会把var sum提前,用typeof操作符显示sum是undefined,所以报错 |
3. new 构造函数,虽然这种用法也是函数表达式,但该用法不推荐。因为这种语法会导致解析两次代码(第一次是解析常规的ECMAScript代码,第二次是解析传入构造函数中的字符串),影响性能。
使用 Function 构造函数,构造函数可以接受任意数量的参数,但最后一个参数始终都被看成是函数体,前面的参数则枚举出了新函数的参数。
var sum=new Function('num1','num2','return num1+num2;'); sum;// function anonymous(num1,num2 /**/) { return num1+num2; }
当使用不带圆括号的函数名是访问函数指针,而非调用函数。
3. 函数名仅仅保存指向函数对象的指针,因此函数名与包含对象指针的其他变量没什么不同,也就是说,一个函数对象可以有多个名字:
1
2
3
4
5
6
7
8
|
function sum(a , b ){
return a+b;
} console.log(sum(2 ,3)); //5
var anotherSum = sum; //变量anotherSum也指向了同一个函数对象
console.log(anotherSum(4 , 5)); //9
sum = null ; //sum变量不再保存函数对象的指针了
console.log(anotherSum(1 , 3)); //anotherSum这个变量仍能调用 4. JS为何没有重载这个概念。
|
4. JS为何没有重载这个概念。
1
2
3
4
5
6
7
8
|
function add(a){
return a+3 ;
} function add(a){
return a+5;
} var result = add(3); //8
//两个函数同名了,结果只能是后一个函数覆盖前一个,所以不能重载 |
不能实现重载的原因:
- ECMAScript函数没有签名,因为其参数是由包含零个或多个值的数组来表示的。没有函数签名,真正的重载是不可能做到的。在ECMAScript中定义两个名字相同的的函数,则该名字只属于后定义的函数。如何实现类似于Java中的重载呢,其实可以通过判断传入函数的参数类型和个数来做出不同响应。
function reload(){ if(arguments.length==0){ console.log('没传参'); }else if(arguments.legth==1){ console.log('传了一个参数'); } }
- 深入理解:将函数名想象为指针,也有助于理解为什么ECMAScript中没有函数重载的概念。
function add(){ return 100; } function add(num){ return num+200; } //实际上和下面代码没什么区别 function add(){ return 100; } add=function(num){ return num+200; }
5. 函数的内部属性:函数内部,有两个特殊的对象,arguments和this
1. arguments:
arguments是个类数组对象,包含着传入函数的所有参数,这个对象有一个叫callee的属性,属性值是一个指针,指向拥有这个arguments对象的函数本身
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function foo (){
var a =arguments.callee;
return a.toString();
} foo(); /* 返回结果: "function sum(){ var a =arguments.callee; return a.toString(); }" 也就是说,一个函数内部,arguments.callee指的就是这个函数本身。这个函数在递归调用时有点用,有许多缺陷,在ES5严格模式被移除 */ |
arguments :类数组对象,包含传入函数中所有参数。是每个函数自身的属性,之所以可以直接访问 arguments ,是因为命名空间??以下变化是为了加强JavaScript语言的安全性,这样第三方代码就不能在相同的环境下窥视其他代码了。
- callee 属性:是一个指针,指向拥有 arguments 对象的函数。严格模式访问会导致错误。
//一般阶乘函数 function factorial(num){ if(num<=1){ return 1;} else { return num*factorial(num-1); } }
定义阶乘函数用到递归算法,这样定义是没问题。
缺点:这个函数的执行与函数名 factorial 紧紧耦合在一起。万一出现改变函数指向的这种情况就不太好了,factorial=function(){} factorial(3);// undefiend
为了消除这种现象。
-
function factorial(num){ if(num<=1){ return 1; } else{ return num*arguments.callee(num-1); } }
这样无论引用函数使用的是什么名字都可以保证完成递归。
2. this:简单来说,this指的就是函数执行的环境对象,在哪个对象中执行,this就指哪个对象。
3. caller :不止是ECMAScript5中新增函数对象上的属性,还是 arguments 上的属性。保存着调用当前函数的函数的引用。如果是在全局作用域中调用当前函数,它的值为 null 。
Object.getOwnPropertyNames(Function);// ["length", "name", "arguments", "caller", "prototype"]
function outer(){ inner(); } function inner(){ console.log(inner.caller); //为了实现更松散的耦合,arguments.callee.caller } outer();// function outer(){ inner()}
严格模式下不能为函数的 caller 属性赋值,否则会导致出错。
4. length属性:表示函数希望接受的参数个数
1
2
3
4
|
function add(a ,b ,c){
return a+b+c;
} add.length; //3
|
5. 著名的prototype属性,简单来说,是一个对象,是通过调用构造函数而创建的一个对象,包含可以由特定类型的所有实例共享的属性和方法。
6.作为值的函数
因为ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用。不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回。
function callSomeFunction(someFunction,someArgument){ return someFunction(someArgument); } function concated(str){ return "Hi "+str; } callSomeFunction(concated,'xx');// 'Hi xx'
从一个函数中返回另一个函数的应用:假设有一个对象数组,想要根据某个对象属性对数组进行排序,但传给 sort() 方法的比较函数要接收两个参数,即要比较的值。我们需要一种方式来指明按照哪个属性来排序。我们可以定义一个函数它接收一个属性名,然后根据这个属性名来创建一个比较函数。默认情况下, sort 函数会调用每个对象的 toString() 方法以确定它们的次序。
function createCompare(property){ return function(obj1,obj2){ var value1=obj1[property], value2=obj2[property]; if(value1<value2) return -1; else if(value1>value2) return 1; else return 0; } }
var data=[{name:'aa',age:20},{name:'bb',age:12},{name:'cc',age:30}]; data.sort(createCompare("age"));// [{name:'bb',age:12},{name:'aa',age:20},{name:'bb',age:30}]
7.函数的属性和方法
-
length:表示函数希望接收的命名参数的个数(也就是定义的形参的个数)。
function sayName(name){ // } function sum(num1,num2){ // } function sayHi(){ // } sayName.length;// 1 sum.length;// 2 sayHi.length;// 0
- prototype:对于ECMAScript中的引用类型而言,prototype是保存它们所有实例方法的真正所在。诸如toString和valueOf等方法实际上都保存在Object.prototype名下(原生构造函数比如Function,Array等 在自己原型上重写了toString)。在ECMAScript5中,prototype属性是不可枚举的,因此使用for-in无法发现。 Object.getOwnPropertyDescriptor(Function,'prototype');// Object {writable: false, enumerable: false, configurable: false}
- 每个函数上有两个可用的方法:apply和call。这两个方法实际上是在Function.prototype上, Object.getOwnPropertyNames(Function.prototype);// ["length", "name", "arguments", "caller", "apply", "bind", "call", "toString", "constructor"] 它是在JavaScript引擎内部实现的。因为是属于Function.prototype,所以每个Function的实例都可以用(自定义的函数也是Function的实例)。都是在特定的作用域或自定义的上下文中调用执行函数,实际上等于设置函数体内 this 对象的值。
- apply :参数一为在其中运行函数的作用域,参数二为参数数组(可以是数组,也可以是 arguments 对象)。
function sum(num1,num2){ return num1+num2; } function callSum1(num1,num2){ return sum.apply(this,arguments);//sum.apply(this,[num1,num2]) } callSum1(10,30);// 40
严格模式下,未指定环境对象而调用函数, this 值不会转型为 window 。除非明确把函数添加到某个对象或者调用 apply 或 call ,否则 this 值将是 undefined
- call :参数一没有变化,变化的是其余参数都是直接传递给函数,参数必须都列出来。
function callSum1(num1,num2){ retrun sum.call(this,num1,num2); } callSum1(10,30);// 40
call 和 apply 真正强大的地方是能够扩充函数赖以运行的作用域,改变函数的执行环境。
传递参数并调用函数并非call()和apply()的用武之地,二者真正强大的地方是扩充函数运行的作用域
-
1234567891011
var
color =
'red'
;
var
obj = {
color :
'blue'
}
function
foo(){
console.log(
this
.color);
}
foo();
//'red'
foo.call(
this
);
//'red'
foo.call(obj);
//'blue'
//最后一次调用foo()函数的执行环境变了,其中的this指向了obj对象,
所以是'blue'
使用call()和apply()扩充作用域的最大好处,就是使对象与方法之间解耦
-
bind :ECMAScript5定义的方法,也是 Function.prototype 上的方法。用于控制函数的执行上下文,返回一个新函数,这个函数的 this 值会被绑定到传给 bind() 函数中的值。
window.color="red"; var o={color:'blue'}; function sayColor(){ console.log(this.color); } var newobj=sayColor.bind(o); newobj;// function sayColor(){ console.log(this.color); } newobj==sayColor;// false newobj();// blue
深入理解:可以将函数绑定到指定环境的函数。接收一个函数和一个环境,返回在给定环境中调用给定函数的函数。
function bind(func,context){ return function(){ func.apply(context,arguments);//这里创建了一个闭包,arguments使用的返回的函数的,而不是bind的 } }
当调用返回的函数时,它会在给定环境中执行被传入的函数并给出所有参数。
function bind(func,context,args){ return function(){ func.call(context,args); }; }
- toString,toLocaleString :返回函数代码的字符串形式,返回格式因浏览器而异,有的返回源码,有的返回函数代码的内部表示,由于存在差异,用这个也实现不了什么功能。
- valueOf :返回函数的自身引用。