面向对象编程-this关键字

本文详细介绍了 JavaScript 中 `this` 的基本概念及其在不同场景下的指向,包括全局环境、构造函数、对象方法等。还讨论了 `this` 指向可能变化的情况,如回调函数、数组方法和多层 `this` 使用的问题,并提供了解决方案。最后,讲解了 `call`、`apply` 和 `bind` 方法用于绑定 `this` 指向的用法,并给出了实际示例。
摘要由CSDN通过智能技术生成

this 它总是返回一个对象

- this就是属性或方法“当前”所在的对象
    let obj = {
      name: "中小余",
      age: 18,
      gender: "女",
      say: function() {
        return "hello " + this.name; //  this指的name属性所在的对象
      }
    };
    obj.say(); // 'hello 中小余'

- this的指向是可变的
    let obj = {
      name: "中小余",
      age: 18,
      gender: "女",
      say: function() {
        return "hello " + this.name;
      }
    };
    obj.say(); //'hello 中小余'
    let obj2 = {
      name: "中小鱼",
      age: 17
    };
    obj2.say = obj.say; //  对象的属性可以赋值给另一个对象
    obj2.say();//'hello 中小鱼'

使用场合

- 全局环境
	在全局环境中使用`this`它指的就是顶层对象`window`
	this === window; // true

- 构造函数
	构造函数中的this,指的是实例对象。
	let Obj = function(val) {
      this.val = val;
    };
    let o = new Obj("hello world");
    console.log(o); // { val: 'hello world' }

- 对象的方法
	如果对象的方法里面包含thisthis的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变this的指向。
    let obj = {
      name: "中小余",
      say: function() {
        console.log(this.name + "说:我是一只酸菜鱼,又酸又菜又多余");
      }
    };
    obj.say(); // '中小余说:我是一只酸菜鱼,又酸又菜又多余'


		* 下面这几种用法,都会改变this的指向:
            /*
                // 情况一
                (obj.foo = obj.foo)() // window
                // 情况二
                (false || obj.foo)() // window
                // 情况三
                (1, obj.foo)() // window
            */
		* 如果this所在的方法不在对象的第一层,这时this只是指向当前一层的对象,而不会继承更上面的层。
			let obj = {
              name: "中小余",
              method: {
                say: function() {
                  console.log(this.name + "说:我是一只酸菜鱼,又酸又菜又多余");
                }
              }
            };
            obj.method.say(); // 'undefined说:我是一只酸菜鱼,又酸又菜又多余'

使用this的注意事项

- 避免使用多层this
	由于this的指向是不确定的,所以切勿在函数中包含多层的this
    解决办法:将this的值赋值给一个固定的变量。
    let obj = {
      name: "中小余",
      method: function() {
        let _this = this;// 将外层this赋值给变量_this ,然后在内层使用_this,就不会发生this指向的改变
        let say = (function() {
          console.log(_this.name + "说:我是一只酸菜鱼,又酸又菜又多余");
        })();
      }
    };
    obj.method();//'中小余说:我是一只酸菜鱼,又酸又菜又多余'

- 避免数组处理方法(map forEach)中的this
	数组处理方法中使用this有两种处理方式:
    	*this的值赋值给一个固定的变量(同上)
		*this当作forEach方法的第二个参数固定它的运行环境
		let obj = {
          name: "中小余",
          faves: ["漫画", "动漫", "鲁迅的文字"],
          method: function fun() {
            this.faves.forEach(function(item) {
              console.log(this.name + "喜欢" + item);
            }, this);
          }
        };
        obj.method();
        //'中小余喜欢漫画'
        //'中小余喜欢动漫'
        //'中小余喜欢鲁迅的文字'

- 避免回调函数中的this
	回调函数中的this往往会改变指向,最好避免使用
   // 为了解决这个问题,绑定this方法,也就是使得this固定指向某个对象,减少不确定性。

绑定this的方法

JavaScript提共了三个方法用来绑定this的指向:callapplybind

Function.prototype.call()

- Function.prototype.call()
    函数实例的call方法,可以指定函数内部this的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。
   
    let obj = {};
    let fun = function() {
      console.log(this);
    };

    fun(); // window
    fun.call(obj); // {}
 
// call方法的参数,应该是一个对象。如果参数为空、null和undefined,则默认传入全局对象。
	let str = "字符串";
    let obj = { str: "对象属性" };
    let fun = function() {
      console.log(this);
    };
    fun.call();  // window
    fun.call(null);// window
    fun.call(undefined);// window
    fun.call(window);// window
    fun.call(obj);{str:'对象属性'}

	//如果call方法的参数是一个原始值,那么这个原始值会自动转成对应的包装对象,然后传入call方法
	let fun = function() {
      return this;
    };
    fun.call(1); //[Number: 1]

	// call方法还可以接受多个参数 
	fun.call(thisValue, arg1, arg2, ...)
	
	// call的第一个参数就是this所要指向的那个对象,后面的参数则是函数调用时所需的参数 
	let fun = function(val1, val2) {
      return val1 + val2;
    };
    fun.call(this, 1, 2); // 3 call方法指定函数fun内部的this绑定当前环境(对象)
- call方法的一个应用: 调用对象的原生方法 
	let obj = {};
    obj.hasOwnProperty("toString");//false

    // 覆盖掉继承的 hasOwnProperty 方法
    obj.hasOwnProperty = function() {
      return true;
    };
    obj.hasOwnProperty("toString");// true

    Object.prototype.hasOwnProperty.call(obj, "toString");//false
		

