扫码关注微信公众号

回复“面试手册”,获取本站PDF版

回复“简历”,获取高质量简历模板

回复“加群”,加入程序员交流群

回复“电子书”,获取程序员类电子书

当前位置: 前端 > javascript高频面试题 > 18.手写call,apply,bind

先回顾下call,apply,bind的区别和用法:call 和 apply 的主要作用,是改变对象的执行上下文,并且是立即执行的。它们在参数上的写法略有区别;bind 也能改变对象的执行上下文,它与 call 和 apply 不同的是,返回值是一个函数,可以延后调用

call()

var name = 'John'
var obj = {
	name: 'Jack'
}
function getName(num1,num2){
	console.log(this.name + num1 + num2)
}
console.log(getName(1,2)) // John12
console.log(getName.call(obj,1,2)) // 使用call将this指向obj,输出Jack12

apply()

var name = 'John'
var obj = {
	name: 'Jack'
}
function getName(num1,num2){
	console.log(this.name + num1 + num2)
}
console.log(getName(1,2)) // John12
console.log(getName.apply(obj,[1,2])) // 使用apply将this指向obj,与call不同的是,参数形式是数组,输出Jack12

bind()

var name = 'John'
var obj = {
	name: 'Jack'
}
function getName(num1,num2){
	console.log(this.name + num1 + num2)
}
console.log(getName(1,2)) // John12
console.log(getName.bind(obj)(1,2)) // 使用bind将this指向obj,与call和apply不同的是,返回值是一个函数,需要再次用,
									//输出Jack12

手写一个call

Function.prototype.selfCall = function(target, ...args) {
  if (typeof this !== "function") {	// 判断调用对象是不是函数
    throw new TypeError("not a function")
  }
  target = target || window // 绑定对象不存在时,指向window对象
  target.fn = this // 函数的this指向隐式绑定到target上,改变构造函数的调用者间接改变 this 指向
  let result = target.fn(...args) // 此时调用的是target.fn,this指向target
  return result
};
var name = 'John'
var obj = {
	name: 'Jack'
}
function getName(num1,num2){
	console.log(this.name + num1 + num2)
}
console.log(getName(1,2)) // John12
console.log(getName.selfCall(obj,1,2)) // Jack12

手写一个apply

Function.prototype.selfApply = function(target) {
  if (typeof this !== "function") { // 判断调用对象是不是函数
    throw new TypeError("not a function");
  }
  if (!Array.isArray(arguments[1])) { // 判断第二个传参是不是数组
    throw new Error('arg not a array')
  }
  target = target || window // 绑定对象不存在时,指向window对象
  target.fn = this // 函数的this指向隐式绑定到target上,改变构造函数的调用者间接改变 this 指向
  let args = arguments[1] // 获取函数的传参
  let result = target.fn(...args) // 此时调用的是target.fn,this指向target,因为传参是数组,解构后传入函数执行。
  return result
};
var name = 'John'
var obj = {
	name: 'Jack'
}
function getName(num1,num2){
	console.log(this.name + num1 + num2)
}
console.log(getName(1,2)) // John12
console.log(getName.selfApply(obj,[1,2])) // Jack12

手写一个bind

Function.prototype.selfBind = function(thisArg) {
  if (typeof this !== 'function') {
    throw TypeError("Bind must be called on a function");
  }
  const args = Array.prototype.slice.call(arguments, 1),
    self = this,
    // 构建一个干净的函数,用于保存原函数的原型
    nop = function() {},
    // 绑定的函数
    bound = function() {
      // this instanceof nop, 判断是否使用 new 来调用 bound
      // 如果是 new 来调用的话,this的指向就是其实例,
      // 如果不是 new 调用的话,就改变 this 指向到指定的对象 o
      return self.apply(
        this instanceof nop ? this : thisArg,
        args.concat(Array.prototype.slice.call(arguments)) 
      );
    };
  // 箭头函数没有 prototype,箭头函数this永远指向它所在的作用域
  if (this.prototype) {
    nop.prototype = this.prototype;
  }
  // 修改绑定函数的原型指向
  bound.prototype = new nop();
  return bound;
}
var name = 'John'
var obj = {
	name: 'Jack'
}
function getName(num1,num2){
	console.log(this.name + num1 + num2)
}
console.log(getName(1,2)) // John12
console.log(getName.selfBind(obj)(1,2)) // Jack12

点击面试手册,获取本站面试手册PDF完整版