JavaScript中如何深入理解this,相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。
在莘縣等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都網(wǎng)站制作、成都網(wǎng)站建設(shè) 網(wǎng)站設(shè)計(jì)制作定制制作,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),品牌網(wǎng)站制作,成都全網(wǎng)營(yíng)銷,成都外貿(mào)網(wǎng)站建設(shè)公司,莘縣網(wǎng)站建設(shè)費(fèi)用合理。
定義
this是函數(shù)運(yùn)行時(shí)自動(dòng)生成的內(nèi)部對(duì)象,即調(diào)用函數(shù)的那個(gè)對(duì)象。(不一定很準(zhǔn)確的定義,但還算通俗易懂) 在大多數(shù)情況下,this的值由函數(shù)調(diào)用方式?jīng)Q定,它不能在執(zhí)行期間賦值來設(shè)置,它在每次執(zhí)行下可能都有不同的值。
全局執(zhí)行環(huán)境(outside function)
在全局執(zhí)行環(huán)境中,this一直指向全局對(duì)象(global object),不管是在嚴(yán)格模式還是在非嚴(yán)格模式中。
代碼1
console.log(this.document === document); //true // 在瀏覽器中,window對(duì)象也就是全局對(duì)象(global object) console.log(this === window); //true this.a = 37; console.log(window.a); //37
函數(shù)執(zhí)行環(huán)境(inside function)
在函數(shù)執(zhí)行環(huán)境中,this的值取決于函數(shù)的調(diào)用方式。
而函數(shù)的調(diào)用方式主要有4種:
函數(shù)直接調(diào)用
對(duì)象方法調(diào)用
構(gòu)造函數(shù)調(diào)用
call / apply / bind
箭頭函數(shù)(ES6)
函數(shù)直接調(diào)用
下面的代碼在非嚴(yán)格模式執(zhí)行時(shí),this的值會(huì)指向全局對(duì)象;而在嚴(yán)格模式中,this的值將會(huì)默認(rèn)為undefined。
代碼2
/* 非嚴(yán)格模式 */ function f1 () { return this; } console.log(f1() === window); //true // in node; console.log(f1() === global); //true /* 嚴(yán)格模式 */ function f2 () { 'use strict' return this; } console.log(f1() === undefined); //true
call / apply / bind 改變this的指向
call / apply
call和apply的用法很像,只是后面參數(shù)的傳入形式不同。
代碼3
function add(c, d) { return this.a + this.b + c + d; } var o = {a: 1, b: 3}; // call的第一個(gè)參數(shù) 是對(duì)象,也就是this的指向?qū)ο?。后面的參?shù)就是函數(shù)arguments對(duì)象的成員 add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 // call的第一個(gè)參數(shù) 是對(duì)象,也就是this的指向?qū)ο蟆:竺娴膮?shù)是數(shù)組,數(shù)組里的成員也就是函數(shù)arguments對(duì)象成員 add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
使用call和apply時(shí)需要注意的是,當(dāng)傳入的第一個(gè)參數(shù)的值不是對(duì)象時(shí),JavaScript會(huì)嘗試使用ToObject 操作將其轉(zhuǎn)化為對(duì)象。
代碼4
function bar() { console.log(Object.prototype.toString.call(this)); } bar.call(7); // [object Number]
bind 方法
ECMAScript 5 引入了 Function.prototype.bind。調(diào)用f.bind(someObject)會(huì)創(chuàng)建一個(gè)與f具有相同函數(shù)體和作用域的函數(shù),但是在這個(gè)新函數(shù)中,this將永久地被綁定到了bind的第一個(gè)參數(shù),無論這個(gè)函數(shù)是如何被調(diào)用的。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
代碼5
function f() { return this.a; } var g = f.bind({a: 'azerty'}); //生成一個(gè)綁定函數(shù)g console.log(g()); // azerty var o = {a: 10, f: f, g: g}; console.log(o.f(), o.g()); //10, azerty //需要注意的是,綁定函數(shù)不可以再bind var h = g.bind({a: 'foo'}); console.log(h()); //azerty,不會(huì)變成foo
對(duì)象方法調(diào)用
當(dāng)以對(duì)象里的方法的方式調(diào)用函數(shù)時(shí),它們的 this 是調(diào)用該函數(shù)的對(duì)象.
下面的例子中,當(dāng) o.f() 被調(diào)用時(shí),函數(shù)內(nèi)的this將綁定到o對(duì)象。
var prop = 36; var o = { prop: 37, bar: function() { return this.prop; } }; console.log(o.bar()); //37
構(gòu)造函數(shù)調(diào)用
代碼6
function Person(name, age) { this.name = name; this.age = age; this.introduce = function () { console.log('My name is ' + this.name + ', I\'m ' + this.age); }; } var Joseph = new Person('Joseph', 19); Joseph.introduce(); // "My name is Joseph, I'm 19"
由上述代碼可以清晰的看到this與被新創(chuàng)建的對(duì)象綁定了。
注意:當(dāng)構(gòu)造器返回的默認(rèn)值是一個(gè)this引用的對(duì)象時(shí),可以手動(dòng)設(shè)置返回其他的對(duì)象,如果返回值不是一個(gè)對(duì)象,返回this。(這句話看起來比較難理解,我們看下一個(gè)例子)。
代碼7
function Fn2() { this.a = 9; // dead code return {a: 10}; } var o = new Fn2(); console.log(o.a); // 10
這個(gè)例子說明了當(dāng)構(gòu)造函數(shù)返回的是一個(gè)對(duì)象的話,此時(shí)this的值會(huì)變成此時(shí)返回的對(duì)象。‘this.a = 9’成了僵尸代碼。
箭頭函數(shù)
在箭頭函數(shù)( Arrow functions)中,this的值是封閉執(zhí)行環(huán)境決定的。在全局環(huán)境中,那么被賦值為全局對(duì)象。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
var globalObject = this; var foo = (() => this); console.log(foo() === globalObject); // true
更重要的是它與其他情況不同的是,不管函數(shù)如何調(diào)用,上面this的值一直都是全局對(duì)象。call / bind 也不能改變它的值。
代碼8
// 作為對(duì)象方法被調(diào)用 var obj = {foo: foo}; console.log(obj.foo() === globalObject); // true // 嘗試用 call 改變this的值 console.log(foo.call(obj) === globalObject); // true //this的值并未變成obj // 嘗試用 bind 改變this的值 foofoo = foo.bind(obj); console.log(foo() === globalObject); // true
案例
本文知識(shí)點(diǎn)都看完了。讓我們看幾個(gè)案例,檢查自己的掌握情況。
例1
var prop = 36; var o = { prop: 37, bar1: function() { function foo1() { return this.prop; } return foo1; }, bar2: function() { var foo2 = (() => this.prop); //ES6箭頭函數(shù) return foo2; } }; console.log('result1:'+o.bar1()()); // result1 ? console.log('result2:'+o.bar2()()); // result2 ? var fn2 = o.bar2; console.log('result3:'+fn2()()); // result3 ?
先揭曉答案:例1 result1 = 36,result2 = 37,result3 = 36。我的理解是,在result1中,o.bar1()執(zhí)行導(dǎo)致foo函數(shù)return到了全局環(huán)境中,然后執(zhí)行就變成了在全局中執(zhí)行,所以得到的是全局中36的值。result2呢?因?yàn)閠his在箭頭函數(shù)中。它的值不會(huì)改變。所以this仍指向o。那為什么result3又重新變了呢?因?yàn)榇藭r(shí)‘var fn2 = o.bar2’相當(dāng)于重新定義了一個(gè)函數(shù),而this的值當(dāng)然也就變?yōu)榱巳謱?duì)象。
// 相當(dāng)于這樣 var fn2 = function() { function foo1() { return this.prop; } return foo1; } fn2()();
例2
function sum(a,b) { return a+b; }; var o = { num: 1, fn: function() { function handle() { return this.num = sum(this.num, this.num); } handle(); } }; console.log('result:'+o.fn()); // result ?
同樣先揭曉答案:result = undefined,用控制臺(tái)可以看到此時(shí)this指向window,而不是o。這是個(gè)比較容易掉進(jìn)去的坑(一般認(rèn)為是當(dāng)初的語言設(shè)計(jì)錯(cuò)誤,被人詬病不少)。看似函數(shù)是由對(duì)象方法調(diào)用的,其實(shí)細(xì)心的話,我們可以看到。handle函數(shù)的執(zhí)行,前面的沒有對(duì)象的。這種情況下,this指向全局對(duì)象。解決辦法也很簡(jiǎn)單。
// 1、取消 handle函數(shù)的定義,直接在對(duì)象的方法中使用this fn2: function() { this.value = sum(this.value, this.value); //2 }, ///2、使用變量保存外部函數(shù)的this。 fn3: function() { var that = this; // that == o function handle() { that.value = add(that.value, that.value); } handle(); }
看完上述內(nèi)容,你們掌握J(rèn)avaScript中如何深入理解this的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!