Function.prototype.apply()

- apply方法的作用与call方法类似,也是改变this指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。

func.apply(thisValue, [arg1, arg2, ...])
                       
// apply方法的第一个参数也是this所要指向的那个对象,如果设为null或undefined,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在call方法中必须一个个添加,但是在apply方法中,必须以数组形式添加
 	
	let fun = function(val1, val2) {
      return val1 + val2;
    };
    fun.call(this, 1, 2); // 3
    fun.apply(this, [1, 2]); // 3                       
                       
apply可以做的事
 - 结合使用apply方法和Math.max方法,找出数组最大元素
	let arr = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 51, 53, 59];
    Math.max.apply(this, arr);  //59

- 将数组的元素变为undefined
	//空元素与undefined的差别在于,数组的forEach方法会跳过空元素,但是不会跳过undefined。因此,遍历内部元素的时候,会得到不同的结果
	let arr = [1, 2, 3, , 5];
    function p(i) {
      console.log(i);
    }
    arr.forEach(p); // 1 2 3 5
    Array.apply(this, arr).forEach(p);// 1 2 3 undefined 5

- 转换类似数组的对象
	// 利用数组对象的slice方法,可以将一个类似数组的对象(比如arguments对象)转为真正的数组
	Array.prototype.slice.apply({0: 1, length: 1}) // [1]
    Array.prototype.slice.apply({0: 1}) // []
    Array.prototype.slice.apply({0: 1, length: 2}) // [1, undefined]
    Array.prototype.slice.apply({length: 1}) // [undefined]
	// 参数都是对象 且对象有length属性才行

- 绑定回调函数的对象
	let obj = new Object();
    obj.f = function() {
      console.log(this === obj);
    };
    let fun = function() {
      obj.f.apply(obj);
    };
    fun(); // true

Function.prototype.bind()

bind()方法用于将函数体内的this绑定到某个对象,然后返回一个新函数

let date = new Date();

let p = date.getTime.bind(date);
p;// [Function: bound getTime]  返回的是一个新函数
p(); // 1618481231883

- bind方法的参数就是所要绑定this的对象
    let obj = {
      name: "中小余",
      age: 18,
      method: function() {
        this.age++;
      }
    };
    let fun = obj.method.bind(obj);
    fun();
    obj; // { name: '中小余', age: 19, method: [Function: method] }  age已经加了

- 也可以绑定到其它对象上
	let obj = {
      name: "中小余",
      age: 18,
      method: function() {
        this.age++;
      }
    };
    let obj2 = {
      age: 16
    };
    let fun = obj.method.bind(obj2);
    fun();
    obj;// { name: '中小余', age: 18, method: [Function: method] }
    obj2; //{ age: 17 }


- bind()还可以接受更多的参数,将这些参数绑定原函数的参数。

    let add = function(x, y) {
      return x * this.m + y * this.n;
    };

    let obj = {
      m: 2,
      n: 2
    };

    let newAdd = add.bind(obj, 5);
    newAdd(5); // 20

- 如果bind()方法的第一个参数是null或undefined,等于将this绑定到全局对象,函数运行时this指向顶层对象(浏览器为window)。

    function add(x, y) {
      return x + y;
    }

    let plus5 = add.bind(null, 5);
    plus5(10) // 15
注意事项
-  bind()方法每运行一次,就返回一个新函数,这会产生一些问题。比如,监听事件的时候,不能写成下面这样。
	element.addEventListener('click', o.m.bind(o));
	element.removeEventListener('click', o.m.bind(o));
	//click事件绑定bind()方法生成的一个匿名函数。这样会导致无法取消绑定,所以下面的代码是无效的。
	* 正确的写法:
    var listener = o.m.bind(o);
    element.addEventListener('click', listener);
    //  ...
    element.removeEventListener('click', listener);

- 结合回调函数使用
	回调函数是 JavaScript 最常用的模式之一,但是一个常见的错误是,将包含this的方法直接当作回调函数。
    let obj = {
      name: "中小余",
      faves: ["漫画", "动漫", "鲁迅的文字"],
      method: function() {
        this.faves.forEach(function(n) {
          console.log(this.name + "喜欢" + n);
        });
      }
    };
    obj.method();
        // 喜欢漫画'
        //'喜欢动漫'
        //'喜欢鲁迅的文字'

    * 改成:
     let obj = {
      name: "中小余",
      faves: ["漫画", "动漫", "鲁迅的文字"],
      method: function() {
        this.faves.forEach(
          function(n) {
            console.log(this.name + "喜欢" + n);
          }.bind(this)
        );
      }
    };
    obj.method();
	// '中小余喜欢漫画'
    //'中小余喜欢动漫'
    //'中小余喜欢鲁迅的文字'

- 结合 call()方法使用
	利用bind()方法,可以改写一些 JavaScript 原生方法的使用形式,以数组的slice()方法为例。
	[1, 2, 3].slice(0, 1) // [1]
    // 等同于
    Array.prototype.slice.call([1, 2, 3], 0, 1) // [1]
	 
	// 改成写bind()
	var slice = Function.prototype.call.bind(Array.prototype.slice);
	slice([1, 2, 3], 0, 1) // [1]

	


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

中小余

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值