1. 程式人生 > 實用技巧 >call、apply和bind的用法及區別

call、apply和bind的用法及區別

一、call和apply

1、call和apply都是function.prototype的方法,每一個方法的物件例項 new function() 都會有這兩個屬性;

2、這兩個屬性都是針對方法的屬性針對方法使用;

3、call和apply使用方法相同;

4、call和apply的引數不同;
(1)test.call ( this, param1 , param2 , param3 ) ; // 可以傳多個引數
(2)test.apply ( this, paramArray ) ; // 只能傳兩個引數,第二個一般為陣列

二、bind

call和apply會改變this指向,然後執行函式;
bind會改變this指向後,返回一個繫結新this的函式;

三、demo

var name = "aven";
var age = 18;

var obj = {
      name: "ake",
      age: this.age,
      test: function(){
          console.log("name: ", this.name, "age: ", this.age );
  }
};

obj.test(); 
// name: ake  age: undefined
// 因為obj.test() 只會呼叫本身屬性,本身沒有age屬性,但是有name屬性

obj.test.call()
// name:  aven age:  18
// call引數為null或undefined時預設指向window,
// this含有屬性 name : "aven", age: 18,所以輸出結果變化
var name = "testName";
var age = 18;

var obj = {
      name: "ake",
      age: this.age,
      test: function(gender, home){
          console.log("name: ",this.name,"age:",this.age,"gender:",gender,"home: ", home);
  }
};

var person = {
      name: "aven",
      age: 18
}

obj.test.call(person, "female", "China")  // name:  aven age: 18 gender: female home:  China
obj.test.apply(person, ["female", "China"])  // name:  aven age: 18 gender: female home:  China
obj.test.bind(person, "female", "China")()  // name:  aven age: 18 gender: female home:  China
obj.test.bind(person, ["female", "China"])()  // name:  aven age: 18 gender: ["female", "China"] home:  undefined

obj.test.bind(this, "female", "China")() // name:  testName age: 18 gender: female home:  China

四、實現call方法

Function.prototype.myCall = function () {
    var _this = arguments[0]
    var params = [...arguments].slice(1)
    var isStrict = (function () {return this === undefined})
    if (!isStrict) {
        // 如果是其他原始值,需要通過建構函式包裝成物件
        var type = typeof _this;
        if (type === 'number') {
            _this = new Number(_this)
        } else if (type === 'string') {
            _this = new String(_this)
        } else if (type === 'boolean') {
            _this = new Boolean(_this)
        }
    } 
    var invokeFun = this;
    if (_this === null || _this === undefined) {
        return invokeFunc(...params)
    }
    var uniquePropName = Symbol(_this)
    _this[uniquePropName] = invokeFun
    return _this[uniquePropName](...params)
}

五、實現一個apply,跟call相似,把引數列表改為引數陣列

Function.prototype.myApply = function (_this, params) {
    var isStrict = (function() {return this === undefined}())
    if (!isStrict) {
        // 如果是其他原始值,需要通過建構函式包裝成物件
        var type = typeof _this;
        if (type === 'number') {
            _this = new Number(_this)
        } else if (type === 'string') {
            _this = new String(_this)
        } else if (type === 'boolean') {
            _this = new Boolean(_this)
        }
    }
    var invokeFun = this

    var invokeParams = Array.isArray(params) ? params : []
    if (_this === null || _this === undefined) {
        return invokeFun(...invokeParams)
    }
    uniquePropName = Symbol(_this)
    _this[uniquePropName] = invokeFun
    return _this[uniquePropName](...invokeParams)
}

六、實現bind,區別在於

Function.prototype.myBind = function() {
    var boundTargetFunc = this;
    if (typeof boundTargetFunc !== 'function') {
      throw new Error('繫結的目標必須是函式')
    }
    var boundThis = arguments[0];
    var boundParams = [].slice.call(arguments, 1);
    function fBound () {
      var restParams = [].slice.call(arguments);
      var allParams = boundParams.concat(restParams)
      return boundTargetFunc.apply(this instanceof fBound ? this : boundThis, allParams)
    }
    fBound.prototype = Object.create(boundTargetFunc.prototype || Function.prototype)
    return fBound
  }