javascript - 对 "softBind"函数如何工作的困惑

标签 javascript function binding prototype this

我目前正在通过“你不知道 js”系列学习 javascript。

在“this & object prototype”部分,作者提出了一种软绑定(bind)this的方法。 .

但是,我对代码感到非常困惑。所以我想知道是否有人可以向我解释它,一步一步地,代码到底做了什么?

//step 1: if "softBind" property does not exist on `Function.prototye`

if (!Function.prototype.softBind) {

    //step 2: create a property named "softBind" on "Function.prototype" and assign to "softBind" the following function 

    Function.prototype.softBind = function(obj) {

        //step 3: what is the point of assigning "this" to the variable "fn"?
        //what does "this" represent at this point in time?

        var fn = this,

            //step 4: I understand that "arguments" is an array-like object, i.e. "arguments" is not a true array. 
            //But you can convert "arguments" to an true array by using "[].slice.call(arguments)". 
            //The thing I dont understand here is, why the 1? 
            //I understand it tells "slice" method to start slicing at index 1, but why 1? 
            //And what is the purpose of "curried" variable? 

            curried = [].slice.call( arguments, 1 ),

            bound = function bound() {

                //step 5: I understand what "apply" function does

                return fn.apply(

                    //step 6: I dont really understand how "!this" works. 

                    (!this ||

                        //step 7: utterly confused...   

                        (typeof window !== "undefined" &&
                            this === window) ||
                        (typeof global !== "undefined" &&
                            this === global)

                    //step 8: if the above statements evaluates to "true", then use "obj", 
                    //otherwise, use "this"

                    ) ? obj : this,

                    //step 9: can't I write "curried.concat(arguments)" instead?
                    //why the convoluted syntax?

                    curried.concat.apply( curried, arguments )
                );
            };

        //step 10: Why assign the "fn.prototype" as the prototype to "bound.prototype"?

        bound.prototype = Object.create( fn.prototype );
        return bound;
    };
}

很抱歉问了这么长的问题,但我认为与其将问题分成几篇文章,不如将它们放在一个地方更方便。

最佳答案

第 3 步:将“this”分配给变量“fn”有什么意义?
this的值持有一个指向当前正在执行的函数对象的指针。只能保存函数对象,因此只有通过 new() 或等效符号实际创建的东西。这对于将外部对象的引用传递给在所述外部对象内创建的内部对象非常有用。

这是一个最小的例子:

function fn1() {
    var x = this;  // x = fn1.
    this.u = 5;
    function fn2() {
        this.u = 10;
        console.log(this.u); // Prints the member of fn2.
        console.log(x.u); // Prints the member of fn1.
    };
    var W = new fn2();
}
var V = new fn1();

输出应该是:
10
5

首先,一个类型为 fn1 的对象创建名为 V .它有一个成员变量 u持有值(value) 5 .然后,我们创建一个类型为 fn2 的对象。叫 Wfn1 .它还有有一个成员变量 u ,但这里它持有值 10 .如果我们想 print V.u 的值内W ,那么我们需要一个指向 V 的指针.调用 this.uW将输出它的 u 值 (10),这不是我们想要的。所以我们定义了一个变量 x类范围内fn1 ,拿着this我们的指针。现在可以访问 fn1 的成员了内fn2 .

第 4 步

第一个参数是绑定(bind)到的对象。你不想把它传递给被绑定(bind)的函数,这会破坏它的功能,因为它不希望在它的正常参数列表中添加额外的参数。因此,必须删除第一个参数。

第 6 步:我真的不明白“!这个”是如何工作的。
!this只是一种检查是否 this 的方法被定义为。如果不是,则该值将为 true .否则,因为 this 将是一个对象(当转换为 bool 值时,它的计算结果为 true),那么它是 false .

第 7 步:完全困惑...

这里原作者检查是否this等于 window , 或 global .笔记;在现代浏览器中,只检查 window就足够了,但是 IE 存在(非浏览器 javascript 环境也是如此)。因此,完整的语句评估为:

如果我不是从对象内部调用的,或者如果我是从对象调用的 windowglobal ,然后返回对象 softbind是用创建的。否则,返回我被调用的对象

