国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

面試官問(wèn):能否模擬實(shí)現(xiàn)JS的bind方法

Julylovin / 1110人閱讀

摘要:點(diǎn)擊那么面試官可能會(huì)問(wèn)是否想過(guò)到底做了什么,怎么模擬實(shí)現(xiàn)呢。另外前不久寫過(guò)一篇文章面試官問(wèn)能否模擬實(shí)現(xiàn)的操作符。所以相當(dāng)于調(diào)用時(shí),的返回值函數(shù)內(nèi)部要模擬實(shí)現(xiàn)實(shí)現(xiàn)的操作。文章中的例子和測(cè)試代碼放在中模擬實(shí)現(xiàn)。

前言

用過(guò)React的同學(xué)都知道,經(jīng)常會(huì)使用bind來(lái)綁定this

import React, { Component } from "react";
class TodoItem extends Component{
    constructor(props){
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick(){
        console.log("handleClick");
    }
    render(){
        return  (
            
點(diǎn)擊
); }; } export default TodoItem;

那么面試官可能會(huì)問(wèn)是否想過(guò)bind到底做了什么,怎么模擬實(shí)現(xiàn)呢。

附上之前寫文章寫過(guò)的一段話:已經(jīng)有很多模擬實(shí)現(xiàn)bind的文章,為什么自己還要寫一遍呢。學(xué)習(xí)就好比是座大山,人們沿著不同的路登山,分享著自己看到的風(fēng)景。你不一定能看到別人看到的風(fēng)景,體會(huì)到別人的心情。只有自己去登山,才能看到不一樣的風(fēng)景,體會(huì)才更加深刻。

先看一下bind是什么。從上面的React代碼中,可以看出bind執(zhí)行后是函數(shù),并且每個(gè)函數(shù)都可以執(zhí)行調(diào)用它。
眼見(jiàn)為實(shí),耳聽(tīng)為虛。讀者可以在控制臺(tái)一步步點(diǎn)開(kāi)例子1中的obj:

var obj = {};
console.log(obj);
console.log(typeof Function.prototype.bind); // function
console.log(typeof Function.prototype.bind());  // function
console.log(Function.prototype.bind.name);  // bind
console.log(Function.prototype.bind().name);  // bound

因此可以得出結(jié)論1:

1、bindFunctoin原型鏈中Function.prototype的一個(gè)屬性,每個(gè)函數(shù)都可以調(diào)用它。

2、bind本身是一個(gè)函數(shù)名為bind的函數(shù),返回值也是函數(shù),函數(shù)名是bound 。(打出來(lái)就是bound加上一個(gè)空格)。
知道了bind是函數(shù),就可以傳參,而且返回值"bound "也是函數(shù),也可以傳參,就很容易寫出例子2

后文統(tǒng)一 bound 指原函數(shù)original bind之后返回的函數(shù),便于說(shuō)明。

var obj = {
    name: "軒轅Rowboat",
};
function original(a, b){
    console.log(this.name);
    console.log([a, b]);
    return false;
}
var bound = original.bind(obj, 1);
var boundResult = bound(2); // "軒轅Rowboat", [1, 2]
console.log(boundResult); // false
console.log(original.bind.name); // "bind"
console.log(original.bind.length); // 1
console.log(original.bind().length); // 2 返回original函數(shù)的形參個(gè)數(shù)
console.log(bound.name); // "bound original"
console.log((function(){}).bind().name); // "bound "
console.log((function(){}).bind().length); // 0
由此可以得出結(jié)論2:

1、調(diào)用bind的函數(shù)中的this指向bind()函數(shù)的第一個(gè)參數(shù)。

2、傳給bind()的其他參數(shù)接收處理了,bind()之后返回的函數(shù)的參數(shù)也接收處理了,也就是說(shuō)合并處理了。

3、并且bind()后的namebound + 空格 + 調(diào)用bind的函數(shù)名。如果是匿名函數(shù)則是bound + 空格

4、bind后的返回值函數(shù),執(zhí)行后返回值是原函數(shù)(original)的返回值。

5、bind函數(shù)形參(即函數(shù)的length)是1bind后返回的bound函數(shù)形參不定,根據(jù)綁定的函數(shù)原函數(shù)(original)形參個(gè)數(shù)確定。

根據(jù)結(jié)論2:我們就可以簡(jiǎn)單模擬實(shí)現(xiàn)一個(gè)簡(jiǎn)版bindFn

// 第一版 修改this指向,合并參數(shù)
Function.prototype.bindFn = function bind(thisArg){
    if(typeof this !== "function"){
        throw new TypeError(this + "must be a function");
    }
    // 存儲(chǔ)函數(shù)本身
    var self = this;
    // 去除thisArg的其他參數(shù) 轉(zhuǎn)成數(shù)組
    var args = [].slice.call(arguments, 1);
    var bound = function(){
        // bind返回的函數(shù) 的參數(shù)轉(zhuǎn)成數(shù)組
        var boundArgs = [].slice.call(arguments);
        // apply修改this指向,把兩個(gè)函數(shù)的參數(shù)合并傳給self函數(shù),并執(zhí)行self函數(shù),返回執(zhí)行結(jié)果
        return self.apply(thisArg, args.concat(boundArgs));
    }
    return bound;
}
// 測(cè)試
var obj = {
    name: "軒轅Rowboat",
};
function original(a, b){
    console.log(this.name);
    console.log([a, b]);
}
var bound = original.bindFn(obj, 1);
bound(2); // "軒轅Rowboat", [1, 2]

如果面試官看到你答到這里,估計(jì)對(duì)你的印象60、70分應(yīng)該是會(huì)有的。
但我們知道函數(shù)是可以用new來(lái)實(shí)例化的。那么bind()返回值函數(shù)會(huì)是什么表現(xiàn)呢。

接下來(lái)看例子3

var obj = {
    name: "軒轅Rowboat",
};
function original(a, b){
    console.log("this", this); // original?{}
    console.log("typeof this", typeof this); // object
    this.name = b;
    console.log("name", this.name); // 2
    console.log("this", this);  // original?{name: 2}
    console.log([a, b]); // 1, 2
}
var bound = original.bind(obj, 1);
var newBoundResult = new bound(2);
console.log(newBoundResult, "newBoundResult"); // original?{name: 2}

例子3種可以看出this指向了new bound()生成的新對(duì)象。

可以分析得出結(jié)論3:

1、bind原先指向obj的失效了,其他參數(shù)有效。

2、new bound的返回值是以original原函數(shù)構(gòu)造器生成的新對(duì)象。original原函數(shù)的this指向的就是這個(gè)新對(duì)象。
另外前不久寫過(guò)一篇文章:面試官問(wèn):能否模擬實(shí)現(xiàn)JS的new操作符。簡(jiǎn)單摘要:
new做了什么:

1.創(chuàng)建了一個(gè)全新的對(duì)象。

2.這個(gè)對(duì)象會(huì)被執(zhí)行[[Prototype]](也就是__proto__)鏈接。

3.生成的新對(duì)象會(huì)綁定到函數(shù)調(diào)用的this。

4.通過(guò)new創(chuàng)建的每個(gè)對(duì)象將最終被[[Prototype]]鏈接到這個(gè)函數(shù)的prototype對(duì)象上。

5.如果函數(shù)沒(méi)有返回對(duì)象類型Object(包含Functoin, Array, Date, RegExg, Error),那么new表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新的對(duì)象。

所以相當(dāng)于new調(diào)用時(shí),bind的返回值函數(shù)bound內(nèi)部要模擬實(shí)現(xiàn)new實(shí)現(xiàn)的操作。
話不多說(shuō),直接上代碼。

// 第三版 實(shí)現(xiàn)new調(diào)用
Function.prototype.bindFn = function bind(thisArg){
    if(typeof this !== "function"){
        throw new TypeError(this + " must be a function");
    }
    // 存儲(chǔ)調(diào)用bind的函數(shù)本身
    var self = this;
    // 去除thisArg的其他參數(shù) 轉(zhuǎn)成數(shù)組
    var args = [].slice.call(arguments, 1);
    var bound = function(){
        // bind返回的函數(shù) 的參數(shù)轉(zhuǎn)成數(shù)組
        var boundArgs = [].slice.call(arguments);
        var finalArgs = args.concat(boundArgs);
        // new 調(diào)用時(shí),其實(shí)this instanceof bound判斷也不是很準(zhǔn)確。es6 new.target就是解決這一問(wèn)題的。
        if(this instanceof bound){
            // 這里是實(shí)現(xiàn)上文描述的 new 的第 1, 2, 4 步
            // 1.創(chuàng)建一個(gè)全新的對(duì)象
            // 2.并且執(zhí)行[[Prototype]]鏈接
            // 4.通過(guò)`new`創(chuàng)建的每個(gè)對(duì)象將最終被`[[Prototype]]`鏈接到這個(gè)函數(shù)的`prototype`對(duì)象上。
            // self可能是ES6的箭頭函數(shù),沒(méi)有prototype,所以就沒(méi)必要再指向做prototype操作。
            if(self.prototype){
                // ES5 提供的方案 Object.create()
                // bound.prototype = Object.create(self.prototype);
                // 但 既然是模擬ES5的bind,那瀏覽器也基本沒(méi)有實(shí)現(xiàn)Object.create()
                // 所以采用 MDN ployfill方案 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
                function Empty(){}
                Empty.prototype = self.prototype;
                bound.prototype = new Empty();
            }
            // 這里是實(shí)現(xiàn)上文描述的 new 的第 3 步
            // 3.生成的新對(duì)象會(huì)綁定到函數(shù)調(diào)用的`this`。
            var result = self.apply(this, finalArgs);
            // 這里是實(shí)現(xiàn)上文描述的 new 的第 5 步
            // 5.如果函數(shù)沒(méi)有返回對(duì)象類型`Object`(包含`Functoin`, `Array`, `Date`, `RegExg`, `Error`),
            // 那么`new`表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新的對(duì)象。
            var isObject = typeof result === "object" && result !== null;
            var isFunction = typeof result === "function";
            if(isObject || isFunction){
                return result;
            }
            return this;
        }
        else{
            // apply修改this指向,把兩個(gè)函數(shù)的參數(shù)合并傳給self函數(shù),并執(zhí)行self函數(shù),返回執(zhí)行結(jié)果
            return self.apply(thisArg, finalArgs);
        }
    };
    return bound;
}

面試官看到這樣的實(shí)現(xiàn)代碼,基本就是滿分了,心里獨(dú)白:這小伙子/小姑娘不錯(cuò)啊。不過(guò)可能還會(huì)問(wèn)this instanceof bound不準(zhǔn)確問(wèn)題。
上文注釋中提到this instanceof bound也不是很準(zhǔn)確,ES6 new.target很好的解決這一問(wèn)題,我們舉個(gè)例子4:

instanceof 不準(zhǔn)確,ES6 new.target很好的解決這一問(wèn)題
function Student(name){
    if(this instanceof Student){
        this.name = name;
        console.log("name", name);
    }
    else{
        throw new Error("必須通過(guò)new關(guān)鍵字來(lái)調(diào)用Student。");
    }
}
var student = new Student("軒轅");
var notAStudent = Student.call(student, "Rowboat"); // 不拋出錯(cuò)誤,且執(zhí)行了。
console.log(student, "student", notAStudent, "notAStudent");

function Student2(name){
    if(typeof new.target !== "undefined"){
        this.name = name;
        console.log("name", name);
    }
    else{
        throw new Error("必須通過(guò)new關(guān)鍵字來(lái)調(diào)用Student2。");
    }
}
var student2 = new Student2("軒轅");
var notAStudent2 = Student2.call(student2, "Rowboat");
console.log(student2, "student2", notAStudent2, "notAStudent2"); // 拋出錯(cuò)誤

細(xì)心的同學(xué)可能會(huì)發(fā)現(xiàn)了這版本的代碼沒(méi)有實(shí)現(xiàn)bind后的bound函數(shù)的nameMDN Function.name和lengthMDN Function.length。面試官可能也發(fā)現(xiàn)了這一點(diǎn)繼續(xù)追問(wèn),如何實(shí)現(xiàn),或者問(wèn)是否看過(guò)es5-shim的源碼實(shí)現(xiàn)L201-L335。如果不限ES版本。其實(shí)可以用ES5Object.defineProperties來(lái)實(shí)現(xiàn)。

Object.defineProperties(bound, {
    "length": {
        value: self.length,
    },
    "name": {
        value: "bound " + self.name,
    }
});
es5-shim的源碼實(shí)現(xiàn)bind

直接附上源碼(有刪減注釋和部分修改等)

var $Array = Array;
var ArrayPrototype = $Array.prototype;
var $Object = Object;
var array_push = ArrayPrototype.push;
var array_slice = ArrayPrototype.slice;
var array_join = ArrayPrototype.join;
var array_concat = ArrayPrototype.concat;
var $Function = Function;
var FunctionPrototype = $Function.prototype;
var apply = FunctionPrototype.apply;
var max = Math.max;
// 簡(jiǎn)版 源碼更復(fù)雜些。
var isCallable = function isCallable(value){
    if(typeof value !== "function"){
        return false;
    }
    return true;
};
var Empty = function Empty() {};
// 源碼是 defineProperties
// 源碼是bind筆者改成bindFn便于測(cè)試
FunctionPrototype.bindFn = function bind(that) {
    var target = this;
    if (!isCallable(target)) {
        throw new TypeError("Function.prototype.bind called on incompatible " + target);
    }
    var args = array_slice.call(arguments, 1);
    var bound;
    var binder = function () {
        if (this instanceof bound) {
            var result = apply.call(
                target,
                this,
                array_concat.call(args, array_slice.call(arguments))
            );
            if ($Object(result) === result) {
                return result;
            }
            return this;
        } else {
            return apply.call(
                target,
                that,
                array_concat.call(args, array_slice.call(arguments))
            );
        }
    };
    var boundLength = max(0, target.length - args.length);
    var boundArgs = [];
    for (var i = 0; i < boundLength; i++) {
        array_push.call(boundArgs, "$" + i);
    }
    // 這里是Function構(gòu)造方式生成形參length $1, $2, $3...
    bound = $Function("binder", "return function (" + array_join.call(boundArgs, ",") + "){ return binder.apply(this, arguments); }")(binder);

    if (target.prototype) {
        Empty.prototype = target.prototype;
        bound.prototype = new Empty();
        Empty.prototype = null;
    }
    return bound;
};

你說(shuō)出es5-shim源碼bind實(shí)現(xiàn),感慨這代碼真是高效、嚴(yán)謹(jǐn)。面試官心里獨(dú)白可能是:你就是我要找的人,薪酬福利你可以和HR去談下。

最后總結(jié)一下

1、bindFunction原型鏈中的Function.prototype的一個(gè)屬性,它是一個(gè)函數(shù),修改this指向,合并參數(shù)傳遞給原函數(shù),返回值是一個(gè)新的函數(shù)。

2、bind返回的函數(shù)可以通過(guò)new調(diào)用,這時(shí)提供的this的參數(shù)被忽略,指向了new生成的全新對(duì)象。內(nèi)部模擬實(shí)現(xiàn)了new操作符。

3、es5-shim源碼模擬實(shí)現(xiàn)bind時(shí)用Function實(shí)現(xiàn)了length

事實(shí)上,平時(shí)其實(shí)很少需要使用自己實(shí)現(xiàn)的投入到生成環(huán)境中。但面試官通過(guò)這個(gè)面試題能考察很多知識(shí)。比如this指向,原型鏈,閉包,函數(shù)等知識(shí),可以擴(kuò)展很多。

讀者發(fā)現(xiàn)有不妥或可改善之處,歡迎指出。另外覺(jué)得寫得不錯(cuò),可以點(diǎn)個(gè)贊,也是對(duì)筆者的一種支持。

文章中的例子和測(cè)試代碼放在github中bind模擬實(shí)現(xiàn) github。bind模擬實(shí)現(xiàn) 預(yù)覽地址 F12看控制臺(tái)輸出,結(jié)合source面板查看效果更佳。

// 最終版 刪除注釋 詳細(xì)注釋版請(qǐng)看上文
Function.prototype.bind = Function.prototype.bind || function bind(thisArg){
    if(typeof this !== "function"){
        throw new TypeError(this + " must be a function");
    }
    var self = this;
    var args = [].slice.call(arguments, 1);
    var bound = function(){
        var boundArgs = [].slice.call(arguments);
        var finalArgs = args.concat(boundArgs);
        if(this instanceof bound){
            if(self.prototype){
                function Empty(){}
                Empty.prototype = self.prototype;
                bound.prototype = new Empty();
            }
            var result = self.apply(this, finalArgs);
            var isObject = typeof result === "object" && result !== null;
            var isFunction = typeof result === "function";
            if(isObject || isFunction){
                return result;
            }
            return this;
        }
        else{
            return self.apply(thisArg, finalArgs);
        }
    };
    return bound;
}
參考

OshotOkill翻譯的 深入理解ES6 簡(jiǎn)體中文版 - 第三章 函數(shù)(雖然我是看的紙質(zhì)書籍,但推薦下這本在線的書)
MDN Function.prototype.bind
冴羽: JavaScript深入之bind的模擬實(shí)現(xiàn)
《react狀態(tài)管理與同構(gòu)實(shí)戰(zhàn)》侯策:從一道面試題,到“我可能看了假源碼”

關(guān)于

作者:常以軒轅Rowboat若川為名混跡于江湖。前端路上 | PPT愛(ài)好者 | 所知甚少,唯善學(xué)。
個(gè)人博客
segmentfault前端視野專欄,開(kāi)通了前端視野專欄,歡迎關(guān)注
掘金專欄,歡迎關(guān)注
知乎前端視野專欄,開(kāi)通了前端視野專欄,歡迎關(guān)注
github,歡迎follow~

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/108792.html

相關(guān)文章

