javascript 面向对象精要 第二章 函数

第二章 函数

引言:函数就是对象。函数不同于其他对象的决定性特点是函数存在一个被称为[[call]]的内部属性内部属性无法通过代码访问而是定义了代码执行时的行为。[[call]]属性是函数独有的,表明该对象可以被执行。理解函数的行为是理解javascript 的核心。

2.1 声明还是表达式

函数有两种字面形式。第一种:函数声明

function add(num1,num2){
      return num1+num2;
}

第二种:函数表达式(这种函数被称为匿名函数)

var add=function(num1,num2){
      return num1+num2;
};
两种形式的区别:函数声明会被提升至上下文。这意味着你可以先使用函数后声明它们。

原因:函数声明会被提升,这是因为引擎提前知道了函数的名字。函数表达式只能通过变量引用,因此无法提升。

2.2 函数就是值

可以像使用对象一样使用函数,也可以将他们赋给变量,在对象中添加他们,将他们当成参数传给别的函数,或从别的函数中返回。

function sayHi(){
    console.log("hi");
}
sayHi();       //outputs "hi"
var sayHi2=sayHi;
sayHi2();      //outputs "hi"

这段代码首先有一个函数声明。然后有一个变量sayHi2被创建,并且被赋予sayHi的值。sayHi和sayHi2现在指向同一个函数。两者都可以执行,并且具有相同结果。

只要你记住函数就是对象,很多行为就变得容易理解了。

例如,你可以将函数当成参数传递给其他的参数。javascript的数组的sort()方法,接受一个比较函数作为可选参数。每当数组中有两个值需要进行比较时都会调用比较函数。如果第一个值小于第二个,比较值返回负数。如果第一个值大于第二个,比较值返回正数。如果相等,返回0。

在默认情况下,sort将数组中每个对象转换为字符串,然后进行比较。这意味着你无法在不指定比较函数的情况下为数字的数组精确排序。例子如下:

var numbers=[1,5,8,4,7,10,2,6];
number.sort(function(first,second){
       return first-second;
});
console.log(numbers);     //"[1,2,4,5,6,78,10]"
number.sort();
console.log(numbers);     //"[1,10,2,4,5,6,7,8]"
在本例中,被传给sort()的比较函数其实是一个函数表达式。第二次输出结果是因为默认的sort函数将所有值都转换为字符串进行比较。

2.3 参数

可以给javascript 函数传递任意数量的参数而不造成错误。那是因为函数参数实际上被保存在一个被称为arguments的类似数组的对象中。arguments可以自由增长来包含任意个数的值,这些值可以通过数字索引来引用。arguments的length属性可以告诉你目前有多少个值。arguments对象自动存在于函数中。

下面是一个简单的使用arguments和函数的期望参数个数的例子。实际传入的参数的数量不影响函数的期望参数个数。

function reflect(value){
     return value;
}
console.log(reflect("hi")); //"hi"
console.log(reflect("hi",25)); //"hi"
console.log(reflect.length); //1
reflect=functio(){
     return arguments[0];
}
console.log(reflect("hi")); //"hi"
console.log(reflect("hi",25)); //"hi"
console.log(reflect.length); //0

本例中先定义了一个具有单一命名参数的reflect()函数,但是两个参数传递给它时并,没有任何错误。由于只有一个命名参数,所以length属性为1。之后重新定义reflect()为无命名参数的函数,它返回传入的第一个参数arguments[0]。但它的length为0。

假设你想创建一个函数接受任意数量的参数并返回他们的和。因为你不知道有多少个参数,所以无法使用命名函数,在这种情况下,使用arguments最好。举例如下:

function sum(){
     var result =0,
     i=0,
     len=arguments.length;
     while(i<len){
       result+=arguments[i];
        i++;
      }
      return result;
}
console.log(sum(1,2)); //3
console.log(sum(50)); //50
console.log(sum()); //0

sum()函数接收任意数量的参数,并在while循环中遍历他们的值来求和。由于result的初始值为0,该函数就算没有参数,也可以正常工作。

2.4 重载

javascript 函数可以接受任意数量的参数且参数类型完全没有限制,javascript 函数不存在重载。

