js中的apply()跟call()执行域转换详解
js中的apply()和call()执行域转换详解
从以上实例可以看出B.call(this)这句使得函数A继承了B(B中的show()函数不在B的prototype中,但为什么b.show()可以访问不得而知,望高手赐教),而且a.show()输出A说明B.call(this)使得show函数执行时的作用域变成了对象a,所以此时访问的是对象a中prototype的param。
上述实例中我们不再在prototype对象中定义param变量,而在function A()中用this.param='A'代替,此时a.show()也能输出'A',而A.show()却输出了'undefined',这是为什么呢?首先通过之前的学习可以确定,JS中的函数是一个对象,函数名是一个函数对象的引用,而且JS中的对象和JAVA中的对象一样,同样有类和实例对象之说。于是笔者在此大胆猜测,函数里面定义的变量和prototype中定义的变量需要经过new出的对象才能直接引用,看之前我转载的一篇文章( JS中prototype详解 )中有介绍,称为“对象变量\方法”和“原型变量\方法”,与之相似的还有一个“类变量\方法”。我们先掌握这个,对后面理解有好处。
上述例子中A()中去掉了this.param='A',此时a.show()输出undefined,于是可以推测出B.call(this)不会将prototype中定义的原型变量“复制”到实例变量a,如果变量不存在则输出undefined,如果方法不存在则报错,但是B()中定义的对象变量\方法则可以“复制”到a变量中。注意,此处的“复制”还真是把b中的变量和方法复制到了a变量中,具体读者可用console.log()在浏览器控制台中看看。
js中函数其实是一个对象,这两个方法都是针对函数对象的,其目的是转换当前函数的执行环境,也就是函数中this所指的对象。
我们首先来看第一个实例:
function A(){ B.call(this); } function B(){ this.show = function(){ alert(this.param); } } A.prototype = { param : "A" } B.prototype = { param : "B" } var a = new A(); a.show(); //输出A var b = new B();<span id="transmark"></span> b.show(); //输出B
从以上实例可以看出B.call(this)这句使得函数A继承了B(B中的show()函数不在B的prototype中,但为什么b.show()可以访问不得而知,望高手赐教),而且a.show()输出A说明B.call(this)使得show函数执行时的作用域变成了对象a,所以此时访问的是对象a中prototype的param。
下面我们看第二个实例:
function A(){ this.param = 'A'; B.call(this); } function B(){ this.show = function(){ alert(this.param); } } B.prototype = { param : "B" } var a = new A(); a.show(); //输出A var b = new B(); b.show(); //输出B alert(A.param); //输出undefined
上述实例中我们不再在prototype对象中定义param变量,而在function A()中用this.param='A'代替,此时a.show()也能输出'A',而A.show()却输出了'undefined',这是为什么呢?首先通过之前的学习可以确定,JS中的函数是一个对象,函数名是一个函数对象的引用,而且JS中的对象和JAVA中的对象一样,同样有类和实例对象之说。于是笔者在此大胆猜测,函数里面定义的变量和prototype中定义的变量需要经过new出的对象才能直接引用,看之前我转载的一篇文章( JS中prototype详解 )中有介绍,称为“对象变量\方法”和“原型变量\方法”,与之相似的还有一个“类变量\方法”。我们先掌握这个,对后面理解有好处。
下面我们看第三个实例:
function A(){ B.call(this); } function B(){ this.newParam = 'newB'; this.show = function(){ alert(this.param); } } B.prototype = { param : "B" } var a = new A(); a.show(); //输出undefined alert(a.newParam); //输出newB var b = new B(); b.show(); //输出B alert(A.param); //输出undefined
上述例子中A()中去掉了this.param='A',此时a.show()输出undefined,于是可以推测出B.call(this)不会将prototype中定义的原型变量“复制”到实例变量a,如果变量不存在则输出undefined,如果方法不存在则报错,但是B()中定义的对象变量\方法则可以“复制”到a变量中。注意,此处的“复制”还真是把b中的变量和方法复制到了a变量中,具体读者可用console.log()在浏览器控制台中看看。
下面我们看第四个实例:
function A(){ this.param = 'newA'; } function B(){ this.newParam = 'newB'; this.show = function(){ alert(this.param); } } B.staticParam = 'staticB'; B.prototype = { param : "B" } A.param = 'staticA'; var a = new A(); B.call(A); A.show(); //输出staticA alert(A.newParam); //输出newB alert(A.staticParam); //输出undefined上述例子中我们可以看到B.call(A),此时因为A相当于类,所以将B中的对象方法\变量复制到了类A中,成为了A的类变量\方法,而alert(A.staticParam)输出undefined说明B的类变量不会复制到A中。
下面我们看第五个实例:
function A(){ this.param = 'newA'; } function B(){ this.newParam = 'newB'; this.show = function(){ alert(this.param); } } B.staticParam = 'staticB'; B.prototype = { param : "B" } A.param = 'staticA'; var a = new A(); b.call(A);上述例子报"Uncaught TypeError: b.call is not a function",可推测call()方法是类方法,实例对象无法调用,而apply()也是类方法,读者可自己测之。
好了,现在咱们来总结一下,JS中的call()和apply()方法是函数对象的类方法,方法的第一个参数表示当前需转换的域对象,可以为对象实例,也可以为类。而只有函数的对象变量\方法才能复制到新的执行域中,当执行域为类对象,则需用类对象来引用,否则需用实例对象引用。笔者在本篇文章中都是用的call()函数来举例,apply()其实和call()用法基本一样,后面笔者将会写一篇专门比较apply()和call()的文章。
版权声明:本文为博主原创文章,未经博主允许不得转载。