欢迎来到DIVCSS5查找CSS资料与学习DIV CSS布局技术!
如何改变函数的this指向
 
如果是函数声明,那么函数内部的this指向全局对象或者调用该函数的对象。
 
箭头函数的this绑定的是父级作用域内的this
 
使用call,apply,bind可以绑定this指向。其中bind是永久改变this指向并且不会立即执行。apply,call临时改变this指向并且立即执行。
 
apply,call不同之处在于调用的参数形式:
 
call(thisArg, arg1?, arg2?, arg3?…)第一个参数代表this指向,剩下的参数就是原函数的参数。
 
apply(thisArg, argArray?)第一个参数代表this指向,第二个参数是以数组的形式传递参数。
 
/* bind, call, apply调用示例 */
 
var obj = {
 
x: 'obj',
 
getX: function() {
 
// 注意这里不能使用箭头函数
 
return this.x;
 
}
 
};
 
// 调用 obj 对象上的函数属性, this就会绑定为obj
 
// 'obj'
 
console.log(obj.getX());
 
var x = 'window';
 
function target(a, b, c) {
 
console.log(a, b, c);
 
return this.x;
 
}
 
/* bind */
 
// undefined undefined undefined
 
// 'window'
 
console.log(target());
 
const targetBound = target.bind(obj, 1); // 此时targetBound就是一个改变了this指向的target
 
// 1 2 3
 
// 'obj'
 
console.log(targetBound(2, 3));
 
/* call */
 
// 4 5 6
 
// 'obj'
 
console.log(target.call(obj, 4, 5, 6));
 
/* apply */
 
// 7 8 9
 
// 'obj'
 
console.log(target.apply(obj, [7, 8, 9]));
 
自定义实现call,apply,bind
 
这三个函数都有很强的错误处理功能,假如传入的thisArg是一个基本类型,也会被使用包装类替换,虽然不会报错,但是不推荐这样写,尽量传递复杂类型的变量作为thisArg。
 
自定义实现myBind,myCall,myApply
 
// 先定义一个函数将任意类型都转换成对象,用于指向this
 
function anyToObj(value) {
 
  // symbol, bigint 就不判断了,直接在default中
 
  switch (typeof value) {
 
    case 'boolean':
 
      return new Boolean(value);
 
    case 'number':
 
      return new Number(value);
 
    case 'string':
 
      return new String(value);
 
    case 'undefined':
 
      return this;
 
    case 'object':
 
      if (value === null) return this; // 这里的this就指向了全局对象
 
      return value;
 
    default:
 
      return value;
 
  }
 
}
 
/* myBind */
 
Function.prototype.myBind = function (thisArg, …argArray) {
 
  // 防止出现 const myBind = Function.prototype.myBind; myBind();
 
  if (typeof this !== 'function') {
 
    throw new TypeError('myBind must be called on a function');
 
  }
 
  const that = this; // this 就指向 f.myBind() 的 f
 
  const thatArg = anyToObj(thisArg); // 处理一下thisArg
 
  const myBound = function (…args) {
 
    // 指定唯一的键
 
    const tempKey = Symbol('__innerFunction__');
 
    thatArg.tempKey = that; // 将 that 函数赋值给一个对象的属性
 
    let ret;
 
    if (this instanceof myBound) {
 
      // 调用 myBind 之后返回的是 myBound,假如调用 new myBound(),就会进这个分支
 
      ret = new thatArg.tempKey(…argArray.concat(args));
 
    } else {
 
      // 这里是调用 myBound(),这样调用 tempKey 方法里的 this 就指向了 thatArg
 
      ret = thatArg.tempKey(…argArray.concat(args));
 
    }
 
    // 不会对对象造成污染
 
    delete thatArg.tempKey;
 
    return ret;
 
  };
 
  return myBound;
 
};
 
/* myCall */
 
// 与 myBind 相比直接调用了
 
Function.prototype.myCall = function (thisArg, …argArray) {
 
  if (typeof this !== 'function') {
 
    throw new TypeError('myCall must be called on a function');
 
  }
 
  const thatArg = anyToObj(thisArg);
 
  const tempKey = Symbol('__innerFunction__');
 
  thatArg.tempKey = this;
 
  const ret = thatArg.tempKey(…argArray);
 
  delete thatArg.tempKey;
 
  return ret;
 
};
 
/* myApply */
 
// 与 myCall 相比,第二个参数希望是数组形式,多了个 CreateListFromArrayLike 用于转化 argArray
 
Function.prototype.myApply = function (thisArg, argArray) {
 
  if (typeof this !== 'function') {
 
    throw new TypeError('myApply must be called on a function');
 
  }
 
  const CreateListFromArrayLike = function (val) {
 
    // 同样缺乏 bigint 的处理,直接放在 default 中
 
    switch (typeof val) {
 
      case 'string':
 
      case 'number':
 
      case 'boolean':
 
      case 'symbol':
 
        throw new TypeError('CreateListFromArrayLike called on non-object');
 
      case 'object':
 
        if (Array.isArray(val)) return val;
 
        if (val === null) return [];
 
        return Array.from(val);
 
      default:
 
        return [];
 
    }
 
  };
 
  // 检测 argArray 的类型
 
  const tempArgArray = CreateListFromArrayLike(argArray);
 
  const thatArg = anyToObj(thisArg);
 
  const tempKey = Symbol('__innerFunction__');
 
  thatArg.tempKey = this;
 
  const ret = thatArg.tempKey(…tempArgArray);
 
  delete thatArg.tempKey;
 
  return ret;
 
};
 
// 这样 myBind,myCall,myApply就完成了,下面进行测试
 
var obj = {
 
x: 'obj',
 
getX: function(a, b, c) {
 
console.log(a, b, c);
 
return this.x;
 
}
 
}
 
var x = 'window';
 
// 1 2 3
 
// 'obj'
 
console.log(obj.getX(1, 2, 3));
 
// target变成一个全局函数,this 指向全局对象
 
const target = obj.getX;
 
// 1 2 3
 
// 'window'
 
console.log(target(1, 2, 3));
 
/* myBind */
 
const targetBound = target.myBind(obj, 1);
 
// 1 2 3
 
// 'obj'
 
console.log(targetBound(2, 3));
 
/* myCall */
 
// 4 5 6
 
// 'obj'
 
console.log(target.myCall(obj, 4, 5, 6));
 
/* myApply */
 
// 7 8 9
 
// 'obj'
 
console.log(target.myApply(obj, [7, 8, 9]));
 
这样自定义实现就完成了,大家可以试着看看怎么用才能报错。
 
如果有好的改进意见也可以评论留言,我会修改的

如需转载,请注明文章出处和来源网址:http://www.divcss5.com/html/h64367.shtml