做`Object(this)`的目的是什么?

时间:2017-05-19 21:40:06

标签: javascript node.js

我正在MDN上进行Array'a find polyfill实现:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find?v=control#Polyfill

将其粘贴到下面:

  // https://tc39.github.io/ecma262/#sec-array.prototype.find
  if (!Array.prototype.find) {
    Object.defineProperty(Array.prototype, 'find', {
      value: function(predicate) {
       // 1. Let O be ? ToObject(this value).
        if (this == null) {
          throw new TypeError('"this" is null or not defined');
        }

        var o = Object(this);

        // 2. Let len be ? ToLength(? Get(O, "length")).
        var len = o.length >>> 0;

        // 3. If IsCallable(predicate) is false, throw a TypeError exception.
        if (typeof predicate !== 'function') {
          throw new TypeError('predicate must be a function');
        }

        // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
        var thisArg = arguments[1];

        // 5. Let k be 0.
        var k = 0;

        // 6. Repeat, while k < len
        while (k < len) {
          // a. Let Pk be ! ToString(k).
          // b. Let kValue be ? Get(O, Pk).
          // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
          // d. If testResult is true, return kValue.
          var kValue = o[k];
          if (predicate.call(thisArg, kValue, k, o)) {
            return kValue;
          }
          // e. Increase k by 1.
          k++;
        }

        // 7. Return undefined.
        return undefined;
      }
    });
  }

为什么我们需要var o = Object(this);

Object(this)取得了什么成果?

感谢您的讨论。

2 个答案:

答案 0 :(得分:5)

在严格模式下,原始this强制转换为对象。

因此,使用Object(this) 明确强制对象是必要的。

这是一个更详细的例子:

&#13;
&#13;
const array = Array.prototype;

Object.defineProperty(array, 'foo1', { value() { 
  return this.length >>> 0;  }});

Object.defineProperty(array, 'foo2', { value() { 
  "use strict"; 
  return this.length >>> 0; }});

console.log(Array.prototype.foo1.call(undefined));
console.log(Array.prototype.foo2.call(undefined));
&#13;
&#13;
&#13;

第一个示例成功运行,结果为0,因为参数undefined被强制转换为非严格模式的对象。第二个示例失败,因为undefined 在非严格模式下强制执行,因此this.length出错。

来自MDN

  

