JavaScript为函数对象定义了两个方法:apply和call,它们的作用都是将函数绑定到另
外一个对象上去运行,两者仅在定义参数的方式有所区别:
Function.prototype.apply(thisArg,argArray);
Function.prototype.call(thisArg[,arg1[,arg2…]]);
从函数原型可以看到,第一个参数都被取名为thisArg,也就是说,所有函数内部的this
指针都会被赋值为thisArg,这就达到了将函数作为另外一个对象的方法运行的目的。两个
方法除了thisArg 参数,都是为Function 对象传递的参数。
<script type="text/javascript"> //定义一个函数func1,具有属性p和方法A function func1() { this.p = "func1-"; this.A = function (arg) { alert(this.p + arg); } } //定义一个函数func2,具有属性p和方法B function func2() { this.p = "func2-"; this.B = function (arg) { alert(this.p + arg); } } var obj1 = new func1(); var obj2 = new func2(); obj1.A("byA"); //显示func1-byA obj2.B("byB"); //显示func2-byB obj1.A.apply(obj2, ["byA"]); //显示func2-byA,其中[“byA”]是仅有一个元素的数组,下同 obj2.B.apply(obj1, ["byB"]); //显示func1-byB obj1.A.call(obj2, "byA"); //显示func2-byA obj2.B.call(obj1, "byB"); //显示func1-byB </script>
可以看出,obj1的方法A被绑定到obj2运行后,整个函数A的运行环境就转移到了obj2,
即this指针指向了obj2。同样obj2 的函数B也可以绑定到obj1 对象去运行。代码的最后4
行显示了apply和call函数参数形式的区别。
与 arguments 的length属性不同,函数对象的还有一个参数相关的属性length,它表示
函数定义时所指定参数的个数,而非调用时实际传递的参数个数。例如下面的代码将显示2:
function sum(a,b){ return a+b; } alert(sum.length);
call,apply都是Function的原型的方法,其他function继承
this
和传统意义的面向对象的语言不同,JavaScript 中的this 指针是一个动态的变量,一个
方法内的this指针并不是始终指向定义该方法的对象的,在上一节讲函数的apply和call方
法时已经有过这样的例子。
<script type="text/javascript"> <!-- //创建两个空对象 var obj1 = new Object(); var obj2 = new Object(); //给两个对象都添加属性p,并分别等于1和2 obj1.p = 1; obj2.p = 2; //给obj1添加方法,用于显示p的值 obj1.getP = function () { alert(this.p); //表面上this指针指向的是obj1 } //调用obj1的getP方法 obj1.getP(); //使obj2的getP方法等于obj1的getP方法 obj2.getP = obj1.getP; //调用obj2的getP方法 obj2.getP(); //--> </script>
从代码的执行结果看,分别弹出对话框1 和2。由此可见,getP函数仅定义了一次,在
不同的场合运行,显示了不同的运行结果,这是有this指针的变化所决定的。在obj1 的getP
方法中,this就指向了obj1 对象,而在obj2 的getP 方法中,this就指向了obj2 对象,并通
过this指针引用到了两个对象都具有的属性p。
由此可见,JavaScript 中的this 指针是一个动态变化的变量,它表明了当前运行该函数
的对象。由this指针的性质,也可以更好的理解JavaScript中对象的本质:一个对象就是由
一个或多个属性(方法)组成的集合。每个集合元素不是仅能属于一个集合,而是可以动态
的属于多个集合。这样,一个方法(集合元素)由谁调用,this指针就指向谁。实际上,前
面介绍的apply方法和call方法都是通过强制改变this指针的值来实现的,使this指针指向
参数所指定的对象,从而达到将一个对象的方法作为另一个对象的方法运行的效果。
this的范围
var user={ name:"jack", //定义了name属性,初始化为jack favoriteColor:["red","green","black","white"],//定义了颜色喜好数组 hello:function(){ //定义了方法hello alert("hello,"+this.name); }, sex:"male" //定义了性别属性sex,初始化为sex } user.hello(); //"hello,jack"
var user={ name:"jack", //定义了name属性,初始化为jack favoriteColor:["red","green","black","white"],//定义了颜色喜好数组 hello:function(){ //定义了方法hello alert("hello,"+name); }, sex:"male" //定义了性别属性sex,初始化为sex } user.hello(); //"hello,"
name="hongda"; var user={ name:"jack", //定义了name属性,初始化为jack favoriteColor:["red","green","black","white"],//定义了颜色喜好数组 hello:function(){ //定义了方法hello alert("hello,"+name); }, sex:"male" //定义了性别属性sex,初始化为sex } user.hello(); //"hello,hongda"
name = "hongda"; var user = { name: "jack", //定义了name属性,初始化为jack favoriteColor: ["red", "green", "black", "white"], //定义了颜色喜好数组 hello: function () { //定义了方法hello name = "hongdadada"; alert("hello," + name); }, sex: "male" //定义了性别属性sex,初始化为sex } user.hello(); //"hello,hongdadada"
一个对象内部的方法调用该对象的属性时要指明this,该属性不是某个范围内的变量。
function fun() { name = "hongdada"; function show() { //没有意义 alert(name); } } var f = new fun(); console.dir(f);
function fun() { this.name = "hongdada"; this.show=function show() { alert(name); } } var f = new fun(); console.dir(f);
function中没有this时,它就是个方法,只有有this时它才类似一个类
传递给函数的隐含参数:arguments
当进行函数调用时,除了指定的参数外,还创建一个隐含的对象——arguments。
arguments是一个类似数组但不是数组的对象,说它类似是因为它具有数组一样的访问性质,
可以用arguments[index]这样的语法取值,拥有数组长度属性length。arguments 对象存储的
是实际传递给函数的参数,而不只局限于函数声明所定义的参数列表,
function func(a,b){ alert(a); alert(b); for(var i=0;i<arguments.length;i++){ alert(arguments[i]); } } func(1,2,3); //1,2,1,2,3
代码:
<script type="text/javascript"> function dwn(s) { document.write(s + "<br/>"); } //定义一个多边形(Polygon)类型 function Polygon() { //存放多边形的顶点 var m_points = []; m_points = Array.apply(m_points, arguments); //用上面介绍的那种方式定义getter function GETTER() { }; GETTER.prototype = m_points[0]; this.firstPoint = new GETTER(); //公有属性 this.length = { valueOf: function () { return m_points.length }, toString: function () { return m_points.length } } //添加一个或多个顶点 this.add = function () { m_points.push.apply(m_points, arguments); } //取得序号为idx 的顶点 this.getPoint = function (idx) { return m_points[idx]; } //设置特定需要的顶点 this.setPoint = function (idx, point) { if (m_points[idx] == null) { m_points[idx] = point; } else { m_points[idx].x = point.x; m_points[idx].y = point.y; } } } //构造一个三角形p var p = new Polygon({ x: 1, y: 2 }, { x: 2, y: 4 }, { x: 2, y: 6 }); dwn(p.length); //3 dwn(p.firstPoint.x); //1 dwn(p.firstPoint.y); //2 p.firstPoint.x = 100; //不小心写了它的值 dwn(p.getPoint(0).x); //1 不会影响到实际的私有成员 delete p.firstPoint.x; //恢复 dwn(p.firstPoint.x); //1 p.setPoint(0, { x: 3, y: 4 }); //通过setter 改写了实际的私有成员 dwn(p.firstPoint.x); //3 getter 的值发生了改变 dwn(p.getPoint(0).x); //3 </script>
应用例子:
function dwn(s) { document.write(s + "<br/>"); } function App() { var fun={}; var arr = Array.apply(null, arguments); var arr2 = Array.apply(arr2, arguments); var arr3 = Array.apply(fun, arguments); var arr4 = Array.apply(window, arguments); dwn(arr); dwn(arr2); dwn(arr3); dwn(arr4); } App(1, 2, 3, 4, 5, 6, 7, 8);
Array这个function方法对象初始化中没有使用this,所有thisArg不管填什么都没影响。
function Class1() { this.name = "class1"; this.showNam = function () { alert(this.name); } } function Class2() { this.name = "class2"; } var c1 = new Class1(); var c2 = new Class2(); c1.showNam.call(c2); //class2
call也可以形成继承
function Class1() { this.showTxt = function (txt) { alert(txt); } } function Class2() { Class1.call(this); } var c2 = new Class2(); c2.showTxt("cc"); //cc
class2继承class1
多继承
function Class1() { this.showTxt1 = function (txt) { alert("Class1:"+txt); } } function Class0() { this.showTxt0 = function (txt) { alert("Class0:"+txt); } } function Class2() { Class0.call(this); Class1.call(this); } var c2 = new Class2(); c2.showTxt0("cc"); c2.showTxt1("cccc");
在javascript框架prototype里就使用apply来创建一个定义类的模式
var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } }
解析:从代码看,该对象仅包含一个方法:Create,其返回一个函数,即类。但这也同时是类的
构造函数,其中调用initialize,而这个方法是在类创建时定义的初始化函数。通过如此途径,
就可以实现prototype中的类创建模式
var vehicle=Class.create(); vehicle.prototype = { initialize: function (type) { this.type = type; }, showSelf: function () { alert("this vehicle is " + this.type); } } var moto=new vehicle("Moto"); moto.showSelf();