Note On <You Don't Know JS - this and Object Prototypes>

来源:互联网 发布:百度云mac版怎么加速 编辑:程序博客网 时间:2024/06/17 03:20



Chapter 1

this is not an author-time binding but a runtime binding.

It is contextual based on the conditions of the function's invocation.

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.


Chapter 2

How does the call-site determine where this will point during the execution of a function?


Rule 1: Default Binding

Standalone function invocation, this rule is the default when none of the other rules apply.

function foo() {console.log(this.a);}var a = 2;foo(); // 2


It points this at the global object.


Note: If strict mode is in effect, the global object is not eligible for the default binding, so the this is instead set to undefined.However, whether the global object is eligible for the default binding is determined only by if the contents of foo() are running in strict mode; not the state of the call-site of foo():

function foo() {console.log( this.a );}var a = 2;(function(){"use strict";foo(); // 2})();


Rule 2: Implicit Binding

In this case, the call-site has a context object.

function foo() {console.log(this.a);}var obj = {a: 2,foo: foo};obj.foo(); // 2


Regardless of whether foo() is initially declared on foo, or is added as a reference later (as this snippet shows), in neither case is the function really "owned" or "contained" by the obj object.


However, the call-site uses the obj context to reference the function, and when there is a context object for a function reference, the implicit binding rule says that it's that object that should be used for the function call's this binding.


Implicitly lost

Implicitly bound function can lose the binding, and it usually falls back to the default binding. It happens when passing a callback function as a parameter.

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"


Rule 3: Explicit Binding


function foo() {console.log(this.a);}var obj = {a: 2};foo.call(obj); // 2


Now you force its this to be obj.


Note: If you pass a simple primitive value (of type string, boolean, or number) as the this binding, the primitive value is wrapped in its object form (new String(..), new Boolean(..), or new Number(..), respectively). This is often referred to as "boxing."


Hard binding

But it doesn't solve implicitly lost. To fix it, you need Hard binding:

function foo() {console.log(this.a);}var obj = {a: 2};var bar = function() {foo.call(obj);};bar(); // 2setTimeout(bar, 100); // 2// hard-bound `bar` can no longer have its `this` overriddenbar.call(window); // 2


No matter how you later invoke the function bar, it will always manually invoke foo with obj. This binding is both explicit and strong, so we call it hard binding.

function foo(something) {console.log(this.a, something);return this.a + something;}var obj = {a: 2};var bar = function() {return foo.apply( obj, arguments );};var b = bar(3); // 2 3console.log(b); // 5


Or you can create a helper:

function foo(something) {console.log(this.a, something);return this.a + something;}// simple `bind` helperfunction bind(fn, obj) {return function() {return fn.apply(obj, arguments);};}var obj = {a: 2};var bar = bind(foo, obj);var b = bar(3); // 2 3console.log(b); // 5


Actually it's provided with a builtin utility as of ES5, Function.prototype.bind, and it's used like this:

function foo(something) {console.log(this.a, something);return this.a + something;}var obj = {a: 2};var bar = foo.bind(obj);var b = bar(3); // 2 3console.log(b); // 5


Rule 4: new Rule

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. They are not even special types of functions. They're just regular functions.


Any ol' function, including the built-in object functions like Number(..) can be called with new in front of it, and that makes that function call a constructor call.


When a function is invoked with new in front of it, the following things are done automatically:

  1. A brand new object is created (aka constructed) out of thin air.
  2. The newly constructed object is [[Prototype]]-linked.
  3. The newly constructed object is set as the this binding for that function call.
  4. 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


Precedence of the Rules

  1. new binding
  2. explicit binding
  3. implicit binding
  4. default binding

new binding vs. explicit binding

function foo(something) {    this.a = something;}var obj1 = {};var bar = foo.bind(obj1);bar(2);console.log(obj1.a); // 2var baz = new bar(3);console.log(obj1.a); // 2console.log(baz.a); // 3


new binding vs. 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


explicit binding vs. implicit binding

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



0 0
原创粉丝点击