function say(message){
      console.log(message);
}
function say(){
      console.log("default");
}
say("hello");      //"default"

在javascript里,当你试图定义多个重名的函数时,只有最后的定义有效,之前的函数声明被完全删除。

function say(message){
     if (arguments.length===0){
         message="default";
     }
     console.log(message);
}
say("hello");    //hello

本例中,say()函数的行为视传入参数的个数而定。

2.5 对象方法

在下面代码中,变量person被赋予了一个对象的字面形式,包含属性和方法sayname.

var person={
  name:"hui",
  sayName:function(){
    console.log(person.name);
   }
};
person.sayName();  //"hui"

2.5.1 this 对象

sayName()方法直接引用了person.name在方法和对象之间建立了紧耦合。上例中应该改person.name为this.name。

javascript所有的函数作用域内都有一个this对象代表调用函数的对象。在全局作用域,this代表全局对象(浏览器里的window)。当一个函数作为对象的方法被调用时,默认this的值等于那个对象。

function sayNameForAll(){
     console.log(this.name);
}
var person1={
      name:"hui",
      sayName: sayNameForAll
};
var person2={
      name:"xia",
      sayName: sayNameForAll
};
var name="mm";
person1.sayName();
person2.sayName();
sayNameForAll();

运行结果如下:

本例中先定义函数,sayNameForAll.然后以字面形式创建两个对象以sayNameForAll函数作为sayName方法。当person1调用sayName时,输出“hui".当person2调用sayName时,输出“xia".最后定义了全局变量name.,所以当直接调用sayNameForAll是输出是"mm".

2.5.2 改变this

记住函数是对象,而对象有方法,所以函数也有。

(1)call()方法

它以指定的this值和参数来执行函数。call()的第一个参数指定了函数执行时的this,其后所有的参数都是需要被传入函数的参数。

function sayNameForAll(label){
       console.log(label+":"+this.name);
}
var person1={
       name:"hui"
};
var person2={
       name:"xia"
};
var name="mm";
sayNameForAll.call(this,"global");
sayNameForAll.call(person1,"person1");
sayNameForAll.call(person2,"person2");

运行结果如下:

由于使用了call()方法,你不需要将函数加入每个对象。因为你显式指定了this而不是让javascript引擎自动指定。

(2)apply()方法

apply()的工作方式和call()方法完全一样,但它只接受两个参数,this和一个数组或者类似数组的对象,内含需要被传入函数的参数(也就是说你可以把arguments对象作为apply()的第二个参数)。

function sayNameForAll(label){
       console.log(label+":"+this.name);
}
var person1={
       name:"hui"
};
var person2={
       name:"xia"
};
var name="mm";
sayNameForAll.call(this,["global"]);
sayNameForAll.call(person1,["person1"]);
sayNameForAll.call(person2,["person2"]);

运行结果和上例一样。

(3)bind()方法

ECMAScript5中新增的这个方法。bind()方法的第一个参数为this对象的值,第二个参数可选,可以不写,也可以写传入函数的参数值。

下面例子演示了使用bind的例子。创建了sayNameForPerson1()函数,并将person1绑定为其this对象的值。然后创建了sayNameForPerson2()函数,并将person2绑定为其this对象的值,person2绑定为其第一个参数。

function sayNameForAll(label){
       console.log(label+":"+this.name);
}
var person1={
       name:"hui"
};
var person2={
       name:"xia"
};
var sayNameForperson1=sayNameForAll.bind(person1);
sayNameForperson1("person1");

var sayNameForperson2=sayNameForAll.bind(person2,"person2");
sayNameForperson2();

person2.sayName=sayNameForperson1;
person2.sayName("person2");

sayNameForPerson1()函数没有绑定参数,所以你仍需传入lable参数用于输出。sayNameForPerson2()函数不仅绑定this为person2,同时也绑定了第一个参数为"person2".这意味着你可以调用sayNameForPerson2()函数而不传入任何额外参数。

运行结果如下:


例子最后将sayNameForPerson1()设置为person2sayName方法。由于其this值已经绑定,所以虽然sayNameForPerson1()现在是person2 的方法。它仍然输出person1.name的值。

完  等待第三章更新哦吐舌头

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值