【问题标题】:Using prototype apply vs this to call function使用原型 apply vs this 来调用函数
【发布时间】:2015-04-22 22:36:43
【问题描述】:

MDN String 页面上,他们有一个 polyfill String.includes 的示例。

String.prototype.includes = function() {'use strict';
    return String.prototype.indexOf.apply(this, arguments) !== -1;
};

他们使用String.prototype.indexOf.apply 与直接在this 上调用indexOf 有什么原因吗?

String.prototype.includes = function(searchString, position) {'use strict';
    return this.indexOf(searchString, position) !== -1;
};

【问题讨论】:

    标签: javascript prototype this


    【解决方案1】:

    答案是使用this.indexOf 的polyfill 版本不符合spec for String.prototype.includes,这允许this 可以转换为字符串:

    如果 searchString 显示为 将此对象转换为字符串的结果的子字符串...

    例如,thisincludes 可以是一个数字:

    << String.prototype.includes.call(1, '1')
    >> true
    

    这类似于String.prototype.indexOf,根据spec 也不需要它的this 是一个字符串。

    << String.prototype.indexOf.call(1, '1')
    >> 0
    

    如果includes 是按照OP 建议的this.indexOf 实现的:

    String.prototype.includes = function(searchString, position) {'use strict';
        return this.indexOf(searchString, position) !== -1;
    };
    

    然后在规范允许的情况下,使用非字符串 this 调用 includes 会生成运行时错误:

    << String.prototype.includes.call(1, '1')
    >> TypeError: undefined is not a function
    

    而 MDN polyfill:

    String.prototype.includes = function() {'use strict';
        return String.prototype.indexOf.apply(this, arguments) !== -1;
    };
    

    工作正常,利用String.prototype.indexOfthis 也不必是字符串这一事实:

    << String.prototype.includes.call(1, '1')
    >> true
    

    所以我认为 MDN polyfill 的编写方式不是为了防止 indexOf 方法在某些特定字符串对象上被覆盖,或者作为避免列出参数的简写,或者由于某些 Crockfordian 偏好prototype.apply 成语,而是为了正确实现规范。

    【讨论】:

    • 然后return String(this).indexOf(searchString, position) !== -1; 将适应 this 不是字符串并且符合规范的地方。 ;-)
    • @RobG 不准确,因为String(null) 给你"null" 并且调用将“成功”,而String.prototype.indexOf.call(null, '1') 生成String.prototype.indexOf called on null or undefined 运行时错误。
    • 谁在里面放了CheckObjectCoercible? >:-(
    • 很好的解释和指向规范的加分
    【解决方案2】:

    String.prototype.indexOf可以接受一两个参数,使用 apply 可以让您简单地传递传入的内容,而不用担心类型或存在检查。

    【讨论】:

    • 你还是可以的this.indexOf.apply(this, arguments)
    • 当然,你说得有道理,尽管我个人讨厌在该行中重复使用 this。所以我已经回答了为什么要使用apply,但没有回答为什么要使用String.prototype
    • 关于 2 个参数的参数也很弱:this.indexOf(searchString, position); 将比给定的 .apply() 调用更具可读性
    • 耸耸肩,我不同意它的弱点。如果你要对原型进行大量扩充(就像在 polyfilling 中所做的那样),那么随着时间的推移,你就不会那么纠结了。我想是自行车棚。
    • @JAAulde 在这种特殊情况下没有什么可以改变:1. indexOf 方法的参数是已知的并且不会改变。 2. includes 的参数是已知的并且不会改变。从你的角度来看,我看不出String.prototype.indexOf.apply(this, arguments)this.indexOf(searchString, position) 好多少
    【解决方案3】:

    是的,这样做是有原因的。它确保即使字符串的indexOf 属性被覆盖,原始的indexOf 属性仍将被使用。

    如果我们使用new String 构造函数,这样的事情是可能的。

    var s = new String('test');
    s.indexOf = function() {
        throw new Error('this is bad');
    };
    s.indexOf('test');//Throws error.
    

    【讨论】:

    • 这看起来是不是有点奇怪。我的意思是,如果您不能相信字符串、数组等的常规方法已经到位......那么您可以在您的环境中相信什么?一个人应该去Array.prototype.slice.apply(this, xxx) 只是为了在一个数组上做一个 slice() 吗?妄想症在哪里停止?
    • @jfriend00 我听到了,但是 Crockford 经常做这个案子。这是一个很好的答案。
    • @jfriend00 我同意这不是使用它的最有说服力的案例,但 polyfill 将在完全未知的环境中使用,目标是尽可能紧密和安全地重新创建功能可能。
    • 问题的答案没有错,我只是质疑问题中的代码。我的意思是,与单个字符串对象相比,字符串原型本身被篡改的可能性更大。由于字符串是不可变的,对它们的大多数操作都会创建新的字符串,这使得对特定字符串对象的修改在很长一段时间内都不是很有用,所以如果你想改变一个字符串对象,你可能会改变原型。
    • 你能给你引用 Crockford 的参考吗?
    猜你喜欢
    • 2018-02-28
    • 1970-01-01
    • 2015-10-30
    • 1970-01-01
    • 2018-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多