相信大家都知道,函数的调用模式与进入函数执行上下文的this指针有着密不可分的关系。随着调用模式的不同,this指针的指向便有所不同。随着应用的复杂、代码的累积,对于函数作用域的分析真是个头痛的问题。因此,深入理解函数调用模式对函数作用域(this指针)的影响变得至关重要。
/** * 函数调用模式 * * 1>方法调用模式 * (1)当一个函数被保存为对象的一个属性时,称它为一个方法。 * (2)当一个调用表达式包含一个属性存取表达式(即一个.表达式或[subscript]下标表达式), * 那么它被当做一个方法来调用。 * (3)当一个方法被调用时,this被绑定到该对象。可以使用this去访问对象,所以它能从对象中取值或修改对象。 * this到对象的绑定发生在调用的时候。 * 2>函数调用模式 * (1)当函数以此模式调用时,this被绑定到全局对象。这是语言设计上的一个错误。倘若语言设计正确, * 当内部函数被调用时,this应该仍然绑定到外部函数的this变量, * 这个设计错误的后果是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值, * 所以不能共享该方法对对象的访问权。 * (2)解决方案:如果该方法定义一个变量(如that)并给它赋值为this,那么内部就可以通过那个变量访问到this。 * 3>构造器调用模式 * (1)如果在一个函数前面带上new来调用,那么将创建一个隐藏连接到该函数的prototype成员的新对象, * 同时this将会被绑定到那个新对象上。 * (2)我们通常以大写格式来命名构造器函数。 * 4>Apply、Call调用模式 * (1)apply(thisObj, argsArray) * 第一个是将被绑定给this的值,第二个是参数数组。 * (2)call(thisObj, arg1, arg2, ...) * 第一个是将被绑定给this的值,之后为参数列表枚举。 * * 我们需要深入区分前两者调用模式,深入理解汤姆大叔的博客便知其区别了—— * 深入理解JavaScript系列(13):This? Yes,this! */
1 //分析 2 (function ($) { 3 4 window.color = 'red'; 5 6 /** 7 * 1>this与上下文中可执行代码的类型有直接关系,this值在进入上下文时确定,并且在上下文运行期间永久不变。 8 * 2>this值的首要特点(或许是最主要的)是它不是静态的绑定到一个函数。 9 * 3>this值的确定与引用类型的值相关,其通用规则如下: 10 * 在一个函数上下文中,this由调用者提供,由调用函数的方式来决定。 11 * 如果调用括号()的左边是引用类型的值,this将设为引用类型值的base对象(base object), 12 * 在其他情况下(与引用类型不同的任何其它属性),这个值为null。 13 */ 14 var myapp = { 15 color: 'green', 16 paint: function (node) { 17 node.style.color = this.color; 18 } 19 }; 20 21 var findNodes = function (callback) { 22 23 var nodes = $('div'); 24 for (var i = 0, len = nodes.length; i < len; i += 1) { 25 //此时经过中间值计算后,callback的base是global,因此调用callback后的this指向global 26 callback(nodes[i]); 27 } 28 } 29 //findNodes的调用模式为函数调用模式 30 findNodes(myapp.paint); 31 32 //myapp.paint()的调用模式为方法调用模式 33 myapp.paint($('div').get(0)); 34 35 })(jQuery); 36 37 //改造 38 (function ($) { 39 40 var myapp = { 41 color: 'green', 42 paint: function (node) { 43 node.style.color = this.color; 44 } 45 }; 46 47 var findNodes = function (callback_obj, callback) { 48 var nodes = $('div'); 49 for (var i = 0, len = nodes.length; i < len; i += 1) { 50 if (typeof callback === 'function') { 51 //改造的方式无非是改变回调函数的作用域 52 callback.call(callback_obj, nodes[i]); 53 } 54 if (typeof callback === 'string') { 55 callback_obj[callback](nodes[i]); 56 } 57 } 58 }; 59 //我们指定在myapp对象的作用域下,执行myapp.paint函数 60 //传递一个对象和一个方法 61 findNodes(myapp, myapp.paint); 62 //传递一个对象和方法名称字符串 63 findNodes(myapp, 'paint'); 64 65 })(jQuery);
/**
* 本篇随笔参考资料:
*
* 《JavaScript语言精粹》
* 第4章:函数
* 《JavaScript模式》
* 第4章:函数
* 《深入理解JavaScript系列——汤姆大叔的博客》
* 深入理解JavaScript系列(13):This? Yes,this!
*/
深入理解JavaScript系列的篇篇文章都是精华,集合了很多JavaScript书籍的核心思想,建议博友细细推敲。
以下列举本人已经读过、加以深入理解并部分应用于实践的文章——
深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点
深入理解JavaScript系列(2):揭秘命名函数表达式
深入理解JavaScript系列(3):全面解析Module模式
深入理解JavaScript系列(4):立即调用的函数表达式
深入理解JavaScript系列(5):强大的原型和原型链
深入理解JavaScript系列(9):根本没有“JSON对象”这回事!
深入理解JavaScript系列(10):JavaScript核心(晋级高手必读篇)
深入理解JavaScript系列(11):执行上下文(Execution Contexts)
深入理解JavaScript系列(12):变量对象(Variable Object)
深入理解JavaScript系列(13):This? Yes, this!
深入理解JavaScript系列(14):作用域链(Scope Chain)
深入理解JavaScript系列(15):函数(Functions)
深入理解JavaScript系列(16):闭包(Closures)
很惭愧,看的并不是很多,还有不少东西要学,我一直信奉一个学习理念——
抓住一个点,深究下去;
抓住一个点,扩展下去!
还有很多可以研究的知识,还有很多可以整合的知识。加油吧,给自己打打气!