arrow function and this

Arrow functions don't bind this. As per MDN:

No binding of this

Until arrow functions, every new function defined its own this value (a new object in the case of a constructor, undefined in strict mode function calls, the context object if the function is called as an "object method", etc.). This proved to be annoying with an object-oriented style of programming.

So this in your example will be the global object window which apparently don't have a property called x.

Example:

function foo() {
  let arrow = () => {
    console.log(this);     // will use foo's this as arrow will never have its own this
  }
  
  arrow.call({"x": "x"});  // ... even if we specify it using bind, call, or apply
}

foo.call({"y": "y"});      // specifying the this for foo (this value will eventually be used by arrow because it will be availbale in its scope)

The key thing to remember is:

  • Arrow functions close over this, exactly the way that functions close over variables. (In fact, it's the same mechanism.) Whatever this is where the arrow function is created is what this will be during a call to that arrow function. It will never be anything else. Arrow functions ignore the this they're called with.

If you remember that, you'll never be confused by this in an arrow function again.

When you run this snippet in console it'll produce NaN. How? Author is explicitly binding the value of x, but still it's showing NaN.

numDouble = double.bind({ x: 5 }) creates a new function (numDouble) that, when called, will call the original function (double) with this set to the value you provide as bind's first argument ({ x: 5 }). But since arrow functions ignore the this they're called with, bind can't control what this they use.

Author is also specifying that arrow function can't bind this. As i know that arrow function lexically bind the value of this form surrounding scope.

Right, which means you can't change it. Lexical binding is the way closures work. This arrow function:

const a = () => {
    console.log(typeof this);
};

treats this exactly the way this traditional function treats thisWhereFunctionWasCreated:

const thisWhereFunctionWasCreated = this;
const t = function() {
    console.log(typeof thisWhereFunctionWasCreated);
};

Just like you can't change what thisWhereFunctionWasCreated variable t uses when you call it, you can't change what this a uses when you call it. (If thisWhereFunctionWasCreated weren't a const, you could change the value it holds, but not which thisWhereFunctionWasCreated variable t uses. But it's a constant in that example because this is a constant.)

Since an arrow function completely ignores the this it was called with, it doesn't matter what mechanism you use to try to tell the arrow function what this to use, it won't work. Whether you specify this implicitly by calling the function as a method (obj.arrow()), or via call or apply (arrow.call(obj)), or via bind (const boundArrow = arrow.bind(obj); boundArrow();), it will still use the this it closes over instead:

"use strict";

function Ctor() {
    
    // `this` will be the object created by `new Ctor`; grab it
    this.name = "outerThis";
    const outerThis = this;
    
    // `traditional` doesn't close over `this`, so you CAN change
    // what `this` it uses when you call it, in various ways
    function traditional(testNum) {
        console.log(testNum, "traditional:", getName(this));
    }
    
    // `arrow` closes over `this`, so you CAN'T change
    // what `this` it uses when you call it
    const arrow = testNum => {
        console.log(testNum, "arrow:      ", getName(this));
    };

    // Remember that the `this` in a direct call is the global
    // object in loose mode, `undefined` in strict mode; this
    // code is in strict mode
    console.log("Direct call (default `this`):");
    traditional(1);              // 1 traditional: window
    arrow(1);                    // 1 arrow:       outerThis
    
    console.log("`obj.xyz()`:");
    const obj = {
        name: "obj",
        arrow,
        traditional
    };
    obj.traditional(2);          // 2 traditional: obj
    obj.arrow(2);                // 2 arrow:       outerThis

    console.log("Using `call`:");
    traditional.call(obj, 3);    // 3 traditional: obj
    arrow.call(obj, 3);          // 3 arrow:       outerThis

    console.log("Using `bind` and calling result:");
    const boundTraditional = traditional.bind(obj);
    const boundArrow = arrow.bind(obj);
    boundTraditional(4);         // 4 traditional: obj
    boundArrow(4);               // 4 arrow:       outerThis
}

function getName(t) {
    switch (t) {
        case undefined:
            return "undefined";
        case window:
            return "window";
        default:
            return t.name;
    }
}

new Ctor();
.as-console-wrapper {
    max-height: 100% !important;
}

The only thing bind can do when called on an arrow function is bind arguments to it:

const arrow = (x, y) => x + y;
console.log(arrow(2, 3));      // 5

const arrowWith2 = arrow.bind(null, 2);
console.log(arrowWith2(3));    // 5

const arrowWith2And3 = arrow.bind(null, 2, 3);
console.log(arrowWith2And3()); // 5

(It also sets the name of the resulting function to "bound x" [where x is the name of the original function. So arrowWith2.name in the above is "bound arrow".)