- 為什么要使用this
- this 的使用誤解 (
2 種 指向誤解
) - this 和詞法作用域的比較
- this 的指向正解
- this 的綁定方法(
call()、apply()、bind()
)
this 總是返回一個對象( fun 也是一個對象 ) + this 指向總是善變的
this 的三篇文章按照順序復習
this基本概念 -> this綁定規則 -> this 應用實例
基本概念
this 總是返回一個對象 即包含當前屬性或者方法函數的對象
, 因為對象的屬性和方法函數可以賦予另一個對象,所有 this 指向的對象也是善變的。
function foo () {
console.log('name :' + this.name);
}
var obj = { name: "obj1", foo: foo };
var obj2 = { name: "obj2", foo: foo };
var obj3 = { name: "obj3" };
obj.foo() // obj1
obj2.foo() // obj2
- 為什么 使用this
-
this 提供了一種優雅的方式來
傳遞
一個對象的引用, 使用 this 能夠得到更加 簡介的 api.顯式的傳遞對象的引用(eg: 函數參數傳遞) 使得代碼變得混亂和難以維護(eg: 傳遞參數的個數就是個問題)。
使用 this 可以
自動引用合適的上下文環境
,并且確定this當前指代的對象
進而使用其屬性和方法函數
** 我們使用 this 的最終目的是保證能夠
正確 + 便捷
的使用 對象的屬性和方法函數處理數據 **
-
this 的使用誤解 ( this 的指向誤解 )
this 并不指向函數自身
-
this 不一定指向定義它的函數作用域或者定義時的作用域
function foo() { var a = 2; this.bar = function() { console.log(this.a) } this.bar(); } foo();
-
this 和詞法作用域的比較
this 可以通過
參數傳遞
或者直接的對象引用
來代替。** 但是 this 因為有其更加靈活的特性才被大家所使用,總是使用詞法作用域容易讓你 回到編程的舒適區 **
我們來記錄 foo 函數被調用的次數
function foo(num) {
console.log('foo ' + num);
// 記錄 foo 函數被調用的次數
this.count++;
}
foo.count = 0;
for(var i = 0; i < 5; i++) {
foo(i);
}
// 記錄 foo 函數被調用的次數
console.log(foo.count);
分析一下上面的 輸出 的值 和 this 的指向?
若需要你來改造 你應該怎么辦??
-
this 的指向正解 ( 請看 this 的 四種綁定策略 )
函數內部的this 總是指向 函數本身被調用的位置。(
this 綁定規則有詳解
)所以對于 this 的 指向就是 尋找函數的調用位置為首。(瀏覽器自帶的 開發者工具可以很方便的查看函數調用棧)
this 的 三種
強制綁定
的 方法詳解
** javascript 提供了 call apply 和 bind 三個方法,用于固定 this 的指向 **
** call 和 apply bind 都是定義在 Function 上面的方法。**
** call apply 返回一個調用函數的執行結果。 **
** bind 返回一個修改了內部this的 包裝函數。 **
-
call 的 傳參解析 ( 4 種傳參的方式 )
call 參數使用正常的對象
call 參數為空、null、undefined 則默認傳入全局對象
var n = 'bar'; var obj = {n: 'foo'} function baz() { console.log(this.n) } baz.call(obj); // foo baz.call(window); baz.call(); baz.call(null), baz.call(undefined);
call 傳入一個基本類型的數據,這個基本類型的數據會被轉為包裝對象 賦值給 this
var f = function () { console.log(this); }; f.call(5) // Number {[[PrimitiveValue]]: 5}
call 可以傳遞多個參數,第一個參數為需要綁定的對象,后面的參數 依次是函數調用的傳參參數
apply ( 接受一個數組作為函數執行時的參數 )
-
bind 將函數體內部的 this 強制綁定,返回一個新的經過包裝的函數
// 自己實現的簡單功能的 bind 函數 function bind(fun, obj) { return function() { // 這個 就是 bind 返回的函數 fn.apply(obj); } }
apply call bind 使用全攻略 ( 4種使用場景 )
-
傳遞空對象 ** 參數柯里化 **( 看你傳遞的對象到底有多空 )
call apply bind 傳遞一個空對象常常用于參數的柯里化
function bar(a, b) { console.log(a + ',' + b); }
直接使用 null / undefined 作為空 傳入
var baz = bar.call(null, 1, 4) // 1, 4 var bak = bar.bind(null, 10); bar(19) // 10,19
傳遞進去一個 真空對象 var ? = Object.create(null);
var baz = bar.call(?, 1, 4) // 1, 4
-
自定義的兩種 bind 方法
-
創建自定義的 bind 方法函數
function bindCopy(fn, obj) { return function() { fn.apply(obj, arguments); // 這里主要依賴 call 方法 ** 注意不能少了函數參數 ** } }
-
擴展函數方法庫(** 重要知識點 **)
Function.prototype.bindCopy() { var fn = this; var obj = arguments[0]; var args = Array.prototype.slice(arguments, 1); return function() { fn.apply(obj, args) } }
重要知識點:
函數使用 prototype 可以進行擴展( Function.ptototype.bindCopy )
使用 apply 可以將 類數組對象( 有 length 屬性的對象 ) 進行參數結構
函數自身調用時也是使用 this 表示調用的上下文
注意 原函數本身的 參數不能少
-
-
apply 和 call 的妙用
-
將數組解構 (上面是將類數組對象解構)
var arr = [1, 4, 6, 7]; Math.max.apply(null, arr); // 7
-
使用 call 方法用在 對象繼承中 重新調用被子類覆蓋的父類方法;
function Parent() { this.a = 'super'; this.Super = function() { console.log('父類的 Super 方法' + this. a); } } var p = new Parent(); function Children (a) { this.a = a; Parent.call(this); // 調用 父類的構造函數 } var c = new Children(12); c.Super() // 使用子類 繼承過來的方法 c.Super.call(p) // 使用 call 重新調用父對象的方法
試著自己實現一個類似 于 forEach 的 方法吧
-
.