摘要:原文地址基礎心法歡迎。也就是說,這三個方法可以改變函數體內部的指向。令為一個空列表。提供作為值并以作為參數列表,調用的內部方法,返回結果。在外面傳入的值會修改并成為值。語法其中,就是指向,是指定的參數。
原文地址:JavaScript基礎心法——call apply bind
歡迎star。
如果有錯誤的地方歡迎指正。
整理call、apply、bind這三個方法的的知識點。
之前這篇文章提到過this的各種情況,其中有一種情況就是通過call、apply、bind來將this綁定到指定的對象上。
也就是說,這三個方法可以改變函數體內部this的指向。
這三個方法有什么區別呢?分別適合應用在哪些場景中呢?
先舉個簡單的栗子 ~
var person = { name: "axuebin", age: 25 }; function say(job){ console.log(this.name+":"+this.age+" "+job); } say.call(person,"FE"); // axuebin:25 FE say.apply(person,["FE"]); // axuebin:25 FE var sayPerson = say.bind(person,"FE"); sayPerson(); // axuebin:25 FE
對于對象person而言,并沒有say這樣一個方法,通過call/apply/bind就可以將外部的say方法用于這個對象中,其實就是將say內部的this指向person這個對象。
callcall是屬于所有Function的方法,也就是Function.prototype.call。
The call() method calls a function with a given this value and arguments provided individually.
call() 方法調用一個函數, 其具有一個指定的this值和分別地提供的參數(參數的列表)。
它的語法是這樣的:
fun.call(thisArg[,arg1[,arg2,…]]);
其中,thisArg就是this指向,arg是指定的參數。
call的用處簡而言之就是可以讓call()中的對象調用當前對象所擁有的function。
ECMAScript規范ECMAScript規范中是這樣定義call的:
當以thisArg和可選的arg1,arg2等等作為參數在一個func對象上調用call方法,采用如下步驟:
如果IsCallable(func)是false, 則拋出一個TypeError異常。
令argList為一個空列表。
如果調用這個方法的參數多余一個,則從arg1開始以從左到右的順序將每個參數插入為argList的最后一個元素。
提供thisArg作為this值并以argList作為參數列表,調用func的[[Call]]內部方法,返回結果。
call方法的length屬性是1。
在外面傳入的thisArg值會修改并成為this值。thisArg是undefined或null時它會被替換成全局對象,所有其他值會被應用ToObject并將結果作為this值,這是第三版引入的更改。
使用call調用函數并且指定thisvar obj = { a: 1 } function foo(b, c){ this.b = b; this.c = c; console.log(this.a + this.b + this.c); } foo.call(obj,2,3); // 6call實現繼承
在需要實現繼承的子類構造函數中,可以通過call調用父類構造函數實現繼承。
function Person(name, age){ this.name = name; this.age = age; this.say = function(){ console.log(this.name + ":" + this.age); } } function Student(name, age, job){ Person.call(this, name ,age); this.job = job; this.say = function(){ console.log(this.name + ":" + this.age + " " + this.job); } } var me = new Student("axuebin",25,"FE"); console.log(me.say()); // axuebin:25 FEapply
apply也是屬于所有Function的方法,也就是Function.prototype.apply。
The apply() method calls a function with a given this value, and arguments provided as an array (or an array-like object).
apply() 方法調用一個函數, 其具有一個指定的this值,以及作為一個數組(或類似數組的對象)提供的參數。
它的語法是這樣的:
fun.apply(thisArg, [argsArray]);
其中,thisArg就是this指向,argsArray是指定的參數數組。
通過語法就可以看出call和apply的在參數上的一個區別:
call的參數是一個列表,將每個參數一個個列出來
apply的參數是一個數組,將每個參數放到一個數組中
ECMAScript規范當以thisArg和argArray為參數在一個func對象上調用apply方法,采用如下步驟:
如果IsCallable(func)是false, 則拋出一個TypeError異常 .
如果argArray是null或undefined, 則
返回提供thisArg作為this值并以空參數列表調用func的[[Call]]內部方法的結果。
如果Type(argArray)不是Object, 則拋出一個TypeError異常 .
令len為以"length"作為參數調用argArray的[[Get]]內部方法的結果。
令n為ToUint32(len).
令argList為一個空列表 .
令index為0.
只要index<n就重復
令indexName為ToString(index).
令nextArg為以indexName作為參數調用argArray的[[Get]]內部方法的結果。
將nextArg作為最后一個元素插入到argList里。
設定index為index + 1.
提供thisArg作為this值并以argList作為參數列表,調用func的[[Call]]內部方法,返回結果。
apply方法的length屬性是 2。
在外面傳入的thisArg值會修改并成為this值。thisArg是undefined或null時它會被替換成全局對象,所有其他值會被應用ToObject并將結果作為this值,這是第三版引入的更改。
用法在用法上apply和call一樣,就不說了。
實現一個apply參考鏈接:https://github.com/jawil/blog/issues/16
第一步,綁定上下文Function.prototype.myApply=function(context){ // 獲取調用`myApply`的函數本身,用this獲取 context.fn = this; // 執行這個函數 context.fn(); // 從上下文中刪除函數引用 delete context.fn; } var obj ={ name: "xb", getName: function(){ console.log(this.name); } } var me = { name: "axuebin" } obj.getName(); // xb obj.getName.myApply(me); // axuebin
確實成功地將this指向了me對象,而不是本身的obj對象。
第二步,給定參數上文已經提到apply需要接受一個參數數組,可以是一個類數組對象,還記得獲取函數參數可以用arguments嗎?
Function.prototype.myApply=function(context){ // 獲取調用`myApply`的函數本身,用this獲取 context.fn = this; // 通過arguments獲取參數 var args = arguments[1]; // 執行這個函數,用ES6的...運算符將arg展開 context.fn(...args); // 從上下文中刪除函數引用 delete context.fn; } var obj ={ name: "xb", getName: function(age){ console.log(this.name + ":" + age); } } var me = { name: "axuebin" } obj.getName(); // xb:undefined obj.getName.myApply(me,[25]); // axuebin:25
context.fn(...arg)是用了ES6的方法來將參數展開,如果看過上面那個鏈接,就知道這里不通過...運算符也是可以的。
原博主通過拼接字符串,然后用eval執行的方式將參數傳進context.fn中:
for (var i = 0; i < args.length; i++) { fnStr += i == args.length - 1 ? args[i] : args[i] + ","; } fnStr += ")";//得到"context.fn(arg1,arg2,arg3...)"這個字符串在,最后用eval執行 eval(fnStr); //還是eval強大第三步,當傳入apply的this為null或者為空時
我們知道,當apply的第一個參數,也就是this的指向為null時,this會指向window。知道了這個,就簡單了~
Function.prototype.myApply=function(context){ // 獲取調用`myApply`的函數本身,用this獲取,如果context不存在,則為window var context = context || window; context.fn = this; //獲取傳入的數組參數 var args = arguments[1]; if (args == undefined) { //沒有傳入參數直接執行 // 執行這個函數 context.fn() } else { // 執行這個函數 context.fn(...args); } // 從上下文中刪除函數引用 delete context.fn; } var obj ={ name: "xb", getName: function(age){ console.log(this.name + ":" + age); } } var name = "window.name"; var me = { name: "axuebin" } obj.getName(); // xb:25 obj.getName.myApply(); // window.name:undefined obj.getName.myApply(null, [25]); // window.name:25 obj.getName.myApply(me, [25]); // axuebin:25第四步 保證fn函數的唯一性
ES6中新增了一種基礎數據類型Symbol。
const name = Symbol(); const age = Symbol(); console.log(name === age); // false const obj = { [name]: "axuebin", [age]: 25 } console.log(obj); // {Symbol(): "axuebin", Symbol(): 25} console.log(obj[name]); // axuebin
所以我們可以通過Symbol來創建一個屬性名。
var fn = Symbol(); context[fn] = this;完整的apply
Function.prototype.myApply=function(context){ // 獲取調用`myApply`的函數本身,用this獲取,如果context不存在,則為window var context = context || window; var fn = Symbol(); context[fn] = this; //獲取傳入的數組參數 var args = arguments[1]; if (args == undefined) { //沒有傳入參數直接執行 // 執行這個函數 context[fn]() } else { // 執行這個函數 context[fn](...args); } // 從上下文中刪除函數引用 delete context.fn; }
這樣就是一個完整的apply了,我們來測試一下:
var obj ={ name: "xb", getName: function(age){ console.log(this.name + ":" + age); } } var name = "window.name"; var me = { name: "axuebin" } obj.getName(); // xb:25 obj.getName.myApply(); // window.name:undefined obj.getName.myApply(null, [25]); // window.name:25 obj.getName.myApply(me, [25]); // axuebin:25
ok 沒啥毛病 ~
再次感謝1024大佬 ~
bindThe bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
bind()方法創建一個新的函數, 當被調用時,將其this關鍵字設置為提供的值,在調用新函數時,在任何提供之前提供一個給定的參數序列。
語法:
fun.bind(thisArg[, arg1[, arg2[, ...]]])
其中,thisArg就是this指向,arg是指定的參數。
可以看出,bind會創建一個新函數(稱之為綁定函數),原函數的一個拷貝,也就是說不會像call和apply那樣立即執行。
當這個綁定函數被調用時,它的this值傳遞給bind的一個參數,執行的參數是傳入bind的其它參數和執行綁定函數時傳入的參數。
用法當我們執行下面的代碼時,我們希望可以正確地輸出name,然后現實是殘酷的
function Person(name){ this.name = name; this.say = function(){ setTimeout(function(){ console.log("hello " + this.name); },1000) } } var person = new Person("axuebin"); person.say(); //hello undefined
這里this運行時是指向window的,所以this.name是undefined,為什么會這樣呢?看看MDN的解釋:
由setTimeout()調用的代碼運行在與所在函數完全分離的執行環境上。這會導致,這些代碼中包含的 this 關鍵字在非嚴格模式會指向 window。
有一個常見的方法可以使得正確的輸出:
function Person(name){ this.name = name; this.say = function(){ var self = this; setTimeout(function(){ console.log("hello " + self.name); },1000) } } var person = new Person("axuebin"); person.say(); //hello axuebin
沒錯,這里我們就可以用到bind了:
function Person(name){ this.name = name; this.say = function(){ setTimeout(function(){ console.log("hello " + this.name); }.bind(this),1000) } } var person = new Person("axuebin"); person.say(); //hello axuebinMDN的Polyfill
Function.prototype.bind = function (oThis) { var aArgs = Array.prototype.slice.call(arguments, 1); var fToBind = this; var fNOP = function () {}; var fBound = function () { fBound.prototype = this instanceof fNOP ? new fNOP() : fBound.prototype; return fToBind.apply(this instanceof fNOP ? this : oThis || this, aArgs ) } if( this.prototype ) { fNOP.prototype = this.prototype; } return fBound; }總結
三者都是用來改變函數的this指向
三者的第一個參數都是this指向的對象
bind是返回一個綁定函數可稍后執行,call、apply是立即調用
三者都可以給定參數傳遞
call給定參數需要將參數全部列出,apply給定參數數組
感謝不用call和apply方法模擬實現ES5的bind方法
深入淺出妙用 Javascript 中 apply、call、bind
回味JS基礎:call apply 與 bind
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/91926.html
摘要:原文地址基礎心法歡迎。作為一個構造函數被綁定到正在構造的新對象。通過構造函數創建一個對象其實執行這樣幾個步驟創建新對象將指向這個對象給對象賦值屬性方法返回所以就是指向創建的這個對象上。 原文地址:JavaScript基礎心法——this 歡迎star。 如果有錯誤的地方歡迎指正。 看看這個有著深不可測的魔力的this到底是個什么玩意兒 ~ 什么是this 在傳統面向對象的語言中,比如...
摘要:前言書接上文細數實用黑科技一本文介紹獨孤九劍和兩篇最高內功心法。可以將變量轉換為布爾值。可以把任何類型的值轉換為布爾值,并且只有當這個變量的值為的時候才會返回,其他情況都返回。同樣的,函數體內部聲明的函數,作用域綁定函數體內部。 showImg(https://segmentfault.com/img/remote/1460000016507838); 前言 書接上文:細數 JavaS...
摘要:首先我們可以通過給目標函數指定作用域來簡單實現方法保存,即調用方法的目標函數考慮到函數柯里化的情況,我們可以構建一個更加健壯的這次的方法可以綁定對象,也支持在綁定的時候傳參。原因是,在中,多次是無效的。而則會立即執行函數。 bind 是返回對應函數,便于稍后調用;apply 、call 則是立即調用 。 apply、call 在 javascript 中,call 和 apply 都是...
摘要:系統,扎實的語言基礎是一個優秀的前端工程師必須具備的。第一個參數為調用函數時的指向,隨后的參數則作為函數的參數并調用,也就是。和的區別只有一個,就是它只有兩個參數,而且第二個參數為調用函數時的參數構成的數組。 系統,扎實的 javascript 語言基礎是一個優秀的前端工程師必須具備的。在看了一些關于 call,apply,bind 的文章后,我還是打算寫下這篇總結,原因其實有好幾個。...
摘要:它代表函數運行時,自動生成的一個內部對象,只能在函數內部使用類似的還有。總結關鍵字就是,誰調用我,我就指向誰。注意由于已經被定義為函數內的一個變量。因此通過關鍵字定義或者將聲明為一個形式參數,都將導致原生的不會被創建。 題目 封裝函數 f,使 f 的 this 指向指定的對象 。 輸入例子 bindThis(function(a, b) { return this.test +...
閱讀 2974·2021-10-27 14:16
閱讀 696·2021-10-13 09:39
閱讀 3671·2021-09-29 09:46
閱讀 2090·2019-08-30 15:54
閱讀 2597·2019-08-30 15:52
閱讀 2994·2019-08-30 15:44
閱讀 1103·2019-08-30 15:44
閱讀 497·2019-08-30 10:51