日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

作者:一川來源:前端萬有引力

Javascript的New、Apply、Bind、Call知多少

 

1 寫在前面

JAVAscript中的Apply、call、bind方法是前端代碼開發中相當重要的概念,并且與this的指向密切相關。本篇文章我們將深入探討這個關鍵詞的作用,并嘗試進行手動編寫復現。

閱讀文章前,我們帶著幾個問題進行研究:

  • 用什么樣的思路可以new關鍵字?
  • apply、bind、call這三個方法有什么區別?
  • 怎樣手動實現一個apply、bind和call?

2 new關鍵詞

new關鍵詞的作用是執行一個構造函數,返回一個實例對象,根據構造函數的情況來確定是否可以接受參數的傳遞。

2.1 new的原理

使用new進行實例化對象,其步驟是:

  1. 創建一個新的空對象,即{}
  2. 將該對象構造函數的作用域賦給新對象,this指向新對象(即將新對象作為this的上下文)
  3. 執行構造函數中的代碼,為這個新對象添加屬性
  4. 如果該對象構造函數沒有返回對象,則返回this
function Person(){ 
 this.name = "yichuan" 
} 
 
const p = new Person(); 
console.log(p.name);//"yichuan" 

我們可以看到當使用new進行實例化時,可以將構造函數的this指向新對象p。當不使用new時,此時構造函數的this指向window。

function Person(){ 
 this.name = "yichuan" 
} 
 
const p = Person(); 
console.log(p);//undefined 
console.log(name);//"yichuan"   window.name 
console.log(p.name);//"yichuan" is undefined 

當我們在構造函數中直接返回一個和this無關的對象時,使用new關鍵字進行實例化對象,新生成的對象就是構造函數返回的對象,而非構造函數的this的對象。

function Person(){ 
 this.name = "yichuan"; 
  return {age:18}; 
} 
 
const p = new Person(); 
console.log(p);//{age:18} 
console.log(p.name);//"undefined" 
console.log(p.age);/18 

此外,當構造函數返回的不是一個對象,而是基礎數據類型的值時,使用new創建新對象,會將構造函數返回的值以對象形式給新對象。

function Person(){ 
 this.name = "yichuan"; 
  return "onechuan"; 
} 
 
const p = new Person(); 
console.log(p);//{name:"yichuan"} 
console.log(p.name);//"yichuan" 

new關鍵詞執行之后總是會返回一個對象,要么是實例,要么是return語句指定的對象。

2.2 手寫new的實現

new被調用后大致做了哪些事情?

  1. 讓實例可以訪問私有屬性
  2. 讓實例可以訪問構造函數原型(constructor.prototype)所在原型鏈上的屬性
  3. 構造函數返回的最后結果是引用數據類型
function new_object(ctor,...args){ 
 //先要判斷ctor是否為一個函數 
  if(typeof ctor !== "function"){ 
   throw "ctor must be a function"; 
  } 
  //創建一個空對象 
  const obj = new Object(); 
  //將實例obj可以訪問到ctor原型所在原型鏈的屬性 
  obj.__proto__ = Object.create(ctor.prototype); 
  //將構造函數的this指向實例對象obj 
  const res = ctor.apply(obj,...args); 
  //確保最后new返回的是一個對象 
  const isObject = typeof res === "object" && typeof res!== null; 
  const isFunction = typeof res === "function"; 
  return isObject || isFunction ? res : obj; 
} 

當然,我們還可以進行優化以下:

function new_object() { 
  // 1、獲得構造函數,同時刪除 arguments 中第一個參數 
  const ctor = [].shift.call(arguments);//其實這里是借用了數組的shift方法 
  // 2、創建一個空的對象并鏈接到原型,obj 可以訪問構造函數原型中的屬性 
  const obj = Object.create(ctor.prototype); 
  // 3、綁定 this 實現繼承,obj 可以訪問到構造函數中的屬性 
  const ret =ctor.apply(obj, arguments); 
  // 4、優先返回構造函數返回的對象 
  return ret instanceof Object ? ret : obj; 
}; 

3 apply、bind以及call

apply、bind和call是掛載Function對象上的三個方法,調用這三個方法的必須是一個函數。

3.1 apply

apply() 方法調用一個具有給定 this 值的函數,以及作為一個數組(或類似數組對象)提供的參數。apply()方法可以改變函數this的指向,且立即執行函數。

注意:Chrome 14 以及 Internet Explorer 9 仍然不接受類數組對象。如果傳入類數組對象,它們會拋出異常。

func.apply(thisArg, [param1,param2,...]); 

在使用apply時,會將func的this指向改變為指向thisArg,然后以[param1,param2,...]參數數組作為參數輸入。

func(["red","green","blue"]); 
func.apply(newFun, ["red","green","blue"]); 

