call和Apply和Bind

call apply Bind ??

call 和 apply

  1. 介紹
    • 在你指定的作用域中調用函數
    • 實際上等于設置函數體內 this 的值
    • 非繼承而來的方法
  2. 作用
    • 擴充函數的作用 eg.1
    • 更方便的傳遞參數 eg.2
  3. apply參數
    • 參數1: 在其中運行函數的作用域
    • 參數2: 參數數組(類數組對象)
  4. call參數
    • 參數1: 在其中運行函數的作用域
    • 參數2: 一堆參數排排站
  5. 區別
    • 參數2不同
  6. 優點
    • 對象不需要和方法有任何的耦合關系,我們之前經常寫例如 eg 這種,再通過 p 去調用他
  7. 代碼作用舉例
/* eg */
const p = {
  color: "red",
  sayColor: function() {
    console.log(this.color);
  }
};
p.sayColor();

/* eg.1 作用1:擴充作用域 */
global.color = "red";
let o = { color: "blue" };
function sayColor() {
  console.log(this.color);
}
sayColor();  // 'red'
sayColor.call(o);  // 'blue'
// 理解:
// 運行sayColor.call(o)的時候,函數的執行環境變了,因為辭職函數體內的this對象指向了o 

/* eg.2 作用2: 更方便的傳遞參數 */
function sum(a, b) {
  return a + b;
}
function callSum(c, d) {
  // 因為是在全局作用域調用的,this是window(node是global)
  return sum.call(this, c, d);  
}
function applySum1() {
  return sum.apply(this, arguments);
}
function applySum2(c, d) {
  return sum.apply(this, [c, d]);
}


console.log(callSum(1, 2));  // 3
console.log(applySum1(1, 2)); // 3
console.log(applySum2(1, 2));  // 3
 
/* bind用法 */


  1. 實際應用舉例

/* 通過apply 擴展,使得Math.max可以接收數組作為參數 */
Math.max(1, 2, 3);
Math.max.apply(this, [1, 2, 3]);

bind

  • bind方法創建一個新的函數,在bind方法被調用時,這個新函數的this被指定為bind()的第一個參數,而其余參數作為新函數的參數,供調用時使用
  1. 舉例
// eg1.
global.color = "red";
let o = { color: "blue" };

function sayColor() {
  return this.color;
}
sayColor();  // 'red'
sayColor.bind(o)();  // 'blue'

// eg2. bind() 傳遞參數
const foo = {
  value: 1
};

const demo = function(name, age) {
  console.log(this.value);  // 1
  console.log(this.name);   // undefined
  console.log(name);        // "nametest"
  console.log(age);         // 19
};
demo.bind(foo, "nametest")(19);

一個綁定函數也能使用new操作符創建對象,這種行為就像把原函數當作構造器,提供的this值被忽略,同時調用時的參數被當作模擬函數

  • 也就是說,當bind返回的函數當作構造函數的時候,bind時指定的this值會失效,但傳入的參數依然生效
const foo = {
  value: 1
};

function demo(name, age) {
  this.job = "programmer";
  console.log(this.value);   // undefined
  console.log(name);         // "nameTest2"
  console.log(age);          // 19
}
demo.prototype.friend = "huahua";

const bindName = demo.bind(foo, "nameTest2");
const newDemo = new bindName(18); 

console.log(newDemo.friend);  // "huahua"
console.log(newDemo.job);     // "programmer"

使用的new操作符之后,綁定的this已經失效,此時的this指向bindName,

手寫bind: 使用基本類型的擴充實現bind

Function.prototype.method = function(name, func) {
  if (!this.prototype[name]) {
    this.prototype[name] = func;
  }
  return this;
};

Function.method("bind2", function(context, ...args) {
  // 這里this 指向 sayName
  this.apply(context, args);
});

const person = {
  name: "hong",
  sayName: function() {
    console.log(this.name);
  }
};
global.name = "ming";

person.sayName(); // "hong"
person.sayName.bind2(null, 2); // "ming"

手寫bind: 函數柯里化實現一個bind

  • 其實bind就是把this 綁定到傳入的對象上
Function.prototype.bind2 = function(context) {
  // 如果使用bind的不是函數就拋出錯誤
  if (typeof this !== "function") {
    throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
  }

  var self = this;
  var args = Array.prototype.slice.call(arguments, 1);
  var fNOP = function() {};

  var fbound = function() {
    // 當作為構造函數時,this 指向實例,self 指向綁定函數,因為下面一句 `fbound.prototype = this.prototype;`,已經修改了 fbound.prototype 為 綁定函數的 prototype,此時結果為 true,當結果為 true 的時候,this 指向實例。
    // 當作為普通函數時,this 指向 window,self 指向綁定函數,此時結果為 false,當結果為 false 的時候,this 指向綁定的 context。
    self.apply(
      this instanceof self ? this : context,
      args.concat(Array.prototype.slice.call(arguments))
    );
  };

  fNOP.prototype = this.prototype;
  // 如果直接修改fbound 的prototype 也會直接修改函數的prototype, 這時可以使用空函數進行中轉
  fbound.prototype = new fNOP();

  return fbound;
};
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容