js学习笔记-函数中this

151 阅读5分钟

1、this的指向

函数中的this总指向调用它的对象。

2、this的绑定形式

2.1、this的默认绑定

当一个函数没有明确的调用对象的时候,也就是单纯作为独立函数调用的时候,将对函数的this使用默认绑定:绑定到全局的window对象。

凡是函数作为独立函数调用,无论它的位置在哪里,它的行为表现,都和直接在全局环境中调用无异。

如:

var obj = {
   fire: function () {
       function innerFire() {
          console.log(this === window)
        }
        innerFire();   // 独立函数调用
     }
}
obj.fire(); //输出 true

2.2、this的隐式绑定

当函数被一个对象“包含”的时候,我们称函数的this被隐式绑定到这个对象里面了,这时候,通过this可以直接访问所绑定的对象里面的其他属性。

fire函数并不会因为它被定义在obj对象的内部和外部而有任何区别,如下:

// 我是第一段代码
function fire () {
      console.log(this.a)
}

var obj = {
      a: 1,
      fire: fire
  }
obj.fire(); // 输出1

// 我是第二段代码
var obj = {
        a: 1,
        fire: function () {
             console.log(this.a)
         }
}
obj.fire(); // 输出1

注意点:

  1. this是动态绑定的,或者说是在代码运行期绑定而不是在书写期; 基于this动态绑定的特点,写在对象内部,作为对象属性的函数,对于这个对象来说是独立的。(函数并不被这个外部对象所“完全拥有”)

  2. 函数于对象的独立性, this的传递丢失问题;如函数赋值的过程:

var obj = {
      a: 1,    // a是定义在对象obj中的属性   1
      fire: function () {
   console.log(this.a)
        }
      }

var a = 2;  // a是定义在全局环境中的变量    2
var fireInGrobal = obj.fire;  
fireInGrobal(); //  输出 2

3、在一串对象属性链中,this绑定的是最内层的对象;

var obj = {
      a: 1,
      obj2: {
           a: 2,
           obj3: {
                a:3,
                getA: function () {
                    console.log(this.a)   
                 }
           }
       }
}

obj.obj2.obj3.getA();  // 输出3

2.3、this的显示绑定(call\apply\bind)

关于this的传递丢失问题,call()、apply()、bind() 都是用来重定义 this 这个对象的!

call的基本使用方式:fn.call(object,'args1','args2')

fn是你调用的函数,object参数是你希望函数的this所绑定的对象。

  • 即刻调用这个函数(fn)

  • 调用这个函数的时候函数的this指向object对象

apply的基本使用方式:fn.apply(object,['args1','args2'])

  • 即刻调用这个函数(fn)

bind的基本使用方式:

fn.bind(object,'args1','args2')或者fn.bind(object,['args1','args2'])

  • bind不执行函数,只返回一个可供执行的函数;
var obj = {
  name:'duizhang',
  age:'22',
  adress:'1128'
} 
function print(){
  console.log(this); // 打印 this 的指向
  console.log(arguments); // 打印传递的参数
} 
// 通过 call 改变 this 指向
print.call(obj,1,2,3);
// 通过 apply 改变 this 指向
print.apply(obj,[1,2,3]);
// 通过 bind 改变 this 的指向
let fn = print.bind(obj,1,2,3);
fn();

2.4、new绑定

执行new操作的时候,将创建一个新的对象,并且将构造函数的this指向所创建的新对象 ;

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

var a1  = new foo (1);
var a2  = new foo (2);
var a3  = new foo (3);
var a4  = new foo (4);

console.log(a1.a); // 输出1
console.log(a2.a); // 输出2
console.log(a3.a); // 输出3
console.log(a4.a); // 输出4

3、箭头函数中的this

由于箭头函数没有单独的 this 值。箭头函数的 this 与声明所在的上下文相同。也就是说调用箭头 函数的时候,不会隐士的调用 this 参数,而是从定义时的函数继承上下文。

