引用类型之Function类型

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script>
Function类型:在ECMAScript中,函数实际上是对象.每个函数都是Function类型的实例,而却都与其它引用类型一样,都有属性和方法.
由于函数是对象,因此函数名也是一个指向函数对象的指针.
/**
*
* 定义函数的3中方式(创建函数的3种方式)

**/
1.函数声明的方式
function sum (num1,num2) {
return num1+num2;
}
2.函数表达式(又叫函数字面量)
var sum = function(num1,num2) {
return num1 + num2;
};
3.Function构造函数
   var sum = new Function('num1','num2','return num1+num2'); 
    不推荐使用构造函数创建函数,因为这种方式会导致解析两次代码(一次是解析常规的ECMAScript代码,第二次是解析传入构造函数的字符串).从而影响性能
    
/***
* 1.函数可以有多个名字
*/
由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其它变量没什么不同,换句话说,函数可以有多个名字.
function sum (num1,num2) {
return num1 + num2
}
alert(sum(10,10));  //20;

var anotherSum = sum;
alert(anotnerSum(10,10)); //20

sum=null;
alert(anotnerSum(10,10)); //20

/***
* 没有重载
* 重载: 在同一个类中,方法的名字相同,但参数个数,参数类型,或返回值类型不同
* 重写: 它是子类和父类的关系,子类重写了父类的方法,但方法名,参数类型,参数个数必须相同
*/
function addSomeNumber(num){
return num + 100;
}

function addSomeNumber(num){
return num + 200;
}

var result = addSomeNumber(100); //结果为300
声明了两个同名的函数,结果前面一个被后面的给覆盖了.以上代码实际上与下面的代码没区别.

var addSomeNumber = function(num){
return num + 100;
}

addSomeNumber = function(num){
return num +200;
}

var result = addSomeNumber(100); //结果为300   在创建第二个函数的时候,实际上覆盖了引用第一个函数的变量addSomeNumber

/**

*函数声明与函数表达式的区别

* 除了什么时候可以通过变量访问函数这一点区别之外,函数声明与函数表达式的语法其实是等价的。
**/
实际上,解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁.解析器会率先读取函数声明,并使其在执行任何代码之前可用(可以访问).至于函数表达式,
则必须等到解析器执行到它所在的代码行,才会被真正的解释执行. (函数声明提升);

alert(sum(10,10));
function sum(num1,num2){
return num1 + num2;
}
以上代码可以正确的执行,因为在代码执行之前,解析器已经通过一个名为函数声明提升的过程,读取,并将函数添加到执行环境中.对代码求值时,javaScript引擎在第一遍会声明函数,
并将它放到源代码树的顶部.所以,即使声明函数的代码放在调用它的代码之后,javascript引擎也能把函数声明提升到顶部.

sum(10,10);  //、执行错误
var sum = function (num1,num2){
return num1+ num2
};

如果把函数声明改为等价的函数表达式,就会在执行期间出错.

/**
*作为值的函数

* */
因为ECMASCript中函数名本省就是变量,所以函数也可以当做值来使用. 1.做函数的参数  2.作为另一个函数的返回结果

function callSomeFunction(someFunction,someArgument){
return someFunction(someArgument);
}

function add10(num){
return num + 10;
}

要访问函数的指针而不执行函数的话,必须去掉函数名后面的那对圆括号
var  result = callSomeFunction(add10 , 10);
alert(result); //10       


将 对象数组按照name属性排序  
var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}];
data.sort(createComparisonFunction("name"));
alert(data[0].name); //Nicholas
data.sort(createComparisonFunction("age"));
alert(data[0].name); //Zachary


function createComparisonFunction(propertyName) {
return function(object1, object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
};
}

/***

* 函数内部属性 arguments和this

* */
在函数内部有两个特殊的对象:arguments 和 this. arguments 是一个类数组对象,包含着传入函数的所有参数,虽然 arguments 的主要作用是保存函数参数,
但这个对象还有一个叫callee的属性,该属性是一个指针,指向拥有 arguments 对象的函数(callee的作用:解耦)

阶层函数:
function factorial(num){
if(num <=1){
return 1;
}else{
return num* factorial(num -1);
}
}

函数可以正常执行,但是问题在于这个函数的执行与函数名 factorial紧紧的耦合在一起,我们可以利用  callee 解耦

function factorial(num){
if(num < 1){
return 1;
}else{
return num * arguments.callee(num-1);
}
}

这样就可以保证 无论引用函数时使用的是什么名子,都可以保证正常完成递归调用.
var trueFactorial = factorial;
factorial = function(){
return 0;
};
alert(trueFactorial(5)); //120
alert(factorial(5));   //0

1.案例1
function factorial(num){
if(num < 1){
return 1;
}else{
return num * arguments.callee(num-1);
}
}

var trueFactorial = factorial;  //我的理解是  这行代码重新定义了一个函数 trueFactorial 说以trueFactorial的toString方法会返上面的那个函数
factorial = function(){
return 0;
};

