JavaScript - This
来源:互联网 发布:蘑菇云刷机软件 编辑:程序博客网 时间:2024/06/05 00:10
One of most confusing mechanisms in JavaScript is the this
identifier. It’s a special identifier keyword that’s automatically defined in the scope of every function.
1. How to use this
this
refers to thecontext
(or object) that calls a function. (which means it defines the “scope” of the function)
function identify() { return this.name.toUpperCase();}function speak() { var greeting = "Hello, I'm " + identify.call( this ); console.log( greeting );}var me = { name: "Kyle"};var you = { name: "Reader"};// call(x) is explicitly invoking a function by x.identify.call( me ); // KYLEidentify.call( you ); // READERspeak.call( me ); // Hello, I'm KYLEspeak.call( you ); // Hello, I'm READER
identify()
and speak()
functions to be re-used against multiple context (me and you) objects, rather than needing a separate version of the function for each object.
Instead of relying on this
, you could have explicitly passed in a context object to both identify()
and speak()
. Like:
function identify(context) { return context.name.toUpperCase();}function speak(context) { var greeting = "Hello, I'm " + identify( context ); console.log( greeting );}identify( you ); // READERspeak( me ); // Hello, I'm KYLE
However, you’ll see that passing context
around as an explicit parameter is often messier than passing around a this
context.
When we explore objects and prototypes, you will see the helpfulness of a collection of functions being able to automatically reference the proper context object.
2. Eliminate the confusion of this
The name “this” creates confusion when developers try to think about it too literally. There are two meanings often assumed, but both are incorrect. this
is not a reference to the function itself or its scope.
1. Itself
The first common temptation is to assume this
refers to the function itself. That’s a reasonable grammatical inference, at least.
Many developers may believe that this
refer to a function from inside itself. The most common reasons would be things like recursion (calling a function from inside itself) or having an event handler that can unbind itself when it’s first called. lets you store state (values in properties) between function calls.
But for just a moment, we’ll explore that pattern, to illustrate how this
doesn’t let a function get a reference to itself like we might have assumed.
function foo(num) { console.log( "foo: " + num ); // keep track of how many times `foo` is called this.count++;}foo.count = 0;var i;for (i=0; i<10; i++) { if (i > 5) { foo( i ); }}// foo: 6// foo: 7// foo: 8// foo: 9// how many times was `foo` called?console.log( foo.count ); // 0 -- WTF?
foo.count
is still 0, even though the four console.log
statements clearly indicate foo(..)
was in fact called four times. The frustration stems from a too literal interpretation of what this
(in this.count++
) means.
Note: the this.count++
in the code refers to a global variable count
which is not declared yet. So when it runs, it will be declared automatically and its value it NaN
. //NaN++ is also a NaN
.
When the code executes foo.count = 0
, indeed it’s adding a property count
to the function object foo
. But for the this.count
reference inside of the function, this
is not in fact pointing at all to that function object, and so even though the property names are the same, the root objects are different, and confusion ensues.
To reference a function object from inside itself, this
by itself will typically be insufficient. You generally need a reference to the function object via a lexical identifier (variable) that points at it.
function foo() { foo.count = 4; // `foo` refers to itself}setTimeout( function(){ // anonymous function (no name), cannot // refer to itself}, 10 );
So another solution to our running example would have been to use the foo
identifier as a function object reference in each place, and not use this
at all, which works:
function foo(num) { console.log( "foo: " + num ); // keep track of how many times `foo` is called foo.count++;}foo.count = 0;var i;for (i=0; i<10; i++) { if (i > 5) { foo( i ); }}// foo: 6// foo: 7// foo: 8// foo: 9// how many times was `foo` called?console.log( foo.count ); // 4
Yet another way of approaching the issue is to force this
to actually point at the foo
function object:
function foo(num) { console.log( "foo: " + num ); // keep track of how many times `foo` is called // Note: `this` IS actually `foo` now, based on // how `foo` is called (see below) this.count++;}foo.count = 0;var i;for (i=0; i<10; i++) { if (i > 5) { // using `call(..)`, we ensure the `this` // points at the function object (`foo`) itself foo.call( foo, i ); }}// foo: 6// foo: 7// foo: 8// foo: 9// how many times was `foo` called?console.log( foo.count ); // 4
2 . Scope
The next most common misconception about the meaning of this
is that it somehow refers to the function’s scope. It’s a tricky question, because in one sense there is some truth, but in the other sense, it’s quite misguided.
To be clear, this
does not, in any way, refer to a function’s lexical scope.
Consider code which attempts (and fails!) to cross over the boundary and use this
to implicitly refer to a function’s lexical scope:
function foo() { var a = 2; this.bar();}function bar() { console.log("invoking bar"); console.log( this.a ); // failed to access "var a"}foo(); //undefined
However, the developer who writes such code is attempting to use this
to create a bridge between the lexical scopes of foo()
and bar()
, so that bar()
has access to the variable a
in the inner scope of foo()
. No such bridge is possible.
Every time you feel yourself trying to mix lexical scope look-ups with
this
, remind yourself: there is no bridge.
3. What is this
?
We said earlier that this
is not an author-time binding but a run-time binding. It is contextual based on the conditions of the function’s invocation (who call the function).
this
binding has nothing to do with where a function is declared, but has instead everything to do with the manner in which the function is called.
When a function is invoked, an activation record, otherwise known as an execution context, is created. This record contains information about where the function was called from (the call-stack), how the function was invoked, what parameters were passed, etc. One of the properties of this record is the
this
reference which will be used for the duration of that function’s execution.
For the previous example:
var a = 4;function foo() { var a = 2; this.bar();}function bar() { console.log( this.a );}foo(); // 4
foo()
is called by the window or global
. So this
refers to the global context. It will emit 4 (global variable) instead. There is no bridge between two function scopes.
4. Four rules determine where this
will point
a. Default Binding
function foo() { console.log( this.a );}var a = 2;foo(); // 2
The first thing to note, is that variables declared in the global scope, as var a = 2 is, are synonymous with global-object properties of the same name. They’re not copies of each other, they are each other. Think of it as two sides of the same coin.
Secondly, we see that when foo()
is called, this.a
resolves to our global variable a
. Why? Because in this case, the default binding for this applies to the function call, and so points this
at the global object.(window)
If strict mode
is in effect, the global object is not eligible for the default binding, so the this is instead set to undefined.
function foo() { "use strict"; console.log( this.a );}var a = 2;foo(); // TypeError: `this` is `undefined`
A subtle but important detail is: even though the overall this
binding rules are entirely based on the call-site, the global object is only eligible for the default binding if the contents of foo()
are not running in strict mode
; the strict mode
state of the call-site of foo()
is irrelevant.
function foo() { console.log( this.a );}var a = 2;(function(){ "use strict";//There is no strict mode in the content of foo() function foo(); // 2})();
b. Implicit Binding
Another rule to consider is: does the call-site have a context object, also referred to as an owning or containing object, though these alternate terms could be slightly misleading.
function foo() { console.log( this.a );}var obj = { a: 2, foo: foo};obj.foo(); // 2
Note: Only the top/last level of an object property reference chain matters to the call-site. For instance:
function foo() { console.log( this.a );}var obj2 = { a: 42, foo: foo};var obj1 = { a: 2, obj2: obj2};obj1.obj2.foo(); // 42, not the obj1.a, but obj2.a
Implicitly Lost: One of the most common frustrations that this
binding creates is when an implicitly bound function loses that binding, which usually means it falls back to the default binding, of either the global object or undefined
, depending on strict mode
.
function foo() { console.log( this.a );}var obj = { a: 2, foo: foo};var bar = obj.foo; // function reference/alias!var a = "oops, global"; // `a` also property on global objectbar(); // "oops, global"
Even though bar
appears to be a reference to obj.foo
, in fact, it’s really just another reference to foo
itself. Moreover, the call-site is what matters, and the call-site is bar()
, which is a plain, un-decorated call and thus the default binding applies. (this points to global object)
Note: The more subtle, more common, and more unexpected way this occurs is when we consider passing a callback function:
function foo() { console.log( this.a );}function doFoo(fn) { // `fn` is just another reference to `foo` fn(); // <-- call-site!}var obj = { a: 2, foo: foo};var a = "oops, global"; // `a` also property on global objectdoFoo( obj.foo ); // "oops, global"
Parameter passing is just an implicit assignment, and since we’re passing a function, it’s an implicit reference assignment, so the end result is the same as the previous snippet.
c. Explicit Binding
We had to mutate the object in question to include a reference on itself to the function, and use this property function reference to indirectly (implicitly) bind this to the object.
But, what if you want to force a function call to use a particular object for the this binding, without putting a property function reference on the object?
Specifically, functions have call(..)
and apply(..)
methods. They both take, as their first parameter, an object to use for the this
, and then invoke the function with that this
specified.
function foo() { console.log( this.a );}var obj = { a: 2 // no function reference here};foo.call( obj ); // 2
d. new
Binding
JavaScript has a new
operator, and the code pattern to use it looks basically identical to what we see in those class-oriented languages; most developers assume that JavaScript’s mechanism is doing something similar. However, there really is no connection to class-oriented functionality implied by new
usage in JS.
In JS, constructors are just functions that happen to be called with the new
operator in front of them. They are not attached to classes, nor are they instantiating a class.
When a function is invoked with new
in front of it, otherwise known as a constructor call, the following things are done automatically:
- a brand new object is created
- he newly constructed object is [[Prototype]]-linked
- the newly constructed object is set as the this binding for that function call
- unless the function returns its own alternate object, the new-invoked function call will automatically return the newly constructed object.
function foo(a) { this.a = a;}var bar = new foo( 2 );console.log( bar.a ); // 2
By calling foo(..)
with new
in front of it, we’ve constructed a new object and set that new object as the this
for the call of foo(..)
.
So new is the final way that a function call’s this can be bound. We’ll call this new binding.
5. The order of binding rules
function foo() { console.log( this.a );}var obj1 = { a: 2, foo: foo};var obj2 = { a: 3, foo: foo};obj1.foo(); // 2obj2.foo(); // 3obj1.foo.call( obj2 ); // 3obj2.foo.call( obj1 ); // 2
So, explicit binding takes precedence over implicit binding, which means you should ask first if explicit binding applies before checking for implicit binding.
function foo(something) { this.a = something;}var obj1 = { foo: foo};var obj2 = {};obj1.foo( 2 );console.log( obj1.a ); // 2obj1.foo.call( obj2, 3 );console.log( obj2.a ); // 3var bar = new obj1.foo( 4 );console.log( obj1.a ); // 2console.log( bar.a ); // 4
OK, new binding is more precedent than implicit binding. But do you think new binding is more or less precedent than explicit binding?
Note: new
and call/apply
cannot be used together, so new foo.call(obj1)
is not allowed, to test new
binding directly against explicit binding. But we can still use a hard binding to test the precedence of the two rules.
default binding is the lowest.
- javascript this
- javascript this
- JavaScript---this
- JavaScript ???????? this ??
- javascript-this
- Javascript this
- JavaScript - This
- Javascript-this
- JavaScript-this
- 【javascript】javascript中的this
- 【javascript】javascript对象中的this
- 【javascript笔记】this in javascript
- JavaScript中的this指针
- javascript this详解
- javascript 中的"this"
- "this" of JavaScript
- javascript的this使用
- javascript this详解
- 磁盘管理
- iOS本地推送
- 接近源生的android-async-http框架库使用基础
- [itweens插件] itween插件中文文档
- Python入门:文件内容去重操作
- JavaScript - This
- Apache cxf JaxWs基本应用
- linux常用命令-查找文件(find/locate)
- 一天一条Linux指令-true
- maven 内嵌tomcat 开发web 系统
- 高并发Java:NIO和AIO
- CSS3 基础(011_用户接口)
- 文章标题
- 优化SQL查询:如何写出高性能SQL语句