3

用call来解释this很形象,但是不是那么严谨,详情看补充1;

了解this的本质

比较严谨的解释:一个函数可以在多个环境中执行,函数执行时允许访问环境中的其他变量,this会指向调用函数的那个环境;

比较易懂的解释:

  • 函数调用时都指定了它的外部环境,这里用call来展示外部环境:
  • fn(x);,可以理解为fn.call(undefined,x);这里fn中的this就是undefined;
  • obj.fn(); 可以理解为obj.fn.call(obj,x);这里fn中的this就是obj;

call函数的第一个参数就是指定的外部环境,就是this
想要知道一个函数中的this是谁,大部分情况下可以去函数调用栈中找上一个函数看到。

this的绑定规则

1.默认绑定

  • 像fn();
  • 严格模式下等价于fn.call(undefined);
  • 非严格模式下等价于fn.call(全局对象);这个全局对象在浏览器中是window,node中是global;

2.隐氏绑定

  • 像obj.fn();等价于fn.call(obj),obj作为一个上下文对象隐性传递到了fn中;
  • 这个传递是隐性的,我们观察不到的。

3.显示绑定

  • 像fn.call(context); fn在执行时,this就是context;
  • 这个绑定很明显,是我们可以观察、并且控制的。
  • 显示绑定很常用,这里贴一个bind方法的极简实现
function bind(fn, obj) {
    return function() {
        return fn.apply( obj, arguments );
    }; 
}

4.new绑定

  • js中的new操作符和其他语言的new机制完全不一样。
  • new执行的操作用伪代码表达一下:
// 执行new Foo()时发生的操作
var obj = {};                        // 创建空对象
obj.__proto__ = Foo.prototype;        // 连接到foo的原型,继承机制
Foo.call(obj);                        // 绑定成上下文,并执行foo
return obj;                            // foo中如果没有return的话,return obj这个对象回去

补充1:this被忽略的情况,用call做例子不严谨的原因

  • 当把null或undefined作为this传入call、apply、bind时,实际应用的是默认绑定规则;
// 非严格模式
function foo() { 
    console.log( this.a );
}
var a = 2;
foo.call( null ); // 2

补充2:一个结合this、声明提升、全局局部变量的例子:

var a=10;    
function test(){        
    a=5;        
    alert(a);        
    alert(this.a);        
    var a;       
    alert(this.a);        
    alert(a);    
}
执行test()和new test() 结果是什么

答案:
alert的内容:test(): 5,10,10,5
new test():5,undefined,undefined,5

补充3:this丢失(优先级)

// 用一个简单的例子开个头
// obj.fn是一种隐性绑定,将fn中的this指向obj
// 但是this又被context覆盖了,这种this丢失可以引申出优先级的问题。
obj.fn.call(context)

补充4:箭头函数

箭头函数的this继承上层函数

var obj = {
      say: function () {
        setTimeout(() => {
            console.log(this)    //obj
        }, 0)
      }
    }

补充5

立即执行函数执行时,obj还是undefined

var obj = {
    say: function () {
      function _say() {
        console.log(this)    //window or global
      }
      return _say.bind(obj)
    }()
}
obj.say()

补充6

var length = 10;
function fn() {
    console.log(this.length);
}

var obj = {
  length: 5,
  method: function(fn) {
    fn();
    arguments[0]();    // arguments.0()
  }
};

obj.method(fn, 1);
// 10 2

补充7

var a=10;
var foo={
  a:20,
  bar:function(){
      var a=30;
      return this.a;
    }
}
foo.bar()
(foo.bar)()
(foo.bar=foo.bar)()
(foo.bar,foo.bar)()
// 20 20 10 10

补充8

var num = 1;
function codequn(){
    'use strict';
    console.log('codequn: ',this.num++);
}
function codequn2(){
    console.log('codequn2: ',++this.num);
}
(function(){
    'use strict';
    codequn2();
})();
codequn();
// codequn2: 2   
// Cannot read property 'num' of undefined

补充9

箭头函数中的this是定义时所在对象的this,而不是执行时

const debounce = function(fn, time) {
  let timeout = null
  return function() {
    const _self = this
    clearTimeout(timeout)
    timeout = setTimeout(()=>{
      console.log(this === _self)    //true
      fn.call(this, ...arguments)
    }, time)
  }
}
参考:
《你不知道的javascript上卷》;
https://www.zhihu.com/questio...
http://www.ruanyifeng.com/blo...
https://developer.mozilla.org...

qikke
10 声望3 粉丝