认识call apply bind

认识call apply bind

::: tip
函数原型链中的 apply,call 和 bind 方法是 JavaScript 中相当重要的概念,与 this 关键字密切相关.
:::

apply

apply()方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。

::: warning
call()方法的作用和 apply() 方法类似,区别就是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组。
:::

var numbers = [5, 6, 2, 3, 7];
var max = Math.max.apply(null, numbers);
console.log(max);
// expected output: 7
var min = Math.min.apply(null, numbers);
console.log(min);
// expected output: 2

语法

func.apply(thisArg, [argsArray])

thisArg

可选的。在 func 函数运行时使用的 this值。请注意,this可能不是该方法看到的实际值:如果这个函数处于非严格模式下,则指定为 nullundefined 时会自动替换为指向全局对象,原始值会被包装。

argsArray

可选的。一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数。如果该参数的值为 nullundefined,则表示不需要传入任何参数。从ECMAScript 5 开始可以使用类数组对象.

例子

let array=[1,2]
let elements=[3,4]
array.push.apply(array,elements)

console.log(array)

call

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

语法

fun.call(thisArg, arg1, arg2, ...)

thisArg

thisArg
fun 函数运行时指定的 this 值。

如果this的值是 null或者 undefined ,指向window

if(thisArg == undefined|null) this = window

如果this的值是 number|boolean|string ,指向构造对象里面

if(thisArg == number|boolean|string) this == new Number()|new Boolean()| new String()

arg1, arg2, ...

参数列表

例子

使用 call 方法调用父构造函数

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

function Toy(name, price) {
  Product.call(this, name, price);
  this.category = 'toy';
}

var cheese = new Food('feta', 5);
var fun = new Toy('robot', 40);

使用 call 方法调用函数并且指定上下文的 this

function greet() {
  var reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
  console.log(reply);
}

var obj = {
  animal: 'cats', sleepDuration: '12 and 16 hours'
};

greet.call(obj);  // cats typically sleep between 12 and 16 hours

bind

bind()方法创建一个新的函数,在bind()被调用时,这个新函数的thisbind的第一个参数指定,其余的参数将作为新函数的参数供调用时使用

语法

function.bind(thisArg[,arg1[,arg2[, ...]]])

arg1, arg2, ...
当目标函数被调用时,预先添加到绑定函数的参数列表中的参数

thisArg

调用绑定函数时作为this参数传递给目标函数的值。 如果使用new运算符构造绑定函数,则忽略该值。当使用bindsetTimeout中创建一个函数(作为回调提供)时,作为thisArg传递的任何原始值都将转换为object。如果bind函数的参数列表为空,执行作用域的this将被视为新函数的thisArg

返回一个原函数的拷贝,并拥有指定的this值和初始参数。

例子

this.x = 9;    // 在浏览器中,this指向全局的 "window" 对象
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX();
// 返回9 - 因为函数是在全局作用域中调用的

// 创建一个新函数,把 'this' 绑定到 module 对象
// 新手可能会将全局变量 x 与 module 的属性 x 混淆
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81

call/apply与bind的区别

call/apply改变了函数的this上下文后马上执行该函数

bind则是返回改变了上下文后的函数,不执行该函数

call/apply 返回fun的执行结果

bind返回fun的拷贝,并指定了funthis指向,保存了fun的参数。

共同点

改变方法中this的指向,减少重复代码,节省内存

引用场景

摘自

http://obkoro1.com/web_accumulate/accumulate/JS/JS基础-call和apply还有bind.html

判断数据类型

