Js you understand how the front end of the interview call, apply, bind?

Function prototype chain apply, call and bind method is a very important concept in JavaScript, is closely related to this keyword, a considerable part of people's understanding of them is quite simple and obvious, so-called js solid foundation, not open around these common foundation API , this time let's take a thorough grasp them!

table of Contents

  1. call, apply, bind basic introduction
  2. call / apply / bind core philosophy: to borrow methods
  3. call and apply application scenarios
  4. bind application scenarios
  5. Senior face questions: Handwriting call / apply, bind

call, apply, bind basic introduction

grammar:

fun.call(thisArg, param1, param2, ...)
fun.apply(thisArg, [param1,param2,...])
fun.bind(thisArg, param1, param2, ...)

return value:

call / apply: funa result of performing the
bind: Returns funa copy, and has a specified thisvalue and the initial parameter

parameter

thisArg(Optional):

  1. funThe thispoint to thisArgobjects
  2. Non-strict mode: thisArg specified as null, undefined, fun in this points to the window object.
  3. Strict mode: funthe thisisundefined
  4. Value of the original values ​​(numbers, strings, Boolean) this will point to the original value of the automatic packaging of objects, such as String, Number, Boolean

param1,param2(Optional): pass funparameters.

  1. If you do not pass or param is null / undefined, it means do not need to pass any parameters.
  2. apply the second parameter is an array, the array is passed to funa parameter.

Call call/ apply/ bindmust be a function

call, apply and bind three methods are hanging on Function object, these methods have only functions.

As long as you can function, for example: Object.prototype.toStringit is a function, we often see such usage:Object.prototype.toString.call(data)

effect:

at this point to change the function executes, the current use of all about them, it is based on it carried out.

How not to be confused call and aaply

Confuse these two API minority, do not underestimate this problem, remember the following method like this.

applyIs the abeginning, it passed funparameters are Arrayalso to abegin with.

the difference:

The only difference between the call and apply

Pass funparameters different wording:

  • applyIt is the second parameter, which is an array: pass funparameters are written in the array.
  • callFrom 2 ~ n Parameters are passed funto.

call the difference between / apply and bind the

Execution :

  • After the call / apply the function to change the context of this immediate execution of the function
  • bind function is returned after the context has changed, it does not perform the function

Return value :

  • call apply to return / funexecution result
  • bind returns a copy of the fun, and specifies the fun of this point, save the parameters of fun.

The return value of this example resolves detail below bind applications.

call / apply / bind core philosophy: to borrow methods

We see a great example :

in life:

I usually do not have time to cook, weekend, wanted to give the children a stewed pickled Benedict fresh taste. But no suitable pot, and I do not come up to buy. So I asked a neighbor to borrow a pot with, then it will achieve the purpose, but also saves money, do both.

Program:

A object has a method, the object B for some reason need to use the same method, so this time we are alone extend a method for the B object, or is it the object A method borrow it?

A method of course, is the object of it borrowed, not only achieve the purpose, but also saves memory.

This is the call / apply / bind the core idea: borrowing method .

The method has been implemented by the method for changing data in this point, reduce the duplication of code, saving memory.

call and apply application scenarios:

These scenarios, pay more experience can find their philosophy is: borrow methods

  1. Analyzing Data type:

Object.prototype.toStringTo determine the type of a perfect fit, we borrow it can determine almost all types 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) // 借用Object.prototype.toString()获取数据类型
    let typeName = typeObj[name] || '未知类型' // 匹配数据类型
    return typeName === type // 判断该数据类型是否为传入的类型
}
console.log(
    isType({}, 'object'), // true
    isType([], 'array'), // true
    isType(new Date(), 'object'), // false
    isType(new Date(), 'date'), // true
)
  1. Array-based methods borrowed array:

Array of classes for various methods comes on is not a true array of all array types do not, so we need to go to an array of methods to borrow.

For example, borrow push an array of methods:

var arrayLike = {
  0: 'OB',
  1: 'Koro1',
  length: 2
}
Array.prototype.push.call(arrayLike, '添加元素1', '添加元素2');
console.log(arrayLike) // {"0":"OB","1":"Koro1","2":"添加元素1","3":"添加元素2","length":4}
  1. obtaining the minimum value of the maximum array apply:

apply directly passing arrays as parameters to call the method also further expand the array province, such as the use Math.max, Math.minto get the maximum / minimum array:

const arr = [15, 6, 12, 13, 16];
const max = Math.max.apply(Math, arr); // 16
const min = Math.min.apply(Math, arr); // 6
  1. inherit

ES5 are also inherited parent class inheritance to methods / properties by borrowing the parent class constructor:

