【译文】this全解

一. 全局 this

1.在浏览器中,在一个全局环境中,this就是window对象。

<script type="text/javascript">
console.log(this === window); //true
</script>

2.在浏览器中,在全局中运用var相当于分派给this或许window

<script type="text/javascript">
    var foo = "bar";
    console.log(this.foo); //logs "bar"
    console.log(window.foo); //logs "bar"
</script>

3.假如你建立一个新的变量,不运用var或许let(ECMAScript6),你是增加或许转变全局this的属性

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();
    console.log(this.foo); //logs "foo"
</script>

4.在node中运用repl,this是最顶级的定名空间,你可以以为是global

> this
{ ArrayBuffer: [Function: ArrayBuffer],
  Int8Array: { [Function: Int8Array] BYTES_PER_ELEMENT: 1 },
  Uint8Array: { [Function: Uint8Array] BYTES_PER_ELEMENT: 1 },
  ...
> global === this
true

5.在node中实行剧本,在全局中this是一个空对象,而不与global相称

test.js:
console.log(this);
console.log(this === global);
$ node test.js
{}
false

6.在node中,全局环境中的var并不是像在浏览器中实行剧本一样,分派给this

test.js:
var foo = "bar";
console.log(this.foo);
$ node test.js
undefined

然则在repl中是一样的

> var foo = "bar";
> this.foo
bar
> global.foo
bar

7.在node中,运用剧本实行,不必var或许let建立的变量会增加到global而不是this.

test.js
foo = "bar";
console.log(this.foo);
console.log(global.foo);
$ node test.js
undefined
bar

在repl中,它是分派到这两个上的。

二. 函数中的this

除了DOM事宜处置惩罚顺序或许一个thisArg已设置的状况外,在node和浏览器中,函数中(不实例化new)的this是全局局限的。

<script type="text/javascript">
    foo = "bar";

    function testThis() {
      this.foo = "foo";
    }

    console.log(this.foo); //logs "bar"
    testThis();
    console.log(this.foo); //logs "foo"
</script>
test.js:
foo = "bar";

function testThis () {
  this.foo = "foo";
}

console.log(global.foo);
testThis();
console.log(global.foo);
$ node test.js
bar
foo

除非你运用user strictthis会变成underfined

<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>

当你new一个函数的时刻,this会成为一个新的上下文,不等同于全局this

<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

函数对象有一个特别的属性prototype,当你建立一个函数实例,可以接见prototype属性,可以运用this举行接见

    function Thing() {
      console.log(this.foo);
    }

    Thing.prototype.foo = "bar";

    var thing = new Thing(); //logs "bar"
    console.log(thing.foo);  //logs "bar"

到场建立多个实例化,它们同享原型上的值,this.foo都邑返回雷同的值,除非你在实例化函数上举行掩盖。

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";

this在一个实例中是一个特别的对象,this现实是一个关键字,可以以为this作为一种要领去接见prototype,直接分派给this,将会掩盖本来prototype上的要领。你可以删除this挂接的要领,从而恢复接见默许prototype

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";

或许直接援用函数对象的原型。

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";

建立的实例都同享雷同的属性和要领,假如给prototype分派一个数组,一切实例都可以接见。

function Thing() {
}
Thing.prototype.things = [];


var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing2.things); //logs ["foo"]

prototype上分派一个数组一般是一个毛病,假如愿望每一个实例都有本身的数组,那在函数中建立。

function Thing() {
    this.things = [];
}


var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing1.things); //logs ["foo"]
console.log(thing2.things); //logs []

this可以经由过程原型链找到响应的要领。

function Thing1() {
}
Thing1.prototype.foo = "bar";

function Thing2() {
}
Thing2.prototype = new Thing1();


var thing = new Thing2();
console.log(thing.foo); //logs "bar"

在javascript中可以运用原型链模仿传统面向对象继续。
运用函数内含有绑定this的要领或许属性去建立原型链,将会隐蔽上层原型链定义的内容。

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"

我喜好叫绑定在原型上的函数为methods.在methods中运用this绑定某个值,将会掩盖原型上的相干定义。

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";

在JavaScript嵌套函数中,虽然可以捕获到父函数中的变量,然则不继续this

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"