  • 面試官問(wèn)JSthis指向

    摘要:之前寫過(guò)一篇文章面試官問(wèn)能否模擬實(shí)現(xiàn)的和方法就是利用對(duì)象上的函數(shù)指向這個(gè)對(duì)象,來(lái)模擬實(shí)現(xiàn)和的。雖然實(shí)際使用時(shí)不會(huì)顯示返回,但面試官會(huì)問(wèn)到。非嚴(yán)格模式下,和,指向全局對(duì)象 前言 面試官出很多考題,基本都會(huì)變著方式來(lái)考察this指向,看候選人對(duì)JS基礎(chǔ)知識(shí)是否扎實(shí)。讀者可以先拉到底部看總結(jié),再谷歌(或各技術(shù)平臺(tái))搜索幾篇類似文章,看筆者寫的文章和別人有什么不同(歡迎在評(píng)論區(qū)評(píng)論不同之處),...

    warnerwu 評(píng)論0 收藏0
  • 面試官問(wèn)能否模擬實(shí)現(xiàn)JScall和apply方法

    摘要:之前寫過(guò)兩篇面試官問(wèn)能否模擬實(shí)現(xiàn)的操作符和面試官問(wèn)能否模擬實(shí)現(xiàn)的方法其中模擬方法時(shí)是使用的和修改指向。但面試官可能問(wèn)能否不用和來(lái)實(shí)現(xiàn)呢。使用模擬實(shí)現(xiàn)的瀏覽器環(huán)境非嚴(yán)格模式方法的屬性是。 之前寫過(guò)兩篇《面試官問(wèn):能否模擬實(shí)現(xiàn)JS的new操作符》和《面試官問(wèn):能否模擬實(shí)現(xiàn)JS的bind方法》 其中模擬bind方法時(shí)是使用的call和apply修改this指向。但面試官可能問(wèn):能否不用cal...

