JavaScript的this
Javasctipt 的 this
函式的this關鍵字在JavaScript中的行為與其他語言稍有不同。嚴格模態與非嚴格模態也有一定的區別。在大多數情況下,這個值由函式的呼叫方式決定。它不能在定義期間通過賦值來確定,而且每次呼叫函式時它可能是不同的。ES5引入了bind()方法來設定函式this的值,不管它是如何呼叫的,而ES2015引入了箭頭函式,它不提供自己的this繫結(它保留了所包含的詞法上下文的這個值)。
一、在全域性環境中的this
在瀏覽器環境中
- 直接輸出this
console.log(this); // Window console.log(this === window); // true console.log(this === Window); // false window instanceof Window // true
上述結果說明直接輸出this時,輸出的是它的建構函式,但它其實是一個例項;
'use strict' console.log(this); // undefined
嚴格模式下,全域性的this指向undefined
在node環境中
- 直接輸出this
console.log(this); // {}
嚴格和非嚴格模式都是 {}
二、函式中的this
直接在全域性環境中呼叫,而不是作為某個物件的屬性或方法
function fn() { return this; } let obj = { a: 1, b: fn } fn() // node: global , browser: window let objFn = obj.b; objFn(); // node: global , browser: window // 箭頭函式 let obj2 = { a: 2, b: () => this } let obj2Fn = obj2.b; obj2Fn(); // node: global , browser: window // 立即執行函式 !function(){ console.log(this === window) // true }() let obj = { say: function() { console.log(this === window) // true }() } obj.say; let str = 'windows' let o = { str: 'o', methods: { str: 'methods', fn: function () { console.log(this.str) }, arrowFn: function() { // IIFE let fn = () => { console.log(this.str) } return fn; }() } } o.methods.arrowFn(); // undefined ;此時,this指window;而用let宣告的str變數不會新增到window物件上去,所以為undefined;
在非嚴格模式下
function fn() { 'use strict' return this; } fn() // undefined
作為物件的屬性呼叫
function fn() { return this; } let obj1 = { a: 1, b: fn } let obj2 = { a: 2 } obj2.b = obj1.b; obj1.b(); // {a: 1, b: ƒn} obj2.b(); // {a: 2, b: ƒn} // 箭頭函式 let obj1 = { a: 1, b: () => this } let obj2 = { a: 2 } obj2.b = obj1.b; obj1.b(); // node: global , browser: window obj2.b(); // node: global , browser: window
通過call 和 apply 方法呼叫
如果函式在其主體中使用this關鍵字,則可以使用call()或apply()方法將其值繫結到呼叫的特定物件
function add(c, d) { return this.a + this.b + c + d; } var o = {a: 1, b: 3}; add.call(o, 5, 7); // 16 add.apply(o, [10, 20]); // 34 // function bar() { console.log(Object.prototype.toString.call(this)); } bar.call(7);// [object Number] bar.call('foo'); // [object String]
通過 bind 方法來繫結this
function fn() { return this.a; } let k = fn.bind(null); k(); // undefined //此時this === window let g = fn.bind({a: 1}); g(); // 1 let m = fn.bind({a: 2}); m(); // 2 let h = g.bind({a: 3}); // bind只會繫結一次 h(); // 1 // 正常返回值 let obj = { a: 1, say: (function() { let _say = function() { console.log(this.a); } return _say; })() } obj.say(); // 1 // bind obj var obj = { say: (function() { let _say = function() { console.log(this === window); } return _say.bind(obj); })() } obj.say(); // true // bind的簡單實現 Function.prototype.myBind = function(context){ self = this;//儲存this,即呼叫bind方法的目標函式 return function(){ return self.apply(context,arguments); }; }; let g1 = fn.myBind({a: 1}) g1(); // 1 let h1 = g1.myBind({a: 3}) h1(); // 報錯,
原理
this指的是函式執行時所在的環境,執行上下文
JavaScript 將物件儲存在記憶體中,會將儲存的地址賦給我們所申明的變數;
var o = {a: 5}; //記憶體中會這樣儲存 o => a => { //foo: { //[[value]]: 5 //[[writable]]: true //[[enumerable]]: true //[[configurable]]: true //} //} var f = function () {} var m = {a: f} // 當a的值是一個函式的時候,就會變成以下形式: //記憶體中會這樣儲存 o => a => { //foo: { //[[value]]: f的地址 //[[writable]]: true //[[enumerable]]: true //[[configurable]]: true //} //}
this的作用
由上面可以看出,由於函式單獨儲存,可以單獨執行,執行在不同的上下文中。但要有一個機制來表示它,this就是這個作用,能夠指向當前函式的執行環境;
箭頭函式原理的思考
箭頭函式又和上文說的內容不相符。其實仔細想想,它是個語法糖,等同於給函式運用了bind方法,繫結函式的父作用域執行環境
let str = 'windows' let o = { str: 'o', methods: { str: 'methods', fn: function () { console.log(this.str) }, arrowFn: () => { console.log(this.str) } } } let str = 'windows' let o = { str: 'o', methods: { str: 'methods', fn: function () { console.log(this.str) }, arrowFn: function() { let fn = () => { console.log(this.str) } return fn; } } } o.methods.fn(); // methods o.methods.arrowFn()(); // methods;