alert(trueFactorial(5));  //120
alert(factorial(5));  //0

2.案例2
function factorial(num){
if(num < 1){
return 1;
}else{
return num * arguments.callee(num-1);
}
}

factorial = function(){
return 0;
};

var trueFactorial = factorial;  
alert(trueFactorial(5));  //0
alert(factorial(5));  //0

3.案例3
function factorial(num){
if(num < 1){
return 1;
}else{
return factorial * factorial(num-1);
}
}
var trueFactorial = factorial;

factorial = function(){
return 0;
};

alert(trueFactorial(5)); // NaN
alert(factorial(5));    // 0

4.案例4
function factorial(num){
if(num < 1){
return 1;
}else{
return factorial * factorial(num-1);
}
}

factorial = function(){
return 0;
};

var trueFactorial = factorial;
alert(trueFactorial(5));  // 0
alert(factorial(5));      // 0

// this  引用的是函数执行环境的对象,(当在网页的全局作用域中调用函数时,this引用的就是window)

/**

*函数对象属性 caller

**/
ECMAScript5也规范了另外一个函数对象的属性:caller.
caller : 这个属性保存着调用当前函数的函数的引用,如果在全局作用域中调用当前函数,它的值为null

function outer(){
inner();
}

function inner(){
alert(inner().caller);
}
outer();

弹框显示outer函数的源码,因为outer调用了inner,所以inner.caller指向outer();
为了实现更松散的耦合,也可以通过 arguments.callee.caller来访问相同的信息

function outer(){
inner();
}

function inner(){
alert(arguments.callee.caller);
}

outer();

注意:  1.在严格模式下  arguments.callee会报错  
 2.在严格模式下 arguments.caller 会报错    在非严格模式下 为 undefined,ECMAscript定义 arguments.caller 是为了区分和函数的caller属性
 
/**

* 函数内部属性和方法
* 属性:length,prototype
* 方法:call,apply

* **/
length: 该属性表示函数的参数
prototype: 我们创建的每一个函数都有一个prototype(原型)属性,该属性是一个指针,指向一个对象,这个对象包含着特定类型的所有实例共享的属性和方法.
使用原型的好处是,可以让所有实例共享它包含的属性和方法.换句话说就是不必在构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中.

function Person(){}

Person.prototype.name = 'Nichollas';
Person.prototype.age = 29;
Person.prototype.job = 'Software Enginner';
Person.prototype.sayName = function(){
alert(this.name);
}

var person1 = new Person();
person1.sayName(); //Nicholas"

var person2 = new Person();
person2.sayName(); // Nicholas"

alert(person1.sayName() == person2.sayName()); //true

注: 对于ECMAScript5中的引用类型而言,prototype是保存它们所有实例方法的真正所在.换句话说,诸如 toString()和valueOf()等方法,实际上都保存在prototype名下
,只不过是通过各自对象的实例访问罢了.在创建自定义引用类型以及实现. prototype属性是不可枚举的,因此使用 for-in 无法发现.

// call apply方法: 
每个函数都包含两个非继承而来的方法: call和apply.  作用 : 在特定的作用域中调用函数,实际上等于设置函数体内this对象的值(扩充函数赖以运行的作用域)

apply方法: 接受两个参数,一个是在其中运行函数的作用域,另一个是参数数组. 第二个参数可以是 Array 的实例,也可以是  arguments 对象

function sum (num1, num2){
return sum1 + sum2;
}

function callSum1(num1,num2){
sum.apply(this,[num1,num2])  //传入数组
}

function callSum2(num1,num2){
sum.apply(this,arguments)  //传入 arguments 对象
}

alert(callSum1(10,10)); //20
alert(callSum2(10,10)); // 20

call方法: call方法的作用与apply相同,区别在于接受参数的方式不同,第一个是this值没变化,变化的是其余的参数都直接传递给函数(一一列举出来)

function sum(num1 ,num2){
return sum1+sum2;
}

function callSum(num1,num2){
return sum.call(this,num1,num2);
}

alert(callSum(10,10));  // 20

事实上传递参数并非call()和apply()真正的用武之地,他们强大的地方在于能够扩充函数赖以运行的作用域.

window.color= 'red';
var o ={'color':'blue'};

function sayColor(){
aletr(this.color);
}

sayColor() ; // red
sayColor.call(this) //red
sayColor.call(window) // this
sayColor.call(o);  // blue

// ECMASCript5 还定义了一个方法bind()
bind()方法会创建一个函数的实例,其this值会被绑定到传给bind函数的值.

window.color = 'red';
var color = 'blue';

function sayColor(){
alert(this.color);
}

var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue

在这里,sayColor()调用bind()并传入对象 o,创建了objectSayColor函数. objectSayColor()函数的this值就等于 o,因此即使在全局函数作用域中
调用这个函数 ,也会看到'blue'
</script>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值