// 父类
function supFather(name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green']; // 复杂类型
}
supFather.prototype.sayName = function (age) {
    console.log(this.name, 'age');
};
// 子类
function sub(name, age) {
    // 借用父类的方法:修改它的this指向,赋值父类的构造函数里面方法、属性到子类上
    supFather.call(this, name);
    this.age = age;
}
// 重写子类的prototype,修正constructor指向
function inheritPrototype(sonFn, fatherFn) {
    sonFn.prototype = Object.create(fatherFn.prototype); // 继承父类的属性以及方法
    sonFn.prototype.constructor = sonFn; // 修正constructor指向到继承的那个函数上
}
inheritPrototype(sub, supFather);
sub.prototype.sayAge = function () {
    console.log(this.age, 'foo');
};
// 实例化子类,可以在实例上找到属性、方法
const instance1 = new sub("OBKoro1", 24);
const instance2 = new sub("小明", 18);
instance1.colors.push('black')
console.log(instance1) // {"name":"OBKoro1","colors":["red","blue","green","black"],"age":24}
console.log(instance2) // {"name":"小明","colors":["red","blue","green"],"age":18} 

There are many similar scenarios, not go into details, the key lies in the idea that they borrow methods, do not understand the words Duokanjibian.

call, apply, the use of which? ,

call, apply exactly the same effect, their difference also lies

  • The number of parameters / sequence determination on a call, the number of parameter / indeterminate order then to use Apply .
  • Consider readability: small number of parameters to use apply, the number of parameters more, then, to integrate the parameters into an array, use apply.
  • Parameter set is already the case of an array, with Apply, such as an array of obtaining the above maximum / minimum.

The number of parameters / indeterminate order to use then apply, such as the following example:

const obj = {
    age: 24,
    name: 'OBKoro1',
}
const obj2 = {
    age: 777
}
callObj(obj, handle)
callObj(obj2, handle)
// 根据某些条件来决定要传递参数的数量、以及顺序
function callObj(thisAge, fn) {
    let params = []
    if (thisAge.name) {
        params.push(thisAge.name)
    }
    if (thisAge.age) {
        params.push(thisAge.age)
    }
    fn.apply(thisAge, params) // 数量和顺序不确定 不能使用call
}
function handle(...params) {
    console.log('params', params) // do some thing
}

bind application scenarios:

1. Save function parameters:

First look at a classic face questions:

for (var i = 1; i <= 5; i++) {
   setTimeout(function test() {
        console.log(i) // 依次输出:6 6 6 6 6
    }, i * 1000);
}

The reason for this phenomenon is to wait until setTimeoutasynchronous execution, iit has become a 6.

About js event loop mechanism is not understood by the students, I can see this blog: Js event cycle (Event Loop) mechanisms as well as examples to explain

So how do you make him out: 1,2,3,4,5 it?

There are many ways:

  • Closure, save variables
for (var i = 1; i <= 5; i++) {
    (function (i) {
        setTimeout(function () {
            console.log('闭包:', i); // 依次输出:1 2 3 4 5
        }, i * 1000);
    }(i));
}

Here creates a closure, each cycle will be the ilatest value passed in, and then be saved closure.

  • bind
for (var i = 1; i <= 5; i++) {
    // 缓存参数
    setTimeout(function (i) {
        console.log('bind', i) // 依次输出:1 2 3 4 5
    }.bind(null, i), i * 1000);
}

In fact here also with the closures, we know bind returns a function that is closure .

It holds the function of this point, the initial parameters, each ichange bind closure will be kept up, the output 1-5.

Details, there is the following handwritten bind method, look, you can get to know.

  • let

With letthe statement ican also be output 1-5: as leta block-level scope, so each will create a new variable, so setTimeouteach read the values are different, explain .

2. The callback function this loss problems:

This is a common problem, here is my handle plug-in development VSCode webviewreal communication problems encountered, at the beginning of the API that VSCode going wrong, only to find that a lot of debugging thispoint loss problem.

class Page {
    constructor(callBack) {
        this.className = 'Page'
        this.MessageCallBack = callBack // 
        this.MessageCallBack('发给注册页面的信息') // 执行PageA的回调函数
    }
}
class PageA {
    constructor() {
        this.className = 'PageA'
        this.pageClass = new Page(this.handleMessage) // 注册页面 传递回调函数 问题在这里
    }
    // 与页面通信回调
    handleMessage(msg) {
        console.log('处理通信', this.className, msg) //  'Page' this指向错误
    }
}
new PageA()

The callback function thisWhy be lost?

Obviously, when the statement of the problem does not occur when the callback function can not be a problem.

The problem at the time of the transfer callback function:

this.pageClass = new Page(this.handleMessage)

Because the transfer of the past this.handleMessageis a memory address of the function, there is no context object, which means that the function does not bind its thispoints.

That it thispoints to which it applies binding rules :

class Page {
    constructor(callBack) {
        this.className = 'Page'
        // callBack() // 直接执行的话 由于class 内部是严格模式,所以this 实际指向的是 undefined
        this.MessageCallBack = callBack // 回调函数的this 隐式绑定到class page
        this.MessageCallBack('发给注册页面的信息')
    }
}

Now that you know the problem, and then we just bind callback function thispoint is PageAto solve the problem.

The callback function this loss solutions :

  1. bindBinding callback function thispoint:

This is a typical application scenarios bind, bind this point, is used as a callback function.

this.pageClass = new Page(this.handleMessage.bind(this)) // 绑定回调函数的this指向

PS: This is why reactthe renderfunction when binding the callback function, but also use bind binding at thisthe point, but also because the same issues and principles.

  1. Arrow function to bind this point

