javascript函数&函数执行环境

一:javascript函数

javascript的Function也是一种对象,它的作用有三个:

1:普通的逻辑代码容器

2:对象的方法

3:构造函数

创建函数也有三种方法:

1:function语句定义函数(逻辑代码容器,计算两个数的和)。也称定义式;

function fun(x,y){

  alert("arguments"+arguments[0]+"  "+arguments[1]);

  return x+y;

}

2:使用Function构造函数定义函数;

var fun = new Function("x","y",'alert("arguments"+arguments[0]+"  "+arguments[1]);return x+y;');

3:在表达式中定义函数,也称声明式;

var fun = function(x,y){

 return x+y;

}

三种定义函数是有区别的,主要是Function()构造函数可以动态定义和指定,而function语句只能预编译函数。因此每次调用Function构造函数定义的函数时,javascript都会去编译一次该函数。所以,如果一个函数经常被用到,就应该避免使用Function构造函数来定义函数。声明式和定义式还有稍微的区别。

如果采用定义式:

function fun(){ 

    return 1; 

var a =fun();

alert(a); 

function fun(){ 

    return 2; 

var b =fun(); 

alert(b)

得到结果为:

2

2

而如果用声明式:

var fun= function(){ 

    return 1; 

var a =fun();

alert(a); 

var fun= function(){ 

    return 2; 

var b =fun(); 

alert(b)

得到结果为

1

2

这种差别是由于JavaScript解释引擎的工作机制所导致的。JavaScript解释引擎在执行任何函数调用之前,首先会在全局作用域中注册以定义式创建的函数,然后再依次执行函数调用。由于注册函数时,后定义的函数重写了先定义的函数,因此无论调用语句位于何处,执行的都是后定义的函数。相反,对于声明式创建的函数,JavaScript解释引擎会像对待任何声明的变量一样,等到执行调用该变量的代码时才会对变量求值。由于JavaScript代码是从上到下顺序执行的,因此当执行第一个fun()调用时,fun函数的代码就是首先定义代码;而当执行第二个example()调用时,example函数的代码又变成了后来定义的代码。 

二:函数的执行环境

JavaScript在解析代码时,会为声明或定义的函数指定调用对象。所谓调用对象,就是函数的执行环境。如果函数体内有以关键字this声明的变量,则this引用的就是调用对象。事实上,在普通的函数中,也存在调用对象,只不过这个调用对象是默认的全局window对象而已。例如

var fun = function(){

alert(this==window);

}

fun();

结果为true。全局作用域下定义或声明的函数的调用对象就是window。

在以下情况下会改变函数的执行环境

1 对象的方法调用其他的对象方法,如:

var dog   = {}; 

dog.name  = "heibao"; 

dog.age   = "3 months"; 

dog.shout = function() { 

  return "Hello, My name is " + this.name + " and I am " + this.age + " old!"; 

dog.shout(); // “Hello, My name is heibao and I am 3 months old!” 

有意思的是,对象也可以借用其他对象的方法: 

var cat   = {}; 

cat.name  = "xiaohua"; 

cat.age   = "2 years"; 

cat.greet = dog.shout; 

cat.greet();          //“Hello, My name is xiaohua and I am 2 years old!” 

2 使用函数对象的call和apply方法,还可以动态指定函数或方法的调用对象:

dog.shout.call(cat);  //“Hello, My name is xiaohua and I am 2 years old!” 

或者 

dog.shout.apply(cat); //“Hello, My name is xiaohua and I am 2 years old!” 

3 函数作为构造函数使用

JavaScript是通过构造函数来模拟面向对象语言中的类的。例如: 

function Animal(sort, character) { 

   this.sort = sort; 

   this.character = character; 

以Animal作为构造函数,就可以像下面这样创建一个新对象: 

var dog = new Animal("mammal", "four legs"); 

创建dog的对象的过程如下:首先,new运算符创建一个空对象({}),然后以这个空对象为调用对象调用函数Animal,为这个空对象添加两个属性sort和character,接着,再将这个空对象的默认constructor属性修改为构造函数的名称(即Animal;空对象创建时默认的constructor属性值是Object),并且将空对象的__proto__属性设置为指向Animal.prototype——这就是所谓的对象初始化。最后,返回初始化完毕的对象。这里将返回的新对象赋值给了变量dog。 

dog.sort;        // mammal 

dog.character;   // four legs 

dog.constructor; // Animal 

聪明的读者结合前面介绍的内容,可能会认为使用new运算符调用构造函数创建对象的过程也可以像下面这样来实现: 

var dog = {}; 

Animal.call(dog, "mammal", "four legs"); 

表面上看,这两行代码与var dog = new Animal("mammal", "four legs");是等价的,其实却不是。虽然通过指定函数的执行环境能够部分达到初始化对象的目的,例如空对象dog确实获得了sort和character这两个属性: 

dog.sort;        // mammal 

dog.character;   // four legs 

dog.constructor; // Object —— 注意,没有修改dog对象默认的constructor属性 

但是,最关键的是新创建的dog对象失去了通过Animal.prototype属性继承其他对象的能力。只要与前面采用new运算符调用构造函数创建对象的过程对比一下,就会发现,new运算符在初始化新对象期间,除了为新对象添加显式声明的属性外,还会对新对象进行了一番“暗箱操作”——即将新对象的constructor属性重写为Animal,将新对象的__proto__属性设置为指向Animal.prototype。虽然手工“初始化对象”也可以将dog.constructor重写为Animal,但根据ECMA262规范,对象的__proto__属性对开发人员是只读的,对它的设置只能在通过new运算符创建对象时由JavaScript解释引擎替我们完成。 

JavaScript是基于原型继承的,如果不能正确设置对象的__proto__属性,那么就意味着默认的继承机制会失效: 

Animal.prototype.greet = "Hi, good lucky!"; 

dog.greet; // undefined 

事实上,在Firefox中,__proto__属性也是可写的: 

Animal.prototype.greet = "Hi, good lucky!"; 

dog.__proto__ = Animal.prototype; 

dog.greet; // Hi, good lucky! 

但这样做只能在Firefox中行得通。考虑到在兼容多浏览器,必须依赖于new运算符,才能实现基于原型的继承。 

原文地址:

相关推荐