作为this传递给严格模式的函数的值不会被强制成为一个对象(a.k.a。&#34; boxed&#34;)

但是,在这种情况下,已经显式检查null或undefined:

if (this == null) {
  throw new TypeError('"this" is null or not defined');
}

所以我很想说明一个对象的显式转换是没有必要的。这可能是出于谨慎使用或作为样板而使用的。

答案 1 :(得分:4)

这是一个引人入胜的问题......感谢你的帖子!

老实说,我对Object(this)感到有些惊讶,因为在this可能是原始值的情况下,JavaScript似乎强迫任何对象(使用包装器)。

如果我们尝试使用this更改Function.prototype.bind(),JavaScript始终会返回一个对象(函数是一个对象):

var foo = function () {
  console.log(this, typeof this);
}.bind('foo');

var bar = function () {
  console.log(this, typeof this);
}.bind(1337);

var baz = function () {
  console.log(this, typeof this);
}.bind(false);

var qux = function () {
  console.log(this, typeof this);
}.bind(NaN);

var quux = function () {
  console.log(this, typeof this);
}.bind(undefined);

var corge = function () {
  console.log(this, typeof this);
}.bind(null);

var grault = function () {
  console.log(this, typeof this);
}.bind([]);

var garply = function () {
  console.log(this, typeof this);
}.bind({});

var waldo = function () {
  console.log(this, typeof this);
}.bind(/regex/);

var fred = function () {
  console.log(this, typeof this);
}.bind(function () {});

foo(); // String { 0: "f", 1: "o", 2: "o" } object
bar(); // Number { 1337 } object
baz(); // Boolean { false } object
qux(); // Number { NaN } object
quux(); // Window object
corge(); // Window object
grault(); // Array [ ] object
garply(); // Object { } object
waldo(); // /regex/ object
fred(); // function fred<() function   

如果我们尝试使用thisFunction.prototype.call()更改Function.prototype.apply(),JavaScript将再次返回一个对象:

Array.prototype.foo = function () {
  console.log(this, typeof this);
};

['foo'].foo(); // Array [ "foo" ] object
Array.prototype.foo.call('bar'); // String { 0: "b", 1: "a", 2: "r"} object
Array.prototype.foo.call(42); // Number { 42 } object
Array.prototype.foo.call(true); // Boolean { true } object
Array.prototype.foo.call(NaN); // Number { NaN } object
Array.prototype.foo.call(undefined); // Window object
Array.prototype.foo.call(null); // Window object
Array.prototype.foo.call({}); // Object { } object
Array.prototype.foo.call(/regex/); // /regex/ object
Array.prototype.foo.call(function () {}); // function () function

在JavaScript中,我们知道当本机对象不用作构造函数而是用作常规函数时,它们可能对类型转换很有用。 NumberStringBoolean非常方便:

var num = 1337,
    str = '',
    bool = true;
    
console.log(Number(str), typeof Number(str));
console.log(Number(bool), typeof Number(bool));

console.log(String(num), typeof String(num));
console.log(String(bool), typeof String(bool));

console.log(Boolean(num), typeof Boolean(num))
console.log(Boolean(str), typeof Boolean(str));

对于记录,以下是我们通过Object()进行显式转换所获得的结果:

console.log(typeof Object(false), Object(false) instanceof Boolean);
console.log(typeof Object('bar'), Object('bar') instanceof String);
console.log(typeof Object(42), Object(42) instanceof Number);
console.log(typeof Object(NaN), Object(NaN) instanceof Number);
console.log(typeof Object(undefined), Object(undefined) instanceof Object);
console.log(typeof Object(null), Object(null) instanceof Object);
console.log(typeof Object(['foo']), Object(['foo']) instanceof Array);
console.log(typeof Object({}), Object({}) instanceof Object);
console.log(typeof Object(/regex/), Object(/regex/) instanceof RegExp);
console.log(typeof Object(function () {}), Object(function () {}) instanceof Function);

现在显而易见的是Object(this)可用于转换this的任何原始值并改为获取包装器对象。如果this已经是对象,则无效:

var obj1 = {baz: 'Baz'},
    obj2 = Object(obj1);

var arr1 = ['foo', 'bar'],
    arr2 = Object(arr1);
    
var reg1 = /regex/,
    reg2 = Object(reg1);
    
var fun1 = function () { return 'Hello!'; },
    fun2 = Object(fun1);
    
console.log(arr1 === arr2);
console.log(obj1 === obj2);
console.log(reg1 === reg2);
console.log(fun1 === fun2);

此外,Object很奇怪,因为它的行为方式相同,无论是否使用new调用:

var foo = Object('foo'),
    bar = new Object('bar');
    
console.log(foo);
console.log(bar);

我可能错了,但我的结论如下:鉴于this始终强制对象,Object(this)不是必需的。但是,它明确地指出了为避免歧义和提高代码理解而隐式发生的事情。

您怎么看?

修改

torazaburo是对的:严格模式是关键!当函数处于严格模式时,this的原始值不会被强制执行!这可能是使用Object(this) ...

进行显式转换的最合理解释

Function.prototype.bind()

var foo = function () {
  'use strict';
  console.log(this, typeof this);
}.bind('foo');

var bar = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(1337);

var baz = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(false);

var qux = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(NaN);

var quux = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(undefined);

var corge = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(null);

var grault = function () {
  'use strict';
  console.log(this, typeof this);
}.bind([]);

var garply = function () {
  'use strict';
  console.log(this, typeof this);
}.bind({});

var waldo = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(/regex/);

var fred = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(function () {});

foo(); // foo string
bar(); // 1337 number
baz(); // false boolean
qux(); // NaN number
quux(); // undefined undefined
corge(); // null object
grault(); // Array [ ] object
garply(); // Object { } object
waldo(); // /regex/ object
fred(); // function fred<() function

Function.prototype.call()

Array.prototype.foo = function () {
  'use strict';
  console.log(this, typeof this);
};

['foo'].foo(); // Array [ "foo" ] object
Array.prototype.foo.call('bar'); // bar string
Array.prototype.foo.call(42); // 42 number
Array.prototype.foo.call(true); // true boolean
Array.prototype.foo.call(NaN); // NaN number
Array.prototype.foo.call(undefined); // undefined undefined
Array.prototype.foo.call(null); // null object
Array.prototype.foo.call({}); // Object { } object
Array.prototype.foo.call(/regex/); // /regex/ object
Array.prototype.foo.call(function () {}); // function () function