改變函數執行的上下文

參考閱讀:[url="http://www.sj63.com/html/201006/2010060812142929103_1.html"]js教程:javascript作用域(Scope)[/url]

JavaScript中[color=darkblue][b]this[/b][/color]代表的是運行時的對象,是正在調用代碼的對象

在瀏覽器環境中運行需要把println函數改成alert或者console.log

function gf() {println(this)}


運行這段代碼輸出:[object global]

下面這段代碼人爲改變了代碼執行的上下文,this也就代表着不同的對象
var obj1 = {count:1};
var obj2 = {count:6};

function foo(factor) {this.count * factor}

foo.call(obj1, 8); // 1 * 8
foo.call(obj2, 8); // 6 * 8


再看
Prototype的bind方法代碼
Function.prototype.bind = function() {
var __method = this, args = $A(arguments), object = args.shift(); // Important
return function() {
return __method.apply(object, args.concat($A(arguments)));
}
}

// 上面這種方法沒怎麼明白,把arguments轉換成Array,然後把執行函數綁定在第一個方法的參數上(把綁定的對象提取出來)
// 綁定參數又增加args.concat($A(arguments)),這多出來的幾行代碼是什麼意圖?
// 原來這樣是爲了
// 在bind函數執行的時候可以添加一次參數
// 然後在後期bind返回的函數執行的時候又可以添加一次參數
// 這樣在函數執行的上下文變化時還能“記憶”之前的參數,這就是JS的必包特性
// http://mengjiaoyao.blog.163.com/blog/static/298416192009113102149773/


// 將傳入的對象轉化成Array
var $A = Array.from = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) {
return iterable.toArray();
} else {
var results = [];
for (var i = 0, length = iterable.length; i < length; i++)
results.push(iterable[i]);
return results;
}
}


早期的Prototype中bind方法更簡單明瞭
Function.prototype.bind = function(object) {
var method = this; // Important
return function() {
method.apply(object, arguments);
}
}


改寫我們上面代碼
var foo1 = foo.bind(obj1);
foo1(8);


兩次傳參的調用代碼
var obj1 = {count:1};
var obj2 = {count:6};

function foo(a,b,c) {println(this.count * a * b * c)}


var foo1 = foo.bind(obj1, 2);
var foo2 = foo.bind(obj2, 3);
foo1(5,6);
js>60 // 1 * 2 * 5 * 6
foo2(7,8);
js>1008 // 6 * 3 * 7 * 8


這就是改變函數執行上下文+記憶不同上下文之間參數的結果


如果不想用prototype這種方式的寫法的話,可以參考如下代碼
var gbind = function(o, f) {
return function() {
return f.apply(o, arguments);
}
};

這種寫法如果需要支持更復雜的參數傳遞的話,想想該怎麼寫?

雖然看不懂,但每次想這個問題還得看一遍
[quote]A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression).[/quote]

以下純淡騰亂作,請蒙上眼睛
// another version xscript%#%live.com
// 實在沒有必要這麼寫
// bindAsEventListener()返回的函數必須至少帶一個參數,那就是event,否則在FF無法運行
Function.prototype.bindAsEventListener = function() {
var __method = this, args = $A(arguments), object = args.shift();
return function() {
var a = $A(arguments), e, evts;
var __e_m = "警告,您的瀏覽器必須指定第一個參數類型爲事件!";
// 如果後期傳入了參數
// alert(a.length); // IE和W3C輸出的不一樣,應該好好學學,理解
if(0 < a.length) {
e = a[0];
// alert(typeof e);
// alert(e.type);
// 如果是事件,判斷對象是否事件還有待改進
//
if("object" == typeof e && !!e.type && !!e.srcElement) {
a.shift();
evts = [e || window.event];
} else if(!window.event) { // 而且window.event都無法捕捉到
true == !!console.log ? console.log(__e_m) : alert(__e_m);
return false;
} else {
evts = [window.event];
}
} else {
if(!!window.event) {
evts = [window.event];
} else {
true == !!console.log ? console.log(__e_m) : alert(__e_m);
return false;
}
}
return __method.apply(object, evts.concat(args).concat(a));
}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章