函数doIt中的this指向global,在use strict下则为undefined,这是许多不熟悉this用法的人痛楚的泉源之一。
更坏的状况是,将一个实例要领作为参数传入函数。this将指向global,在use strict下则为undefined

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

一些人把this赋值给一个变量,一般叫self,可以防止this指向global这个题目。

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"

然则这类要领在将一个实例要领作为参数传入函数状况下,不起作用

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

处理这个要领,可以运用函数绑定的要领bind

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

你也可以运用apply或许call在新的上下文环境中挪用要领或许函数。

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

可以运用bind替代this,适用于任何函数或要领,纵然没有在实例原型上定义。

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"

防止从组织函数返回任何东西,由于它可能会替代所发生的实例。

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

奇怪的是,假如你返回的是原始值(string或许number),返回语句将会被疏忽。最好不要从你盘算挪用的组织函数中返回任何东西,纵然你晓得你在做什么。假如你想建立一个工场形式,运用一个函数来建立实例,不要用new的。固然,这只是个人观点。
防止运用new`,运用Object.create也能建立一个实例

function Thing() {
}
Thing.prototype.foo = "bar";


Thing.prototype.logFoo = function () {
    console.log(this.foo);
}


var thing =  Object.create(Thing.prototype);
thing.logFoo(); //logs "bar"

但是这不会挪用组织函数。

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"

由于Object.create不会挪用组织函数,所以这是一个有用的建立继续形式的要领,可以重写原型链上的组织函数。

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();

四. 对象中的this

可以在对象的任何函数中运用this来援用该对象上的其他属性。这与运用new实例差别。

var obj = {
    foo: "bar",
    logFoo: function () {
        console.log(this.foo);
    }
};

obj.logFoo(); //logs "bar"

不运用new,Object.create ,function 去建立一个对象,也可以像实例化一样绑定到对象上。

var obj = {
    foo: "bar"
};

function logFoo() {
    console.log(this.foo);
}

logFoo.apply(obj); //logs "bar"

当你像下面运用this时,没有顺着对象的条理构造。只要直接父对象上的属性才经由过程this举行接见

var obj = {
    foo: "bar",
    deeper: {
        logFoo: function () {
            console.log(this.foo);
        }
    }
};

obj.deeper.logFoo(); //logs undefined

你可以直接运用你想要的属性。

var obj = {
    foo: "bar",
    deeper: {
        logFoo: function () {
            console.log(obj.foo);
        }
    }
};

obj.deeper.logFoo(); //logs "bar"

五. DOM event中的this

在一个HTML DOM event处置惩罚顺序中,this一般是指DOM element event绑定的对象

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();

除非你绑定新的上下文

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();

六. HTML中的this

在HTML属性中可以放js代码,this指向当前的元素

<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>

this的掩盖
你不可以复写this,由于它是一个关键词

function test () {
    var this = {};  // Uncaught SyntaxError: Unexpected token this 
}

七. eavl中的this

可以运用eavl接见this

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    eval("console.log(this.foo)"); //logs "bar"
}

var thing = new Thing();
thing.logFoo();

这类做法有安全隐患,可以运用Function接见this

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = new Function("console.log(this.foo);");

var thing = new Thing();
thing.logFoo(); //logs "bar"

八. with中的this

可以运用withthis增加到当前的局限来读取和写入值,而不必显式挪用。

function Thing () {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
    with (this) {
        console.log(foo);
        foo = "foo";
    }
}

var thing = new Thing();
thing.logFoo(); // logs "bar"
console.log(thing.foo); // logs "foo"

许多人以为这是错的做法,鉴于with引发的歧义。

九. jQuery中的this

像HTML DOM elements的事宜处置惩罚顺序一样,jQuery在许多处所运用this指向DOM元素。比方$.each

<div class="foo bar1"></div>
<div class="foo bar2"></div>
<script type="text/javascript">
$(".foo").each(function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").on("click", function () {
    console.log(this); //logs <div class="foo...
});
$(".foo").each(function () {
    this.click();
});
</script>

鉴于笔者翻译程度有限,有什么题目迎接提出指教。

十.参考资料

原文地点:all this

    原文作者:hopeCoder
    原文地址: https://segmentfault.com/a/1190000006768557
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