只有掌握了JavaScript中的this操做符你纔算正式邁入JavaScript這門語言的門檻!咱們一直都在用這個框架,那個框架,但每每忽視掉了js最基礎的東西,筆者認爲這些基礎每每纔是走下去,走深,最不可或缺的東西.那咱們就一塊兒來學習一下js中神奇的this吧--------筆者查看了大量有關this的文章,有些是按照本身思路寫的,有些是直接引用其餘做者成熟的思路的文章javascript
學習一個知識首先要理解他的字面含義,經過翻譯咱們知道,this的含義是這,這個(指較近的人或事物)的意思。那麼咱們結合現實,咱們說的「這」在不一樣的環境所指的事物是不同的。那麼在JavaScript中this在不一樣的環境調用所表達的含義也是很是豐富的。若是你以爲JavaScript中的this和其餘面嚮對象語言Java同樣,是指存儲在實例屬性中的值,那你就大錯特錯了。JavaScript中的this有着在這門語言中不可或缺魔力。java
宿主(環境)解釋
JS的運行環境通常由宿主環境和執行期環境共同構成,宿主環境是由外殼程序(如web瀏覽器就是一個外殼程序)生成,執行期環境是由嵌入到外殼程序中的JS引擎(/JS解釋器)生成的,在執行期環境JS能夠生成內置靜態對象、初始化執行環境等。node
對於JavaScript,宿主環境最多見的是web瀏覽器,瀏覽器提供了一個JavaScript運行的環境,這個環境裏面,須要提供一些接口,好讓JavaScript引擎可以和宿主環境對接。JavaScript引擎纔是真正執行JavaScript代碼的地方,常見的引擎有V8(目前最快JavaScript引擎、Google生產)、JavaScript coregit
可是環境不是惟一的,也就是JavaScript不只僅可以在瀏覽器裏面跑,也能在其餘提供了宿主環境的程序裏面跑,最多見的就是nodejs。一樣做爲一個宿主環境,nodejs也有本身的JavaScript引擎--V8。根據官方的定義: Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applicationsgithub
<script>
console.log(this === window) //true
var a = 3;
console.log(this.a, window.a)//3 3
</script>
複製代碼
說明:在瀏覽器中,window對象同時也是全局對象web
> this === global
true
複製代碼
index.js 文件在node環境中執行
console.log(this) //Object {}
console.log(this === global); //false
複製代碼
index.js 文件在node環境中執行
var foo = "bar";
console.log(this.foo);//undefined
複製代碼
> var foo = "bar";
> this.foo
bar
> global.foo
bar
複製代碼
index.js 文件在node環境中執行
foo = "bar";
console.log(this.foo);//undefined
console.log(global.foo);//bar
複製代碼
上面的幾種種狀況可能你們已經繞暈了,總結起來就是:在瀏覽器裏面this是老大,它等價於window對象,若是你聲明一些全局變量(無論在任何地方),這些變量都會做爲this的屬性。在node裏面,有兩種執行JavaScript代碼的方式,一種是直接執行寫好的JavaScript文件,另一種是直接在裏面執行一行行代碼。對於直接運行一行行JavaScript代碼的方式,global纔是老大,this和它是等價的。在這種狀況下,和瀏覽器比較類似,也就是聲明一些全局變量會自動添加給老大global,順帶也會添加給this。可是在node裏面直接腳本文件就不同了,你聲明的全局變量不會自動添加到this,可是會添加到global對象。因此相同點是,在全局範圍內,全局變量終究是屬於老大的。編程
說明:在函數內部,this的值取決於函數被調用的方式瀏覽器
<script>
testa()
function testa(){
testb()
function testb(){
console.log(this === window)//true
}
}
</script>
複製代碼
index.js 文件在node環境中執行
foo = "bar";
function testThis () {
this.foo = "foo";
}
console.log(global.foo);//bar
testThis();
console.log(global.foo);//foo
複製代碼
說明:由於上面代碼不在嚴格模式下,且this的值不是由該調用設置的,因此this的值默認指向全局對象。閉包
<script type="text/javascript">
foo = "bar";
function testThis() {
"use strict";
this.foo = "foo";
}
console.log(this.foo); //logs "bar"
testThis(); //Uncaught TypeError: Cannot set property 'foo' of undefined
</script>
複製代碼
說明:然而,在嚴格模式下,this將保持他進入執行環境時的值,因此下面的this將會默認爲undefined,因此,在嚴格模式下,若是 this 沒有被執行環境(execution context)定義,那它將保持爲 undefined。app
<script type="text/javascript">
foo = "bar";
function testThis() {
this.foo = "foo";
}
console.log(this.foo); //logs "bar"
new testThis();
console.log(this.foo); //logs "bar"
console.log(new testThis().foo); //logs "foo"
</script>
複製代碼
函數裏面的this其實相對比較好理解,若是咱們在一個函數裏面使用this,須要注意的就是咱們調用函數的方式,若是是正常的方式調用函數,this指代全局的this,若是咱們加一個new,這個函數就變成了一個構造函數,咱們就建立了一個實例,this指代這個實例,這個和其餘面向對象的語言很像。另外,寫JavaScript很常作的一件事就是綁定事件處理程序,也就是諸如button.addEventListener(‘click’, fn, false)之類的,若是在fn裏面須要使用this,this指代事件處理程序對應的對象,也就是button。
<script type="text/javascript">
// 將一個對象做爲call和apply的第一個參數,this會被綁定到這個對象。
var obj = {a: 'Custom'};
// 這個屬性是在global對象定義的。
var a = 'Global';
function whatsThis(arg) {
return this.a; // this的值取決於函數的調用方式
}
whatsThis(); // 'Global'
whatsThis.call(obj); // 'Custom'
whatsThis.apply(obj); // 'Custom'
</script>
複製代碼
說明:使用 call 和 apply 函數的時候要注意,若是傳遞給 this 的值不是一個對象,JavaScript 會嘗試使用內部 ToObject 操做將其轉換爲對象。所以,若是傳遞的值是一個原始值好比 7 或 'foo',那麼就會使用相關構造函數將它轉換爲對象,因此原始值 7 會被轉換爲對象,像 new Number(7) 這樣,而字符串 'foo' 轉化成 new String('foo') 這樣,例如:下面代碼
<script type="text/javascript">
function bar() {
console.log(Object.prototype.toString.call(this));
}
//原始值 7 被隱式轉換爲對象
bar.call(7); // [object Number]
</script>
複製代碼
<script type="text/javascript">
function f(){
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); // azerty
var h = g.bind({a:'yoo'}); // bind只生效一次!
console.log(h()); // azerty
var o = {a:37, f:f, g:g, h:h};
console.log(o.f(), o.g(), o.h()); // 37, azerty, azerty
</script>
複製代碼
<script type="text/javascript">
function f(){
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); // azerty
var h = g.bind({a:'yoo'}); // bind只生效一次!
console.log(h()); // azerty
var o = {a:37, f:f, g:g, h:h};
console.log(o.f(), o.g(), o.h()); // 37, azerty, azerty
</script>
複製代碼
<script type="text/javascript">
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true
</script>
複製代碼
<script type="text/javascript">
function Thing() {
console.log(this.foo);
}
Thing.prototype.foo = "bar";
var thing = new Thing(); //logs "bar"
console.log(thing.foo); //logs "bar"
</script>
複製代碼
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
Thing.prototype.setFoo = function (newFoo) {
this.foo = newFoo;
}
var thing1 = new Thing();
var thing2 = new Thing();
thing1.logFoo(); //logs "bar"
thing2.logFoo(); //logs "bar"
thing1.setFoo("foo");
thing1.logFoo(); //logs "foo";
thing2.logFoo(); //logs "bar";
thing2.foo = "foobar";
thing1.logFoo(); //logs "foo";
thing2.logFoo(); //logs "foobar";
</script>
複製代碼
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
Thing.prototype.setFoo = function (newFoo) {
this.foo = newFoo;
}
Thing.prototype.deleteFoo = function () {
delete this.foo;
}
var thing = new Thing();
thing.setFoo("foo");
thing.logFoo(); //logs "foo";
thing.deleteFoo();
thing.logFoo(); //logs "bar";
thing.foo = "foobar";
thing.logFoo(); //logs "foobar";
delete thing.foo;
thing.logFoo(); //logs "bar";
</script>
複製代碼
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo, Thing.prototype.foo);
}
var thing = new Thing();
thing.foo = "foo";
thing.logFoo(); //logs "foo bar";
</script>
複製代碼
此時的this是指,構造函數的原型上的方法至於爲何看下面
<script type="text/javascript">
function Thing() {
}
Thing.prototype.things = [];
var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing2.things); //logs ["foo"]
</script>
複製代碼
<script type="text/javascript">
function Thing1() {
}
Thing1.prototype.foo = "bar";
function Thing2() {
}
Thing2.prototype = new Thing1();
var thing = new Thing2();
console.log(thing.foo); //logs "bar"
</script>
複製代碼
<script type="text/javascript">
function Thing1() {
}
Thing1.prototype.foo = "bar";
function Thing2() {
this.foo = "foo";
}
Thing2.prototype = new Thing1();
function Thing3() {
}
Thing3.prototype = new Thing2();
var thing = new Thing3();
console.log(thing.foo); //logs "foo"
</script>
複製代碼
<script type="text/javascript">
function Thing1() {
}
Thing1.prototype.foo = "bar";
Thing1.prototype.logFoo = function () {
console.log(this.foo);
}
function Thing2() {
this.foo = "foo";
}
Thing2.prototype = new Thing1();
var thing = new Thing2();
thing.logFoo(); //logs "foo";
</script>
複製代碼
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
var info = "attempting to log this.foo:";
function doIt() {
console.log(info, this.foo);
}
doIt();
}
var thing = new Thing();
thing.logFoo(); //logs "attempting to log this.foo: undefined"
</script>
複製代碼
在doIt裏面的this是global對象或者在嚴格模式下面是undefined。這是形成不少不熟悉JavaScript的人深陷 this陷阱的根源。在這種狀況下事情變得很是糟糕,就像你把一個實例的方法看成一個值,把這個值看成函數參數傳遞給另一個函數可是卻不把這個實例傳遞給這個函數同樣。在這種狀況下,一個方法裏面的環境變成了全局範圍,或者在嚴格模式下面的undefined。
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
function doIt(method) {
method();
}
var thing = new Thing();
thing.logFoo(); //logs "bar"
doIt(thing.logFoo); //logs undefined
</script>
複製代碼
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
var self = this;
var info = "attempting to log this.foo:";
function doIt() {
console.log(info, self.foo);
}
doIt();
}
var thing = new Thing();
thing.logFoo(); //logs "attempting to log this.foo: bar"
</script>
複製代碼
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
var self = this;
function doIt() {
console.log(self.foo);
}
doIt();
}
function doItIndirectly(method) {
method();
}
var thing = new Thing();
thing.logFoo(); //logs "bar"
doItIndirectly(thing.logFoo); //logs undefined
</script>
複製代碼
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
function doIt(method) {
method();
}
var thing = new Thing();
doIt(thing.logFoo.bind(thing)); //logs bar
</script>
複製代碼
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
function doIt() {
console.log(this.foo);
}
doIt.apply(this);
}
function doItIndirectly(method) {
method();
}
var thing = new Thing();
doItIndirectly(thing.logFoo.bind(thing)); //logs bar
</script>
複製代碼
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
function logFoo(aStr) {
console.log(aStr, this.foo);
}
var thing = new Thing();
logFoo.bind(thing)("using bind"); //logs "using bind bar"
logFoo.apply(thing, ["using apply"]); //logs "using apply bar"
logFoo.call(thing, "using call"); //logs "using call bar"
logFoo("using nothing"); //logs "using nothing undefined"
</script>
複製代碼
<script type="text/javascript">
function Thing() {
return {};
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
var thing = new Thing();
thing.logFoo(); //Uncaught TypeError: undefined is not a function
</script>
複製代碼
怪的是,若是你在構造函數裏面返回了一個原始值,上面所述的狀況並不會發生而且返回語句被忽略了。最好不要在你將經過new調用的構造函數裏面返回任何類型的數據,即使你知道本身正在作什麼。若是你想建立一個工廠模式,經過一個函數來建立一個實例,這個時候不要使用new來調用函數。固然這個建議是可選的。
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
var thing = Object.create(Thing.prototype);
thing.logFoo(); //logs "bar"
</script>
複製代碼
<script type="text/javascript">
function Thing() {
this.foo = "foo";
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
var thing = Object.create(Thing.prototype);
thing.logFoo(); //logs "bar"
</script>
複製代碼
<script type="text/javascript">
function Thing1() {
this.foo = "foo";
}
Thing1.prototype.foo = "bar";
function Thing2() {
this.logFoo(); //logs "bar"
Thing1.apply(this);
this.logFoo(); //logs "foo"
}
Thing2.prototype = Object.create(Thing1.prototype);
Thing2.prototype.logFoo = function () {
console.log(this.foo);
}
var thing = new Thing2();
</script>
複製代碼
<script type="text/javascript">
var obj = {
foo: "bar",
logFoo: function () {
console.log(this.foo);
}
};
obj.logFoo(); //logs "bar"
</script>
複製代碼
<script type="text/javascript">
var obj = {
foo: "bar"
};
function logFoo() {
console.log(this.foo);
}
logFoo.apply(obj); //logs "bar"
</script>
複製代碼
<script type="text/javascript">
var obj = {
foo: "bar",
deeper: {
logFoo: function () {
console.log(this.foo);
}
}
};
obj.deeper.logFoo(); //logs undefined
</script>
複製代碼
<script type="text/javascript">
var obj = {
foo: "bar",
deeper: {
logFoo: function () {
console.log(obj.foo);
}
}
};
obj.deeper.logFoo(); //logs "bar"
</script>
複製代碼
<script type="text/javascript">
function Listener() {
document.getElementById("foo").addEventListener("click",
this.handleClick);
}
Listener.prototype.handleClick = function (event) {
console.log(this); //logs "<div id="foo"></div>"
}
var listener = new Listener();
document.getElementById("foo").click();
</script>
複製代碼
<script type="text/javascript">
function Listener() {
document.getElementById("foo").addEventListener("click",
this.handleClick.bind(this));
}
Listener.prototype.handleClick = function (event) {
console.log(this); //logs Listener {handleClick: function}
}
var listener = new Listener();
document.getElementById("foo").click();
</script>
複製代碼
<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>
複製代碼
到此結束~~~~~~~~~~~~~~~~~~~~~~~~~~~~~