当前位置:网站首页>JS interviewer wants to know how much you understand call, apply, bind no regrets series
JS interviewer wants to know how much you understand call, apply, bind no regrets series
2022-07-03 00:02:00 【JackieDYH】
call,apply,bind Basic introduction of
grammar :
fun.call(thisArg, param1, param2, ...)
fun.apply(thisArg, [param1,param2,...])
fun.bind(thisArg, param1, param2, ...)
- 1.
- 2.
- 3.
Return value :
call/apply:
fun Results of execution bind: return fun A copy of the , And have a designated this Values and initial parameters
Parameters
thisArg( Optional ):
-
fun Of this Point to thisArg object- Non strict mode :thisArg Designated as null,undefined,fun Medium this Point to window object .
- In strict mode :
fun Of this by undefined- Value is the original value ( Numbers , character string , Boolean value ) Of this An auto-wrapper object that points to the original value , Such as String、Number、Boolean
param1,param2( Optional ): Pass to fun Parameters of .
- If param Not to pass or for null/undefined, No parameters need to be passed in .
- apply The second parameter is an array , The value in the array is passed to
fun Parameters of .
call call/apply/bind Must be a function
call、apply and bind Is hanging on Function Three methods on the object , Only functions have these methods .
As long as it's a function , such as :
Object.prototype.toString It's just a function , We often see this usage :Object.prototype.toString.call(data)
effect :
Change function execution time this Point to , All about their use at the moment , It's all based on this .
How not to mix up call and apply
Mix up the two API There are not a few , Don't look down on this problem , Just remember the following method .
apply In order to a start , It goes to fun The parameter is Array, Also with a At the beginning .
difference :
call And apply The only difference
Pass to fun The parameters of are written differently :
-
apply It's No 2 Parameters , This parameter is an array : Pass to fun Parameters are written in the array .-
call From 2~n All the parameters of are passed to fun Of .
call/apply And bind The difference between
perform :
- call/apply Changed the this Immediately after the context Execute this function
- bind It returns the function after changing the context , Do not execute the function
Return value :
- call/apply return
fun The results of the implementation of- bind return fun A copy of the , And designated fun Of this Point to , Save the fun Parameters of .
The return value is below bind There are detailed example parsing in the application .
call/apply/bind Core concept of : borrowing methods
See a great example :
In life :
I usually don't have time to cook , I'd like to stew a pickle for my children at the weekend . But there is no suitable pot , And I don't want to go out and buy . So I asked my neighbor to borrow a pot to use , In this way, the goal is achieved , And saved money , Kill two birds with one stone .
In the program :
A The object has a way ,B Objects need to use the same method for some reason , Then we are alone for B Object extension a method , Let's borrow it A How about the method of object ?
Borrowing, of course A The object's method , It has achieved the goal , It saves memory .
This is it. call/apply/bind Core concept of : borrowing methods .
With the help of realized methods , Change the this Point to , Reduce duplicate code , Save memory .
call and apply Application scenarios of :
These application scenarios , With more experience, we can find that their ideas are : borrowing methods
- Judge data type :
Object.prototype.toString It's the best way to judge the type , With it we can judge almost all kinds of data :
function isType(data, type) {
const typeObj = {
'[object String]': 'string',
'[object Number]': 'number',
'[object Boolean]': 'boolean',
'[object Null]': 'null',
'[object Undefined]': 'undefined',
'[object Object]': 'object',
'[object Array]': 'array',
'[object Function]': 'function',
'[object Date]': 'date', // Object.prototype.toString.call(new Date())
'[object RegExp]': 'regExp',
'[object Map]': 'map',
'[object Set]': 'set',
'[object HTMLDivElement]': 'dom', // document.querySelector('#app')
'[object WeakMap]': 'weakMap',
'[object Window]': 'window', // Object.prototype.toString.call(window)
'[object Error]': 'error', // new Error('1')
'[object Arguments]': 'arguments',
}
let name = Object.prototype.toString.call(data) // To borrow Object.prototype.toString() Get data type
let typeName = typeObj[name] || ' Unknown type ' // Match data type
return typeName === type // Determine whether the data type is the incoming type
}
console.log(
isType({}, 'object'), // true
isType([], 'array'), // true
isType(new Date(), 'object'), // false
isType(new Date(), 'date'), // true
)
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- Class array borrows array methods :
Class array because it's not a real array, all kinds of methods are not included in the array type , So we need to borrow the method of array .
For example, borrowing arrays push Method :
var arrayLike = {
0: 'OB',
1: 'Koro1',
length: 2
}
Array.prototype.push.call(arrayLike, ' Additive elements 1', ' Additive elements 2');
console.log(arrayLike) // {"0":"OB","1":"Koro1","2":" Additive elements 1","3":" Additive elements 2","length":4}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- apply Get the maximum and minimum value of the array :
apply Pass the array directly as the parameter of the method to be called , Also save a step to expand the array , For example, use Math.max、Math.min To get the maximum value of the array / minimum value :
const arr = [15, 6, 12, 13, 16];
const max = Math.max.apply(Math, arr); // 16
const min = Math.min.apply(Math, arr); // 6
- 1.
- 2.
- 3.
- Inherit
ES5 The inheritance of is also realized by borrowing the construction method of the parent class / Inheritance of attributes :
// Parent class
function supFather(name) {
this.name = name;
this.colors = ['red', 'blue', 'green']; // Complex type
}
supFather.prototype.sayName = function (age) {
console.log(this.name, 'age');
};
// Subclass
function sub(name, age) {
// Borrow the method of the parent class : Modify its this Point to , The method in the constructor of the assignment parent class 、 Attribute to subclass
supFather.call(this, name);
this.age = age;
}
// Override subclass's prototype, correct constructor Point to
function inheritPrototype(sonFn, fatherFn) {
sonFn.prototype = Object.create(fatherFn.prototype); // Inherit the properties and methods of the parent class
sonFn.prototype.constructor = sonFn; // correct constructor Point to the inherited function
}
inheritPrototype(sub, supFather);
sub.prototype.sayAge = function () {
console.log(this.age, 'foo');
};
// Instantiate subclasses , You can find the properties... On the instance 、 Method
const instance1 = new sub("OBKoro1", 24);
const instance2 = new sub(" Xiao Ming ", 18);
instance1.colors.push('black')
console.log(instance1) // {"name":"OBKoro1","colors":["red","blue","green","black"],"age":24}
console.log(instance2) // {"name":" Xiao Ming ","colors":["red","blue","green"],"age":18}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
There are many similar application scenarios , I won't go into that , The point is that they borrow the idea of the method , If you don't understand, please read it several times .
call、apply, With which ?、
call,apply The effect is exactly the same , The difference between them is
- The number of arguments / Use... When the order is determined call, The number of arguments / If the order is uncertain, use apply.
- Consider readability : Use only a few parameters call, If there are many parameters , Integrate parameters into an array , Use apply.
- If the parameter set is already an array , use apply, For example, get the maximum value of the array / minimum value .
The number of arguments / If the order is uncertain, use apply, For example, the following example :
const obj = {
age: 24,
name: 'OBKoro1',
}
const obj2 = {
age: 777
}
callObj(obj, handle)
callObj(obj2, handle)
// The number of parameters to pass depends on certain conditions 、 And the order
function callObj(thisAge, fn) {
let params = []
if (thisAge.name) {
params.push(thisAge.name)
}
if (thisAge.age) {
params.push(thisAge.age)
}
fn.apply(thisAge, params) // The quantity and order are uncertain Out of commission call
}
function handle(...params) {
console.log('params', params) // do some thing
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
bind Application scenarios of :
1. Save function parameters :
First, let's look at the next classic interview question :
for (var i = 1; i <= 5; i++) {
setTimeout(function test() {
console.log(i) // Output... In sequence :6 6 6 6 6
}, i * 1000);
}
- 1.
- 2.
- 3.
- 4.
- 5.
The reason for this is to wait until setTimeout Asynchronous execution ,i Has become a 6 了 .
So how to make him output : 1,2,3,4,5 Well ?
There are many ways :
- Closure , Save variables
for (var i = 1; i <= 5; i++) {
(function (i) {
setTimeout(function () {
console.log(' Closure :', i); // Output... In sequence :1 2 3 4 5
}, i * 1000);
}(i));
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
Create a closure here , Every cycle will turn i The latest value of , Then it's saved by the closure .
- bind
for (var i = 1; i <= 5; i++) {
// Cache parameters
setTimeout(function (i) {
console.log('bind', i) // Output... In sequence :1 2 3 4 5
}.bind(null, i), i * 1000);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
In fact, closures are also used here , We know bind It returns a function , This function is also a closure .
It holds the this Point to 、 Initial parameter , Every time i The changes will be bind The closure of the , So the output 1-5.
Specific details , Here's a handwritten bind Method , Look at the , You can understand .
-
let
use let Statement i You can also output 1-5: because let It's a block level scope , So every time a new variable is created , therefore setTimeout The value of each reading is different .
2. Callback function this Lost problems :
This is a common problem , Here's what I'm developing VSCode Plug in processing webview When communication , The real problem , At first I thought VSCode Of API Where's the problem , After some debugging, I found that it was this Point to the missing problem .
class Page {
constructor(callBack) {
this.className = 'Page'
this.MessageCallBack = callBack //
this.MessageCallBack(' Information sent to registration page ') // perform PageA Callback function for
}
}
class PageA {
constructor() {
this.className = 'PageA'
this.pageClass = new Page(this.handleMessage) // Registration page Pass the callback function Here's the problem
}
// Communicate callback with page
handleMessage(msg) {
console.log(' Handling communications ', this.className, msg) // 'Page' this Pointing error
}
}
new PageA()
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
Callback function this Why is it lost ?
Obviously there won't be a problem with the statement , There is no problem executing the callback function .
The problem is when passing the callback function :
this.pageClass = new Page(this.handleMessage)
- 1.
Because passing on the past this.handleMessage Is a function memory address , There is no context object , That is to say, the function is not bound to it this Point to .
Then it's this Point to the binding rules it applies :
class Page {
constructor(callBack) {
this.className = 'Page'
// callBack() // Direct execution because class Inside is the strict pattern , therefore this The actual point is undefined
this.MessageCallBack = callBack // Of the callback function this Implicitly bound to class page
this.MessageCallBack(' Information sent to registration page ')
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
Now that you know the problem , So we just bind the callback function this Pointing to PageA That's the solution .
Callback function this Lost solution :
-
bind Bound to callback function this Point to :
This is typical bind Application scenarios of , binding this Point to , Used as a callback function .
this.pageClass = new Page(this.handleMessage.bind(this)) // Bound to callback function this Point to
- 1.
PS: That's why react Of render Function when Binding callback function , Also use bind Bind it this The direction of , It's also because of the same problems and principles .
- Arrow function binding this Point to
Arrowhead function this Point to the definition of the first ordinary function in the outer layer this, What I mean here is class class :PageA
this.pageClass = new Page(() => this.handleMessage()) // Arrow function binding this Point to
- 1.
Intermediate and advanced interview questions - Handwriting call/apply、bind:
In an interview with a big factory , Handwritten implementation call,apply,bind( especially bind) It's always been a high frequency interview question , Let's implement these functions together .
You can write a
call Do you ?
Ideas
- according to call The rules set the context object , That is to say
this The direction of .- By setting
context Properties of , Will function this Point to implicitly bind to context On- Execute functions by implicit binding and pass parameters .
- Delete temporary properties , Return function execution result
Function.prototype.myCall = function (context, ...arr) {
if (context === null || context === undefined) {
// Designated as null and undefined Of this The value will automatically point to the global object ( Browser for window)
context = window
} else {
context = Object(context) // Value is the original value ( Numbers , character string , Boolean value ) Of this Will point to the instance object of the original value
}
const specialPrototype = Symbol(' Special properties Symbol') // Used to temporarily store functions
context[specialPrototype] = this; // Functional this Point to implicitly bind to context On
let result = context[specialPrototype](...arr); // Execute functions by implicit binding and pass parameters
delete context[specialPrototype]; // Delete the properties of the context object
return result; // Return function execution result
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
Determine the context object of the function :
Many people judge function context objects , It's just simple
context Is it false To judge , such as :
// The judgment function context is bound to `window` Not rigorous enough
context = context ? Object(context) : window;
context = context || window;
- 1.
- 2.
- 3.
After testing , The following three are false The situation of , The context objects of the function are bound to
window On :
// Other schemes for binding function context objects on the Internet : context = context || window;
function handle(...params) {
this.test = 'handle'
console.log('params', this, ...params) // do some thing
}
handle.elseCall('') // window
handle.elseCall(0) // window
handle.elseCall(false) // window
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
and
call The context object of the function will be bound to the instance object of these original values :

So the right solution , It should be like what I did above :
// Correctly judge the function context object
if (context === null || context === undefined) {
// Designated as null and undefined Of this The value will automatically point to the global object ( Browser for window)
context = window
} else {
context = Object(context) // Value is the original value ( Numbers , character string , Boolean value ) Of this Will point to the instance object of the original value
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
Use Symbol Temporary storage function
Although the attribute used before is
testFn But I have to admit , There is still a risk of conflict with the original attributes of the context object , Remind by netizens to use Symbol There will be no conflict .Considering compatibility , Or use as special attributes as possible , Take your own ID:
OBKoro1TestFn.You can write a
apply Do you ?
Ideas :
- Parameter handling passed to the function , Not quite the same. , Other parts follow
call equally .-
apply Accept the second parameter as the class array object , This is used here. JavaScript Authoritative guide to determine whether it is an array of class objects .
Function.prototype.myApply = function (context) {
if (context === null || context === undefined) {
context = window // Designated as null and undefined Of this The value will automatically point to the global object ( Browser for window)
} else {
context = Object(context) // Value is the original value ( Numbers , character string , Boolean value ) Of this Will point to the instance object of the original value
}
// JavaScript Authoritative guide to determine whether it is an array of class objects
function isArrayLike(o) {
if (o && // o No null、undefined etc.
typeof o === 'object' && // o It's the object
isFinite(o.length) && // o.length It's a finite number
o.length >= 0 && // o.length It's nonnegative
o.length === Math.floor(o.length) && // o.length Is an integer
o.length < 4294967296) // o.length < 2^32
return true
else
return false
}
const specialPrototype = Symbol(' Special properties Symbol') // Used to temporarily store functions
context[specialPrototype] = this; // Implicit binding this Point to context On
let args = arguments[1]; // Get parameter array
let result
// Handle the second parameter passed in
if (args) {
// Whether to pass the second parameter
if (!Array.isArray(args) && !isArrayLike(args)) {
throw new TypeError('myApply The second parameter is not an array and an error is not thrown for the class array object ');
} else {
args = Array.from(args) // Converted to an array
result = context[specialPrototype](...args); // Execute the function and expand the array , Transfer function parameters
}
} else {
result = context[specialPrototype](); // Execute function
}
delete context[specialPrototype]; // Delete the properties of the context object
return result; // Return function execution result
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
Focus on :
Handwriting
bind It's a high frequency interview question in a large factory , If the middle and senior front end of the interview , Just be able to tell the difference , Usage doesn't stand out , Understanding needs to be deep enough to hold offer return !
Ideas
- Copy source function :
- Store source functions by variables
- Use
Object.create Copy the prototype to fToBind
- Return the copied function
- Call the copied function :
- new Call judgment : adopt
instanceof Judge whether the function passes new call , To determine the binding context- binding this+ Pass parameters
- Returns the execution result of the source function
The repair function does not prototype The situation of
Function.prototype.myBind = function (objThis, ...params) {
const thisFn = this; // Store the source function and the above params( Function parameter )
// For the returned function secondParams Second pass
let fToBind = function (...secondParams) {
const isNew = this instanceof fToBind // this Whether it is fToBind Example That is to say, the returned fToBind Whether to pass new call
const context = isNew ? this : Object(objThis) // new The call is bound to this On , Otherwise, it will be bound to the incoming objThis On
return thisFn.call(context, ...params, ...secondParams); // use call Call source function binding this Point to and pass parameters to , Return execution result
};
if (thisFn.prototype) {
// Copy the prototype to fToBind In some cases, the function does not prototype, For example, arrow function
fToBind.prototype = Object.create(thisFn.prototype);
}
return fToBind; // Return the copied function
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
The object abbreviation method does not prototype
The arrow function doesn't
prototype, This I know , But getInfo2 Is an abbreviation , Why not? prototype.Google /
stack overflow There's no reason
var student = {
getInfo: function (name, isRegistered) {
console.log('this1', this)
},
getInfo2(name, isRegistered) {
console.log('this2', this) // No, prototype
},
getInfo3: (name, isRegistered) => {
console.log('this3', this) // No, prototype
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
边栏推荐
- How can cross-border e-commerce achieve low-cost and steady growth by laying a good data base
- 35 pages dangerous chemicals safety management platform solution 2022 Edition
- How to set automatic reply for mailbox and enterprise mailbox?
- Matlab 信号处理【问答笔记-1】
- Yolox enhanced feature extraction network panet analysis
- C MVC creates a view to get rid of the influence of layout
- How much do you know about synchronized?
- JVM foundation review
- Analyze ad654: Marketing Analytics
- ArrayList analysis 2: pits in ITR, listiterator, and sublist
猜你喜欢

Highly available cluster (HAC)

Is the multitasking loss in pytoch added up or backward separately?

What is the official website address of e-mail? Explanation of the login entry of the official website address of enterprise e-mail

Create an interactive experience of popular games, and learn about the real-time voice of paileyun unity

Interface switching based on pyqt5 toolbar button -2

JDBC教程

Optimization of streaming media technology
![[shutter] shutter open source project reference](/img/3f/b1d4edd8f8e8fd8e6b39548448270d.jpg)
[shutter] shutter open source project reference

Happy Lantern Festival, how many of these technical lantern riddles can you guess correctly?

Bean加载控制
随机推荐
QT 如何将数据导出成PDF文件(QPdfWriter 使用指南)
可知论与熟能生巧
Intranet penetration | teach you how to conduct intranet penetration hand in hand
JDBC教程
数据集-故障诊断:西储大学轴承的各项数据以及数据说明
95 pages of smart education solutions 2022
PR FAQ, what about PR preview video card?
Open Source | Wenxin Big Model Ernie Tiny Lightweight Technology, Accurate and Fast, full Open Effect
Maybe you read a fake Tianlong eight
Remote connection of raspberry pie by VNC viewer
MySQL advanced learning notes (III)
Ideal car × Oceanbase: when the new forces of car building meet the new forces of database
Load balancing cluster (LBC)
Bean load control
Convolution和Batch normalization的融合
I've been interviewed. The starting salary is 16K
Convolution和Batch normalization的融合
Practical series - free commercial video material library
Realization of mask recognition based on OpenCV
富滇银行完成数字化升级|OceanBase数据库助力布局分布式架构中台