普通函数this指向进阶实例讲解

注:新版chrome浏览器下 全局是window 严格模式下:全局是undefined node下:全局是global

其实函数对于宿主对象来说,更专业的说法是绑定关系,fn的绑定对象为obj,obj为fn的执行环境,这篇再整理一些进阶的this实例

const obj = {
          
   
  a: 1,
  fn: function() {
          
   
    console.log(this.a)
  }
}

obj.fn() // 1
// fn绑定的对象为obj,所以console 为 1
const f = obj.fn
f() //undefined
// fn绑定的对象为全局,所以console 为 undefined
function fn () {
          
   
  console.log(this.a)
}

const obj = {
          
   
  a: 1
}

const newFn = fn.bind(obj);
newFn(); // 1
//给fn绑定obj为宿主对象,所以console 为 1
const obj1 = {
          
   
  a: 1,
  fn: function() {
          
   
    console.log(this.a)
  }
}

setTimeout(()=>{
          
   
  obj1.fn();
}, 1000) //1
//这里的绑定对象是obj1,所以为1

setTimeout(obj1.fn,1000);// undefined
//这里相当于把obj1.fn给全局windows,所以为undefined

//函数作为参数传递
function run(fn) {
          
   
  fn()
}
run(obj1.fn) // undefined
// 这里传进去的是一个引用,所以fn()是在windows下执行的
function fn () {
          
   
  console.log(this.a)
}

fn.call(null) // undefined
//当call,bind,apply第一个参数为null的时候,则绑定全局
function fn() {
          
   
  console.log(this)
}

fn.bind(1).bind(2)() // Number(1)
// 为啥可以绑定基本类型 ?
// boxing(装箱) -> (1 ----> Number(1))
// bind 只看第一个 bind(堆栈的上下文,上一个,写的顺序来看就是第一个)

new (fn.bind(1))(); // fn{};
//当bind有new的情况下,不看bind的看new
//指向以fn为构造函数的生成的实例对象

所以衍生一个问题,手写一个bind方法

function bindFn(fn, ...args) {
          
   
    if (typeof fn !== function) {
          
   
        throw new TypeError(function is not callable);
    }
	//获取绑定对象
    const otherThis = args.splice(1);
    //获取剩余参数
    const letArgs = args;
    //获取当前方法
    const fnThis = fn;
    //定义绑定函数
    const fBound = function () {
          
   
        return fnThis.call(
            fnThis.prototype.isPrototypeOf(this) ? this : otherThis, ...letArgs, ...arguments
        )
    }
    //寄生组合方式通过中间函数将fnThis的原形对象赋予fBound的原形对象
    if (fnThis.prototype) {
          
   
        fBound.prototype = Object.create(fnThis.prototype)
    }
}
function foo(a) {
          
   
  this.a = a
}

const f = new foo(2);
f.a // 2

function foo(a) {
          
   
    this.a = a;
    return {
          
   };
}

const f = new foo(2);
f.a // undefined

//new 关键字做了什么
//将函数的原形对象赋值给对象f的隐式原形对象__proto__
//**将函数foo,绑定给对象f
//**如果函数有返回值且为对象,则返回那个新对象,否则返回对象f
function foo() {
          
   
  console.log( this.a ) // console what? 2
}
var a = 2;
(function(){
          
   
  "use strict" // 迷惑大家的
  foo();
})();
var obj1 = {
          
   
  a: 1,
}

var obj2 = {
          
   
  a: 2,
  fn: function () {
          
   
    console.log(this.a);
  }
}

obj2.fn.call(obj1); //1
//这里的bind,call,apply为显式绑定
//这里的obj.fn为隐式绑定
//之前的window.this为默认绑定
//之前的new为new绑定

所以综上所有的例子可以得出一个结论 优先级顺序为 new > 显式绑定 > 隐式绑定 > 默认绑定

over

经验分享 程序员 微信小程序 职场和发展