摘要:大致就是這樣所以可以模擬實現和。這些參數作為的第二個參數跟在后面,之后它們會被插入到目標函數的參數列表的開始位置,傳遞給綁定函數的參數會跟在它們的后面。
一個前端知識點匯總call和apply綜合了學習過程中的知識點,比如this、閉包、BFC、ES6等,如果大佬們覺得還可以的話,求個star啦!
每個函數都包含兩個非繼承而來的方法:apply()和call()
用途相同,都是在特定的作用域中調用函數
接收參數方面不同,apply接收兩個參數,一個是函數運行時的作用域(this),另一個是參數數組;call方法第一個參數與apply方法相同,但傳遞給函數的參數必須列舉出來。
call()方法調用一個函數,其具有一個指定的this值和分別提供的參數:
fun.call(thisArg, arg1, arg2, ...)
apply()方法調用一個函數,其具有一個指定的this值,以及作為一個數組(或類似數組的對象)提供的參數:
fun.apply(thisArg, [argsArray])
舉個栗子???:
var one = { name: "one", sayName: function(age) { console.log(`Hello, I"m ${this.name}, and I"m ${age} years old`) } } var day = { name: "day" } one.sayName.call(day, 20) // Hello, I"m day, and I"m 20 years old one.sayName.apply(day, [20]) // Hello, I"m day, and I"m 20 years old
fn.call(o)的原理就是先通過o.m = fn將fn作為o的某個臨時屬性m存儲,然后執行m,執行完畢后將m屬性刪除。
大致就是這樣:
day.fn = one.sayName day.fn() delete day.fn
所以可以模擬實現apply和call。
首先來看apply的模擬:
第一版Function.prototype.applyOne = function() { var context = arguments[0] var args = arguments[1] context.fn = this eval(context.fn(args.join(","))) // args.join(",")返回的是string,所以需要進行一下特殊處理) delete context.fn } one.sayName.applyOne(day, [20]) // Hello, I"m day, and I"m 20 years old第二版
要注意到的是,若this傳入的是null,或者不傳入,則會默認是全局環境,并且apply是有返回值的。
Function.prototype.applyTwo = function() { var context = arguments[0] || window var args = arguments[1] context.fn = this if (args == void 0) { return context.fn() } var result = eval(context.fn(args.join(","))) // args.join(",")返回的是string,所以需要進行一下特殊處理 delete context.fn return result } var name = "oneday" var one = { name: "one", sayName: function(age) { console.log(`Hello, I"m ${this.name}, and I"m ${age} years old`) } } var day = { name: "day" } one.sayName.applyTwo(null, [20]) // Hello, I"m oneday, and I"m 20 years old
emmmm...有一個問題就是萬一context里面本來就有fn屬性怎么辦呢...對于es6而言,可以將fn設置為一個獨特的Symbol值,如下:
var fn1 = Symbol("aaa") var fn2 = Symbol("aaa") fn1 == fn2 // false
但是畢竟symbol是es6的特性啊,所以在es5中可以使用產生隨機數的方法,例如:
var fn1 = "o" + Math.random() var fn2 = "o" + Math.random()
接下來就是apply:
Function.prototype.callOne = function() { var context = [].shift.applyTwo(arguments) var args = [].slice.applyTwo(arguments) // 將剩下的參數作為數組傳入啊 return this.applyTwo(context, args) }
emmmm...第一個參數就是arguments的第一個(一般是this,或者沒有),后面的參數就作為數組形式傳入。
bind方法bind方法創建一個新的函數,當被調用時,this值是傳遞給bind的第一個參數,它的參數是bind其他的參數和其原本的參數,返回的是由指定的this值和初始化參數改造的原函數拷貝。
fun.bind(thisArg[, arg1[, arg2[, ...]]])
實例:
var name = "2333" function Person(name) { this.name = name this.sayName = function() { setTimeout(function() { console.log(`Hello, I"m ${this.name}`) }, 1000) } } var oneday = new Person("1111") oneday.sayName() // Hello, I"m 2333
但是下面這樣就是1111~
this.sayName = function() { setTimeout(function() { console.log(`Hello, I"m ${this.name}`) }.bind(this), 1000) } var oneday = new Person("1111") oneday.sayName() // Hello, I"m 1111
而且還有偏函數(Partial Functions),在mdn中是這么說的:
bind()的另一個最簡單的用法是使一個函數擁有預設的初始參數。這些參數作為bind()的第二個參數跟在this后面,之后它們會被插入到目標函數的參數列表的開始位置,傳遞給綁定函數的參數會跟在它們的后面。
emmmm...對呀沒看懂,于是就看例子啊...
function list() { return Array.prototype.slice.call(arguments) } var list1 = list(1, 2, 3) // [1, 2, 3] // 所以listFun是擁有預設參數(5, 6)的,作為預設參數跟在第一個參數this后面 var listFun = list.bind(undefined, 5, 6) // 后面傳入的參數會跟在預設參數的后面 var list2 = listFun(7) // [5, 6, 7] var list3 = listFun(8, 9) // [5, 6, 8, 9]
bind的模擬實現:
第一版Function.prototype.bindOne = function() { var me = this // 緩存this var argArray = Array.prototype.slice.call(arguments, 1) return function() { return me.apply(arguments[0], argArray) } }
但是上述的沒有實現繼續傳參可以添加到原參數后的功能...所以有了第二版
第二版Function.prototype.bindTwo = function() { var context = arguments[0] var me = this var argArray = Array.prototype.slice.call(arguments, 1) return function() { var innerArgs = Array.prototype.slice.call(arguments) var finalArgs = argArray.concat(innerArgs) return me.apply(context, finalArgs) } }
bind返回的函數如果作為構造函數,這個構造函數搭配new關鍵字出現的話,bind綁定的this需要被忽略,但是參數還要繼續傳入。意思就是bind的綁定比new的優先級要低。而且要在函數體內判斷調用bind方法的一定要是一個函數。
復習一下new的作用:
創建一個新對象
新對象繼承了該函數的原型(因此this就指向了這個新對象)
為這個新對象添加屬性和方法并返回這個新對象
var obj = {} obj.__proto__ = Base.prototype Base.call(obj)第三版
Function.prototype.bindThree = function() { if (typeof this !== "function") { throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable") } // context指要把this綁定到的目標函數 var context = arguments[0] // 這里的this指向調用bind的函數 var me = this var argArray = Array.prototype.slice.call(arguments, 1) var F = function() {} F.prototype = this.prototype var bound = function() { var innerArgs = Array.prototype.slice.call(arguments) var finalArgs = argArray.concat(innerArgs) // 如果調用bind的函數是F的實例,那么this就還是指向調用bind的函數,如果不是F的實例,那么this就進行改變 return me.apply(this instanceof F ? this : context, finalArgs) } bound.prototype = new F() return bound }參考:
不用call和apply方法模擬實現ES5的bind方法
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/92401.html
摘要:也就是說當返回的函數作為構造函數的時候,時指定的值會失效,但傳入的參數依然生效。構造函數效果的優化實現但是在這個寫法中,我們直接將,我們直接修改的時候,也會直接修改函數的。 JavaScript深入系列第十一篇,通過bind函數的模擬實現,帶大家真正了解bind的特性 bind 一句話介紹 bind: bind() 方法會創建一個新函數。當這個新函數被調用時,bind() 的第一個參數...
摘要:模擬和模擬一樣,現摘抄下面的代碼添加一個返回值對象然后我們定義一個函數,如果執行下面的代碼能夠返回和函數一樣的值,就達到我們的目的。 原文:https://zhehuaxuan.github.io/... 作者:zhehuaxuan 目的 本文主要用于理解和掌握call,apply和bind的使用和原理,本文適用于對它們的用法不是很熟悉,或者想搞清楚它們原理的童鞋。 好,那我們開始...
摘要:返回的綁定函數也能使用操作符創建對象這種行為就像把原函數當成構造器,提供的值被忽略,同時調用時的參數被提供給模擬函數。 bind() bind() 方法會創建一個新函數,當這個新函數被調用時,它的 this 值是傳遞給 bind() 的第一個參數,傳入bind方法的第二個以及以后的參數加上綁定函數運行時本身的參數按照順序作為原函數的參數來調用原函數。bind返回的綁定函數也能使用 n...
摘要:點擊那么面試官可能會問是否想過到底做了什么,怎么模擬實現呢。另外前不久寫過一篇文章面試官問能否模擬實現的操作符。所以相當于調用時,的返回值函數內部要模擬實現實現的操作。文章中的例子和測試代碼放在中模擬實現。 前言 用過React的同學都知道,經常會使用bind來綁定this。 import React, { Component } from react; class TodoItem ...
摘要:但是三作為構造函數時函數其實還有一個非常重要的特點返回的函數如果作為構造函數,搭配關鍵字出現的話,我們的綁定就需要被忽略。其次,當返回的函數作為構造函數時,之前綁定的會失效。 本文共 1100 字,讀完只需 4 分鐘 概述 前一篇文章我們嘗試模擬實現了 call 和 apply 方法,其實 bind 函數也可以用來改變 this 的指向。bind 和 call和 apply 兩者的區別...
閱讀 1628·2021-09-08 10:42
閱讀 3611·2021-08-11 10:23
閱讀 3982·2019-08-30 14:10
閱讀 2740·2019-08-29 17:29
閱讀 3097·2019-08-29 12:50
閱讀 647·2019-08-26 13:36
閱讀 3463·2019-08-26 11:59
閱讀 1494·2019-08-23 16:23