【 JavaScript 】理解call、apply、bind

前言

如果还没有理解this、prototype,需要先去学习相关的知识。

js中如何绑定this?

js中有以下几种方式用来绑定this:

  1. 默认绑定:非严格模式下独立函数调用this默认指向全局对象(浏览器环境为window对象),严格模式指向undefined
  2. 隐式绑定:调用位置是否有上下文对象,或者是否被某个对象拥有或者包含
  3. 显式绑定:call、apply、bind
  4. new绑定:new操作符绑定
  5. 箭头函数绑定:箭头函数的绑定无法修改,根据当前的词法作用域来决定this

所以call、apply、bind就是用来绑定this对象的,并没有什么特别的地方。

举个简单的例子:

// 例1:
// 按照上面绑定this的方法中的第1条,foo函数【默认绑定】了this对象并指向全局对象
function foo() {
    
     return this.a }
// 按照第2条,obj1对象【隐式绑定】了this对象并指向他自己
const obj1 = {
    
     a: 'this is a' }
// 执行foo函数,全局对象中没有属性a,返回undefined
console.log(foo())         // undefined
// 按照第3条,foo函数使用call【显式绑定】obj1的this对象,
// obj1有属性a,返回属性a的值:'this is a'
console.log(foo.call(obj1)) // this is a

// 例2:
// 按照第5条,bar函数使用【箭头函数绑定】了this对象并指向全局对象
const bar = () => {
    
     return this.b }
// 按照第2条,obj2对象【隐式绑定】了this对象并指向他自己
const obj2 = {
    
     b: 'this is b' }
// 执行bar函数,全局对象中没有属性b,返回undefined
console.log(bar())		    // undefined
// bar函数想要使用call【显式绑定】obj2的this对象,
// 但因为bar函数是箭头函数,无法修改他的this对象的指向,返回undefined
console.log(bar.call(obj2)) // undefined

foo.call(obj1)用文字翻译就是foo函数绑定了obj1的this对象,也可以翻译成改变foo函数内部this对象的指向至obj1。

根据例1,我们体验到了使用call改变this指向的灵活性,根据例2,又感受到了js在版本迭代中对call这种可以随意改变this指向的接口的限制,作为开发者,合理使用各种新旧语法就行。

call与apply的区别

function foo(a, b, c) {
    
     return this.message + (a + b + c) }
function bar(a, b, c) {
    
     return this.message + (a + b + c) }
const obj = {
    
     message: 'sum:' }
console.log(foo.call(obj, 1, 2, 3))    // sum:6
console.log(bar.apply(obj, [1, 2, 3])) // sum:6

区别:call第二到第n个参数需要一个一个传递,apply只需在第二个参数中传递一个参数列表,call与apply仅传参方式不一致

bind与call、apply的区别

function foo(a, b, c) {
    
     return this.message + (a + b + c) }
function bar(a, b, c) {
    
     return this.message + (a + b + c) }
const obj = {
    
     message: 'sum:' }
console.log(foo.call(obj, 1, 2, 3)) // sum:6
console.log(foo.bind(obj)) // ƒ foo(a, b, c) { return this.message + (a + b + c) }
console.log(bar.bind(obj)(1, 2, 3)) // sum:6

区别:bind返回函数体,需要再次调用,传参方式与call一致

实现call

function foo(a, b, c) {
    
     return this.message + (a + b + c) }
const obj = {
    
     message: 'sum:' }
Function.prototype.myCall = function(context, ...args) {
    
    
  context = context || window
  args = args.length ? args : []

  let key = Symbol()

  // 由于foo调用了myCall,myCall函数内的this对象就【隐式绑定】至foo函数
  context[key] = this
  // 将foo函数成为context(也就是obj)的一个属性,
  // 那么foo函数的this对象就会【隐式绑定】至obj上

  // 执行foo函数
  const result = context[key](...args)

  delete context[key]

  return result
}
console.log(foo.myCall(obj, 1, 2, 3)) // 6

其他

js中要检测一个属性是否是对象的自有属性时,可以使用hasOwnProperty方法:

const obj = {
    
     'key': 'value' }
// 写法1
console.log(obj.hasOwnProperty('key')) // true
// 写法2
console.log(Object.prototype.hasOwnProperty.call(obj, 'key')) // true

如果项目中安装了eslint并且使用了写法1,eslint会报如下错误:
Do not access Object.prototype method ‘hasOwnProperty’ from target object

具体原因查看:
https://blog.csdn.net/qq_36727756/article/details/105464902
简单来说就是不要在目标对象上使用Object原型上的属性,否则会有安全问题,解决方法就是使用写法2。

写法2的解释:
hasOwnProperty是Object原型上的方法,根据上面绑定this的方法中的第1条,hasOwnProperty函数内已【默认绑定】了this对象,然后使用call将hasOwnProperty的this对象【显式绑定】至obj,以此完成了从Object上调用原型方法去判断目标对象上的属性。

常见的Array.prototype.slice.call() 也是如此解释。

猜你喜欢

转载自blog.csdn.net/qq_34417216/article/details/123728862
今日推荐