    wuyangnju 評(píng)論0 收藏0
  • 學(xué)習(xí) underscore 源碼整體架構(gòu),打造屬于自己函數(shù)式編程類庫(kù)

    摘要:譯立即執(zhí)行函數(shù)表達(dá)式處理支持瀏覽器環(huán)境微信小程序。學(xué)習(xí)整體架構(gòu),利于打造屬于自己的函數(shù)式編程類庫(kù)。下一篇文章可能是學(xué)習(xí)的源碼整體架構(gòu)。也可以加微信,注明來(lái)源,拉您進(jìn)前端視野交流群。 前言 上一篇文章寫了jQuery整體架構(gòu),學(xué)習(xí) jQuery 源碼整體架構(gòu),打造屬于自己的 js 類庫(kù) 雖然看過(guò)挺多underscore.js分析類的文章,但總感覺(jué)少點(diǎn)什么。這也許就是紙上得來(lái)終覺(jué)淺,絕知此...

    junnplus 評(píng)論0 收藏0
  • 面試官問(wèn)能否模擬實(shí)現(xiàn)JSnew操作符

    摘要:接下來(lái)繼續(xù)看升級(jí)版例子例子軒轅軒轅軒轅是瀏覽器實(shí)現(xiàn)的查看原型方案。模擬實(shí)現(xiàn)知道了這些現(xiàn)象,我們就可以模擬實(shí)現(xiàn)操作符。 前言 用過(guò)Vuejs的同學(xué)都知道,需要用new操作符來(lái)實(shí)例化。 new Vue({ el: #app, mounted(){}, }); 那么面試官可能會(huì)問(wèn)是否想過(guò)new到底做了什么,怎么模擬實(shí)現(xiàn)呢。 附上之前寫文章寫過(guò)的一段話:已經(jīng)有很多模擬實(shí)現(xiàn)new...

    shenhualong 評(píng)論0 收藏0
  • 面試官問(wèn)JS繼承

    摘要:用過(guò)的讀者知道,經(jīng)常用繼承。部分源碼使用點(diǎn)擊這里查看源碼面試官可以順著這個(gè)問(wèn)繼承的相關(guān)問(wèn)題,比如的繼承用如何實(shí)現(xiàn)。主要就是三點(diǎn)子類構(gòu)造函數(shù)的指向父類構(gòu)造器,繼承父類的靜態(tài)方法子類構(gòu)造函數(shù)的的指向父類構(gòu)造器的,繼承父類的方法。 用過(guò)React的讀者知道,經(jīng)常用extends繼承React.Component。 // 部分源碼 function Component(props, conte...

    stonezhu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<