请注意,这是 正好原始文章的作者想要什么。当使用这种特殊绑定(bind)调用库函数时,我们可以确定库所做的一切;它无法访问 global通过使用 this 的上下文多变的。但是,它可以访问任何其他对象,从而允许您与库进行交互。

第 9 步:我不能写“curried.concat(arguments)”吗?
Curried保留原始 softbind 的所有参数函数被调用,除了第一个参数。 arguments ,此时,不等于arguments在之前的通话中。在这里,它指的是调用绑定(bind)函数的参数,而不是绑定(bind)函数的参数。该行集成了两组参数,允许您提供默认参数。这里使用的技巧是连接参数,例如假设您的函数具有默认参数 [1,2,3,4]并且您提供[5,6] :
[1,2,3,4].concat([5,6])生产 [1,2,3,4,5,6] .

为什么不简单地连接,并使用原型(prototype)?数组在javascript中通过引用传递,所以这将保留curried相同,同时连接 arguments到电话。同样,你可以这样写:
curried2 = curried.concat(arguments);
return fn.apply(
(.....)
curried2);

诚然,简洁无助于本示例的可理解性。只需将参数重命名为 calledArguments 和 curried (与解释无关的高级数学术语)为 defaultArguments并使用一个简单的 for如果稍微详细一点,循环每个参数会更容易理解。我猜作者想要花哨。

步骤10:为什么将“fn.prototype”作为原型(prototype)分配给“bound.prototype”?

文章上一点到作者讲默认的部分bind功能及其工作原理:基本上,替换 prototype 的最终结果在函数调用期间返回默认原型(prototype)意味着当您的 softbind使用 new 调用启用的函数运算符(operator) this将设置为自身,而不是默认绑定(bind)对象。 prototype仅调用绑定(bind)函数时将不起作用。

它还支持继承,这意味着为 softbind 创建东西使用其 prototype 启用功能不会让该原型(prototype)被 softbind 的原型(prototype)否决当它被绑定(bind)。 (这会使 softbind 与原型(prototype)不兼容)。相反,两者 使用原型(prototype)。

另见 this reddit post .

警告语

我们正在使用新功能扩展该语言。并非完全必要的功能,并且主要与语义有关。如果你只是对学习这门语言感兴趣,这真的太过分了,你并不完全需要特殊的绑定(bind)语义。更糟糕的是,如果 this 可能会令人困惑不会以您期望的方式行事。

一个更简单的选择

启用严格模式。现在 this将默认为 undefined每当它指向全局对象时。防止这个复杂的代码试图解决的问题(通常会导致函数试图访问 undefined 的成员变量或函数的错误),同时更容易使用,同时它会提示关于许多有效的常规 javascript 的语法,但在任何正常用例中都是一个错误。另见 MDN article关于它。它将为您捕获许多潜在的错误,而不是默默地做无意义的事情。

另一种选择
bind当您将对象的成员函数传递给另一个函数时,尝试解决“丢失”对象的问题,例如 setTimeout .另一种方法是使用匿名函数。而不是使用(软)绑定(bind),假设 obj是一个持有函数的对象 fn正在传递参数 param ;
setTimeout(obj.fn(param), 500);

您可以使用:
setTimeout(function(param){obj.fn(param);}, 500);

通过传递匿名函数通过间接层避免了问题。另见 this question .

关于javascript - 对 "softBind"函数如何工作的困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45046836/

相关文章:

javascript - 在 Firebase 中对项目集合进行建模的正确方法

java - 如何使用 JSF 从 javascript 调用 java 函数

javascript - jQuery Datepicker 日期格式不起作用

python **kwags错误: function takes 6 positional arguments but 8 were given

android - MvxCommand 将 CommandParameter 绑定(bind)到字段

javascript - MarkCompactCollector : young object promotion failed Allocation failed

c# - 新(修饰符)函数的实际用法?

javascript - 通过函数返回值仍然存在问题

android - 用于数据绑定(bind)的 LiveData 与 ObservableField

javascript - 如何在 Knockout 中有选择地绑定(bind)属性