const obj = {
  a:()=>{
     console.log(this);
   }
} /
/ 对象调用箭头函数
obj.a(); // window

4、call、apply、bind 实现

4.1、手写call

  • 首先 context 为可选参数,如果不传的话默认上下文为 window ;
  • 接下来给 context 创建一个 fn 属性,并将值设置为需要调用的函数;
  • 因为 call 可以传入多个参数作为调用函数的参数,所以需要将参数剥离出来;
  • 然后调用函数并将对象上的函数删除。
// this 为调用的函数
// context 是参数对象
Function.prototype.myCall = function(context){
      console.log("this",this);//此时this指向调用函数 person
      // 判断调用者是否为函数
      if(typeof this !== 'function'){
      	throw new TypeError('Error')
      } 
      // 不传参默认为 window
      context = context || window
      // 通过为context新增fn属性,并将this赋值fn函数
      context.fn = this
      // 将 arguments 转化为数组,将 call 的传参提取出来 [...arguments]
      const args = Array.from(arguments).slice(1)
      // 传参调用函数,此时this指向context
      const result = context.fn(...args)//数组解构...args
      // 删除函数
      delete context.fn
      // 返回执行结果
      return result;
} 
// 普通函数
function person(age){
	console.log(this.name+" "+age);
} 
// 自定义对象
var obj = {
	name:'yyqx'
} 
// 调用函数的 call 方法
person.myCall(obj,18,2,3) 

4.2、手写apply

  • 首先 context 为可选参数,如果不传的话默认上下文为 window
  • 接下来给 context 创建一个 fn 属性,并将值设置为需要调用的函数
  • 因为 apply 传参是数组传参,所以取得数组,将其剥离为顺序参数进行函数调用
  • 然后调用函数并将对象上的函数删除
Function.prototype.myApply = function(context){
      // 判断调用者是否为函数
      if(typeof this !== 'function'){
      	throw new TypeError('Error')
      } 
      // 不传参默认为 window
      context = context || window
      // 新增 fn 属性,将值设置为需要调用的函数
      context.fn = this
      // 返回执行结果
      let result;
      // 判断是否有参数传入
      if(arguments[1]){
      	result = context.fn(...arguments[1])
      }else{
      	result = context.fn()
      } 
      // 删除函数
      delete context.fn
      // 返回执行结果
      return result;
}
// 普通函数
function print(age,age2,age3){
	console.log(this.name+" "+ age + " "+ age2+" "+age3);
} 
// 自定义对象
var obj = {
	name:'小鹿'
} 
// 调用函数的 call 方法
print.myApply(obj,[1,2,3])

4.3、手写bind

  • 判断调用者是否为函数。
  • 截取参数,注意:这里有两种形式传参。
  • 返回一个函数,判断外部哪种方式调用了该函数(new | 直接调用)
Function.prototype.myBind = function (context) {
        // 判断调用者是否为函数
        if(typeof this !== 'function'){
        	throw new TypeError('Error')
        } 
        // 截取传递的参数
        const args = Array.from(arguments).slice(1)
        // _this 指向调用的函数
        const _this = this;
        // 返回一个函数
        return function F(){
          // 因为返回了一个函数,我们可以 new F(),所以需要判断
          // 对于 new 的情况来说,不会被任何方式改变 this
          if(this instanceof F){
              return new _this(...args,...arguments)
          }else{
              return _this.apply(context,args.concat(...arguments))
          }
	}
}
/
/ 普通函数
function print(){
  // new 的方式调用 bind 参数输出换做 [...arguments]
  console.log(this.name);
} 
// 自定义对象
var obj = {
   name:'小鹿'
} 
// 调用函数的方法
let F = print.myBind(obj,1,2,3);
F()
// 返回对象的方式
let obj1 = new F();
console.log(obj1);

5、自执行函数中的this

自执行函数的this都指向window。

(function(){
	console.log(this.foo);//this指向的是window
})();

原文参考: 1、mp.weixin.qq.com/s/QAXswIi-y…