深入理解js中的bind
阿新 • • 發佈:2018-11-25
/** * bind 函式在js中的應用 */ this.name = "test"; let testObj = { name:'zhangsan', introduce:function(){ return this.name; } } let test = { name:"lisi" } let test1 = { name:"wangwu" } let fn = testObj.introduce; console.log(fn());//test console.log(fn.bind(test)());//lisi console.log(fn.bind(test1)());//王五 console.log(fn());//test 注:不存在永久改變this指向的說法 /** *bind()的另一個最簡單的用法是使一個函式擁有預設的初始引數。這些引數(如果有的話)作為bind()的第二個引數 跟在this(或其他物件)後面,之後它們會被插入到 *目標函式的引數列表的開始位置,傳遞給繫結函式的引數會跟在它們的後面。 */ function list() { return Array.prototype.slice.call(arguments); } var list1 = list(1, 2, 3); // [1, 2, 3] var leadingThirtysevenList = list.bind(undefined, 37,21); var list2 = leadingThirtysevenList(); // [37] var list3 = leadingThirtysevenList(1, 2, 3); // [37,21, 1, 2, 3] var list4 = leadingThirtysevenList(1, 2, 3, 4); // [37,21, 1, 2, 3,4] /** * 來看es6中的函式初始值 */ function EsArguments(name='zhangsan'){ console.log(name); } EsArguments();//zhangsan EsArguments("lisi");//lisi /** * 在自定義物件中的應用 demo * 跟es6中函式的初始值的區別在於不會覆蓋原始值 */ function testBind(){ this.cacheArgs = this.transformArgs.bind(undefined,...arguments); }; testBind.prototype.transformArgs = function(){ return Array.prototype.slice.call(arguments); } testBind.prototype.showArgList = function(){ return this.cacheArgs(...arguments); } let Test = new testBind(1,2,3); console.log(Test.showArgList(13,14)); console.log(Test.showArgList(5,21)); /* bind和call apply的區別在於不會立即呼叫 原因在於 bind() 函式會建立一個新函式(稱為繫結函式), * 新函式與被調函式(繫結函式的目標函式)具有相同的函式體。 * bind一般用在非同步呼叫和事件 * ->下面看bind函式的原生實現方式 */ Function.prototype.bind = function() { var self = this, // 儲存原函式執行上下文中的this指向 context = Array.prototype.shift.call(arguments), // 需要繫結的this上下文 args = Array.prototype.slice.call(arguments); // 剩餘的引數轉成陣列 return function() { //返回一個新函式 // 執行新函式時,將傳入的上下文context作為新函式的this // 並且組合兩次分別傳入的引數,作為新函式的引數 return self.apply(context, Array.prototype.concat.call(args, Array.prototype.slice.call(arguments))); } }; /**bind 官方文件解釋 * 語法 * fun.bind(thisArg[, arg1[, arg2[, ...]]]) * 引數thisArg * 當繫結函式被呼叫時,該引數會作為原函式執行時的 this 指向。當使用new 操作符呼叫繫結函式時,該參 數 無 效。 (注:原因在於new操作會建立一個空物件) * arg1, arg2, ... * 當繫結函式被呼叫時,這些引數將置於實參之前傳遞給被繫結的方法。 * 返回值 * 返回由指定的this值和初始化引數改造的原函式拷貝 */ /** * bind函式在setTimeout中的應用 */ let testBingMethod = { name:'123', test:function(){ console.log(this.name); }, showList:function(){ setTimeout(this.test.bind(this),1000); //第一個this為當前物件執行上文的this //第二個this為setTimeout函式的this指向即window物件 } } testBingMethod.showList() /** * bind作為建構函式使用的繫結函式 * 自然而然地,繫結函式適用於用new操作符 new 去構造一個由目標函式建立的新的例項。當一個繫結函式是用來 構建一個值的,原來提供的 this 就會被忽略。然而, * 原先提供的那些引數仍然會被前置到建構函式呼叫的前面。 <---原因在於new操作會建立一個空物件--> */ function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function() { return this.x + ',' + this.y; }; var p = new Point(1, 2); p.toString(); // '1,2' // 以下這行程式碼在 polyfill 不支援,polyfill下必須指向一個新的物件 // 在原生的bind方法執行沒問題: var YAxisPoint = Point.bind(null, 0/*x*/); var axisPoint = new YAxisPoint(5); axisPoint.toString(); // '0,5' /** *bind 的快捷呼叫 *你可以用 Array.prototype.slice 來將一個類似於陣列的物件(array-like object)轉換成一個真正的陣列,就 拿它來舉例子吧。你可以建立這樣一個捷徑: */ function testFn(){ var slice = Array.prototype.slice; slice.apply(arguments); /* 用 bind()可以使這個過程變得簡單。在下面這段程式碼裡面,slice 是 Function.prototype 的 apply() 方 法的繫結函式,並且將 Array.prototype 的 *slice() 方法作為 this 的值。這意味著我們壓根兒用不著上面那個 apply()呼叫了。 */ var unboundSlice = Array.prototype.slice; var slice = Function.prototype.apply.bind(unboundSlice); slice(arguments); }