function isType(data, type) {
    const typeObj = {
        '[object String]': 'string',
        '[object Number]': 'number',
        '[object Boolean]': 'boolean',
        '[object Null]': 'null',
        '[object Undefined]': 'undefined',
        '[object Object]': 'object',
        '[object Array]': 'array',
        '[object Function]': 'function',
        '[object Date]': 'date', // Object.prototype.toString.call(new Date())
        '[object RegExp]': 'regExp',
        '[object Map]': 'map',
        '[object Set]': 'set',
        '[object HTMLDivElement]': 'dom', // document.querySelector('#app')
        '[object WeakMap]': 'weakMap',
        '[object Window]': 'window',  // Object.prototype.toString.call(window)
        '[object Error]': 'error', // new Error('1')
        '[object Arguments]': 'arguments',
    }
    let name = Object.prototype.toString.call(data) // 借用Object.prototype.toString()获取数据类型
    let typeName = typeObj[name] || '未知类型' // 匹配数据类型
    return typeName === type // 判断该数据类型是否为传入的类型
}
console.log(
    isType({}, 'object'), // true
    isType([], 'array'), // true
    isType(new Date(), 'object'), // false
    isType(new Date(), 'date'), // true
)

类数组借用数组的方法

var arrayLike = {
  0: 'OB',
  1: 'Koro1',
  length: 2
}
Array.prototype.push.call(arrayLike, '添加元素1', '添加元素2');
console.log(arrayLike)
 // {"0":"OB","1":"Koro1","2":"添加元素1","3":"添加元素2","length":4}

手写apply

Function.apply.myApply = function(context) {
  // 如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。
  if (context === null || context === undefined) {
    context = window;
  }
  // 一个数组或者类数组对象,其中的数组元素将作为单独的参数传给 func 函数
  function isArrayLike(o) {
    if (
      o && // o不是null、undefined等
      typeof o === "object" && // o是对象
      isFinite(o.length) && // o.length是有限数值
      o.length >= 0 && // o.length为非负值
      o.length === Math.floor(o.length) && // o.length是整数
      o.length < 4294967296
    )
      // o.length < 2^32
      return true;
    else return false;
  }
  // 用于临时储存函数
  const define_fn = Symbol("manael_apply");
  // 隐式绑定this指向到context上
  context[define_fn] = this;
  let args = arguments[1]; // 获取参数数组
  let result;
  // 处理传进来的第二个参数
  if (args) {
    // 是否传递第二个参数
    if (!Array.isArray(args) && !isArrayLike(args)) {
      throw new TypeError(
        "myApply 第二个参数不为数组并且不为类数组对象抛出错误"
      );
    } else {
      args = Array.from(args); // 转为数组
      result = context[specialPrototype](...args); // 执行函数并展开数组,传递函数参数
    }
  } else {
    result = context[specialPrototype](); // 执行函数
  }
  delete context[specialPrototype]; // 删除上下文对象的属性
  return result; // 返回函数执行结果
};
};

手写 call

Function.prototype.myCall = function (context, ...arr) {
    if (context === null || context === undefined) {
       // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
        context = window
    } else {
        context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
    }
    const specialPrototype = Symbol('特殊属性Symbol') // 用于临时储存函数
    context[specialPrototype] = this; // 函数的this指向隐式绑定到context上
    let result = context[specialPrototype](...arr); // 通过隐式绑定执行函数并传递参数
    delete context[specialPrototype]; // 删除上下文对象的属性
    return result; // 返回函数执行结果
};

手写 bind

Function.prototype.myBind = function (objThis, ...params) {
    const thisFn = this; // 存储源函数以及上方的params(函数参数)
    // 对返回的函数 secondParams 二次传参
    let fToBind = function (...secondParams) {
        const isNew = this instanceof fToBind // this是否是fToBind的实例 也就是返回的fToBind是否通过new调用
        const context = isNew ? this : Object(objThis) // new调用就绑定到this上,否则就绑定到传入的objThis上
        return thisFn.call(context, ...params, ...secondParams); // 用call调用源函数绑定this的指向并传递参数,返回执行结果
    };
    if (thisFn.prototype) {
        // 复制源函数的prototype给fToBind 一些情况下函数没有prototype,比如箭头函数
        fToBind.prototype = Object.create(thisFn.prototype);
    }
    return fToBind; // 返回拷贝的函数
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值