this arrow pointing function, when defined in a first outer layer of this general function, the class categories are referred to herein:PageA

This piece of content, you can look before I wrote blog: Detailed considerations and differences arrow arrow function and a normal function of function, NA scene

this.pageClass = new Page(() => this.handleMessage()) // 箭头函数绑定this指向

Senior interview questions - handwriting call / apply, bind:

In an interview manufacturers, the handwriting achieve call, apply, bind (particularly bind) has been the relatively high frequency of face questions, here we take a look at to achieve these functions.

You can achieve a hand-written callit?

Thinking

  1. Call context object disposed according to the rules, that is thisthe point.
  2. By setting contextproperties, this point will be a function of the implicit binding to context
  3. Functions performed by implicit binding and pass parameters.
  4. Delete the temporary attribute, the function returns the execution result
Function.prototype.myCall = function (context, ...arr) {
    if (context === null || context === undefined) {
       // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
        context = window 
    } else {
        context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
    }
    context.testFn = this; // 函数的this指向隐式绑定到context上
    let result = context.testFn(...arr); // 通过隐式绑定执行函数并传递参数
    delete context.testFn; // 删除上下文对象的属性
    return result; // 返回函数执行结果
};

Analyzing context object function:

A lot of people judge a function of context object, but simply to contextwhether to false judgments, such as:

// 判断函数上下文绑定到`window`不够严谨
context = context ? Object(context) : window; 
context = context || window; 

After testing, the following three cases as false, the context object will function to bind to windowthe:

// 网上的其他绑定函数上下文对象的方案: 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

The callcontext object function will be bound to the original values of the object instance:

Examples of the object's original value

Therefore, the correct solution, it should be like what I have to do:

// 正确判断函数上下文对象
    if (context === null || context === undefined) {
       // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
        context = window 
    } else {
        context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
    }

You can achieve a hand-written applyit?

Ideas:

  1. Parameters passed to the handler, not the same with other parts of callthe same.
  2. applyThe second parameter is the class accepts an array of objects, where the JavaScript with Authoritative Guide to the array is determined whether the class object's method.
Function.prototype.myApply = function (context) {
    if (context === null || context === undefined) {
        context = window // 指定为 null 和 undefined 的 this 值会自动指向全局对象(浏览器中为window)
    } else {
        context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
    }
    // JavaScript权威指南判断是否为类数组对象
    function isArrayLike(o) {
        if (o &&                                    // o不是null、undefined等
            typeof o === 'object' &&                // o是对象
            isFinite(o.length) &&                   // o.length是有限数值
            o.length >= 0 &&                        // o.length为非负值
            o.length === Math.floor(o.length) &&    // o.length是整数
            o.length < 4294967296)                  // o.length < 2^32
            return true
        else
            return false
    }
    context.testFn = this; // 隐式绑定this指向到context上
    const args = arguments[1]; // 获取参数数组
    let result
    // 处理传进来的第二个参数
    if (args) {
        // 是否传递第二个参数
        if (!Array.isArray(args) && !isArrayLike(args)) {
            throw new TypeError('myApply 第二个参数不为数组并且不为类数组对象抛出错误');
        } else {
            args = Array.from(args) // 转为数组
            result = context.testFn(...args); // 执行函数并展开数组,传递函数参数
        }
    } else {
        result = context.testFn(); // 执行函数
    }
    delete context.testFn; // 删除上下文对象的属性
    return result; // 返回函数执行结果
};

You can achieve a hand-written bindit?

Key plan :

Handwriting bindis a high frequency of manufacturers in the face of questions, if senior front end of the interview, but can tell the difference between them, usage and can not come to the fore, understanding have enough depth to hold it offer go!

Thinking

  1. Function copy source:
    • By storing variable source function
    • Use Object.createthe copy source function prototype to fToBind
  2. Copy function returns
  3. Call the function copies:
    • new call to judgment: by instanceofjudging whether through function newcalls to determine the bindingcontext
    • This + binding to pass parameters
    • The function execution result back to the source
Function.prototype.myBind = function (objThis, ...params) {
    const thisFn = this; // 存储源函数以及上方的params(函数参数)
    let fToBind = function () {
        const isNew = this instanceof fToBind // this是否是fToBind的实例 也就是返回的fToBind是否通过new调用
        const context = isNew ? this : Object(objThis) // new调用就绑定到this上,否则就绑定到传入的objThis上
        return thisFn.apply(context, params); // 用apply调用源函数绑定this的指向并传递参数,返回执行结果
    };
    fToBind.prototype = Object.create(thisFn.prototype); // 复制源函数的prototype给fToBind
    return fToBind; // 返回拷贝的函数
};

summary

Originally I thought this would be written quickly, the result of the write off for several days, finally these three API knowledge clearly described, I hope you read after the interview and then encounter this problem, you can sea and air round loaded to force ^ _ ^

I think the blog helpful to you, then give me some more Star now!

Advanced front-end accumulation , public numbers , GitHub , WX: OBkoro1, E-mail: [email protected]

Above 2019/8/30

Guess you like

Origin www.cnblogs.com/OBkoro1/p/11298840.html