我們可以看到都執行func時,第一個func函數的this指向的是window全局對象,而第二個func函數的this指向的是newFun。

Function.prototype.apply = function (context, arr) { 
    context = context ? Object(context) : window;  
    context.fn = this; 
   
    let result; 
   //判斷有沒有參數數組輸入 
    if (!arr) { 
        result = context.fn(); 
    } else { 
        result = context.fn(...arr); 
    } 
   //此處也可以使用eval進行處理 
   // const result = eval("context.fn(...arr)"); 
       
    delete context.fn 
    return result; 
} 

3.2 bind

bind() 方法創建一個新的函數,在 bind() 被調用時,這個新函數的 this 被指定為 bind() 的第一個參數,而其余參數將作為新函數的參數,供調用時使用。

bind(thisArg,param1,param2,...); 

其實,bind的實現思路基本和apply一致,但是在最后實現返回結果時,bind不需要直接執行,而是以返回函數的形式返回結果,之后再通過執行這個結果即可。

先分析下bind的特性:首先是指定新對象this指向,再傳入參數返回一個定義的函數,最后使用柯里化進行調用。同樣的,我們也可以根據這些特性進行手動封裝一個bind函數:

Function.prototype.bind = function(context){ 
 //先要判斷調用bind函數的是不是函數,需要拋出異常 
  if(typeof this !== "function"){ 
   throw new Error("this bind function must be userd to function"); 
  } 
  //存儲this的指向 
  const self = this; 
  //context是新對象this指向的目標對象,而參數就是在第一個參數之后的參數 
  const args = Array.prototype.slice.call(arguments,1); 
   
  //創建一個空對象 
  const fun = function(){} 
   
  //返回一個函數 
  const funBind = function(){ 
   //返回所有的參數給bind函數 
    const bindArg = Array.prototype.slice.call(arguments); 
    //將傳入的參數合并成一個新的參數數組,作為self.apply()的第二個參數 
    return self.apply(this instanceof fun ? this : context,  args.concat(bindArgs)); 
    /**********************說明************************************/ 
  } 
   
  //空對象的原型指向綁定函數的原型 
  fun.prototype = this.prototype; 
  //空對象的實例賦值給 funBind.prototype 
  funBind.prototype = new fun(); 
  return funBinf; 
} 

補充說明:

  • this instanceof fun返回為true時,表示的是fun是一個構造函數,其this指向實例,直接將context作為參數輸入
  • this instanceof fun返回為false時,表示的是fun是一個普通函數,其this指向頂級對象window,將綁定函數的this指向context對象

當然,我們也可以寫成這種形式:

Function.prototype.bind = function(context,...args){ 
 //先要判斷調用bind函數的是不是函數,需要拋出異常 
  if(typeof this !== "function"){ 
   throw new Error("this bind function must be userd to function"); 
  } 
  //存儲this的指向 
  const self = this; 
   
  const fBind = function(){ 
   self.apply(this instanceof self ? this: context, args.concat(Array.prototype.slice.call(arguments)));  
  } 
  if(this.prototype){ 
   fBind.prototype = Object.create(this.prototype); 
  } 
  return fBind; 
} 

注意:Object.create()是es2015語法引入的新特性,因此在IE<9的瀏覽器是不支持的。

3.3 call

call() 方法使用一個指定的 this 值和單獨給出的一個或多個參數來調用一個函數。使用調用者提供的 this 值和參數調用該函數的返回值。若該方法沒有返回值,則返回 undefined。

function.call(thisArg, param1, param2, ...) 

注意:該方法的語法和作用與 apply() 方法類似,只有一個區別,就是 call() 方法接受的是一個參數列表,而 apply() 方法接受的是一個包含多個參數的數組。

call函數的實現:

Function.prototype.call = function(context,...args){ 
  //將函數設置為對象的屬性 
 context = context || window; 
  context.fn = this; 
   
  //執行函數 
  const result = eval("context.fn(...args)"); 
  //刪除對象的這個屬性 
  delete context.fn; 
  return result; 
} 

4 參考文章

  • 《解析 bind 原理,并手寫 bind 實現》
  • 《解析 call/apply 原理,并手寫 call/apply 實現》
  • 《JavaScript核心原理精講》

5 寫在最后

在這篇文章中,我們知道apply、bind、call的區別在于:

  • apply、call改變了this指向后,會立即進行調用函數,返回的是執行結果
  • bind在改變this指向后,返回的是一個函數,需要另外再進行調用一次
  • bind、call傳遞的第一個參數都是this將要指向的對象,后面都是一個一個參數的形式輸入
  • apply傳遞的第一個參數也是this將要指向的對象,后面傳遞的第二個參數是一個參數數組

分享到:
標簽:Javascript
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定