# 面向对象 - call

函数实例的 `call` 方法，可以指定函数内部 `this` 的指向（即函数执行时所在的作用域），然后在所指定的作用域中，调用该函数

```javascript
var obj = {}

var f = function () {
  return this
}

f() === window // true
f.call(obj) === obj // true

```

上面代码中，全局环境运行函数 `f` 时，`this` 指向全局环境（浏览器为 `window` 对象）； `call` 方法可以改变 `this` 指向，指定 `this` 指向对象 `obj`，然后在对象 `obj` 的作用域中运行函数 `f`&#x20;

call 方法的参数，应该是一个对象。如果参数为空、`null` 和 `undefined`，则默认传入全局对象

```javascript
var n = 123
var obj = { n: 456 }

function a() {
  console.log(this.n)
}

a.call() // 123
a.call(null) // 123
a.call(undefined) // 123
a.call(window) // 123
a.call(obj) // 456

```

上面代码中，`a` 函数中的 `this` 关键字，如果指向全局对象，返回结果为 `123`。如果使用 `call` 方法将 `this` 关键字指向 `obj` 对象，返回结果为 `456`。可以看到，如果 `call` 方法没有参数，或者参数为 `null` 或 `undefined`，则等同于指向全局对象

如果 `call` 方法的参数是一个原始值，那么这个原始值会自动转成对应的包装对象，然后传入 `call` 方法

```javascript
var f = function () {
  return this
}

f.call(5)

// Number {[[PrimitiveValue]]: 5}
```

上面代码中，`call` 的参数为 `5`，不是对象，会被自动转成包装对象（`Number` 的实例），绑定 `f` 内部的 `this`

`call` 方法还可以接受多个参数

```javascript
func.call(thisValue, arg1, arg2, ...)
```

call 的第一个参数就是 this 所要指向的那个对象，后面的参数则是函数调用时所需的参数

```javascript
function add(a, b) {
  return a + b
}

add.call(this, 1, 2) // 3
```

上面代码中，`call` 方法指定函数 `add` 内部的 `this` 绑定当前环境（对象），并且参数为 `1` 和 `2`，因此函数 `add` 运行后得到 `3`

`call` 方法的一个应用是调用对象的原生方法

```javascript
var obj = {}
obj.hasOwnProperty('toString') // false

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

Object.prototype.hasOwnProperty.call(obj, 'toString') // false
```

上面代码中，`hasOwnProperty` 是 `obj`  对象继承的方法，如果这个方法一旦被覆盖，就不会得到正确结果。`call` 方法可以解决这个问题，它将 `hasOwnProperty` 方法的原始定义放在 `obj` 对象上执行，这样无论 `obj` 上有没有同名方法，都不会影响结果
