javascript 为什么 lodash 的 .isObject, .isPlainObject 的行为与“typeof x === 'object'”不同?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34111902/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 17:16:01  来源:igfitidea点击:

Why do lodash's .isObject, .isPlainObject behave differently than "typeof x === 'object'"?

javascriptlodash

提问by brandonscript

Consider the following:

考虑以下:

var o1 = {}
var O = function () {
  return this
}
var o2 = new O()
var o3 = function() {}
var o4 = [o1, o1]

var output = [
    [_.isObject(o1), _.isObject(o2), _.isObject(o3), _.isObject(o4)], 
    [_.isPlainObject(o1), _.isPlainObject(o2), _.isPlainObject(o3), _.isPlainObject(o4)],
    [typeof o1 === 'object', typeof o2 === 'object', typeof o3 === 'object', typeof o4 === 'object'],
    [o1 instanceof Array, o2 instanceof Array, o3 instanceof Array, o4 instanceof Array]
]

/* outputs:

[
    [true,true,true,true],
    [true,false,false,false],
    [true,true,false,true],
    [false,false,false,true]
]

*/

Clearly we can see that there is a disconnect between .isObject():

很明显,我们可以看到以下之间存在脱节.isObject()

Which checks if value is the language type of Object. (e.g. arrays, functions, objects, regexes, new Number(0), and new String(' ')

检查 value 是否是 Object 的语言类型。(例如数组、函数、对象、正则表达式、new Number(0) 和 new String(' ')

.isPlainObject():

.isPlainObject()

Which if value is a plain object, that is, an object created by the Object constructor or one with a Prototype of null

其中if value 是一个普通对象,即由 Object 构造函数创建的对象或 Prototype 为 null 的对象

And our good ol' trusty friend typeof x === 'object'.

还有我们可信赖的好朋友typeof x === 'object'

I have three questions:

我有三个问题:

  1. Was there a conscious design decision to make .isObjectand .isPlainObjectbehave differently than the native .js type checking?
  2. If my first question is true, what was the design decision and what are the benefits of doing it this way?
  3. Is there any native lodash(or underscore.js) is*function that behaves exactly the same as typeof x === 'object'?
  1. 是否有一个有意识的设计决策来做出.isObject.isPlainObject原生 .js 类型检查不同的行为?
  2. 如果我的第一个问题是真的,那么设计决策是什么,这样做有什么好处?
  3. 是否有任何本机lodash(或underscore.jsis*函数的行为与typeof x === 'object'?

Obviously I can just continue to use typeof, but syntactically it's a bit weird to use one or the other in some places, for example the usage of .isObjectwill return false positives when checking for typeof x === 'object' && typeof x !== 'function'. I don't really see any benefit of .isObjectreturning true for functions when .isFunctionalready exists.

显然我可以继续使用typeof,但在语法上在某些地方使用一个或另一个有点奇怪,例如.isObject在检查typeof x === 'object' && typeof x !== 'function'. 我真的没有看到.isObject.isFunction已经存在的函数返回 true 的任何好处。

采纳答案by djechlin

typeofhas nothing to do with whether something is an object. Functions, strings, and {}have different typeofand they are all objects. Functions are of course first-class objects, just like strings are first-class objects, therefore isObjectmust return true for strings and objects.

typeof与某物是否为对象无关。函数、字符串和{}有不同的typeof,它们都是对象。函数当然是一等对象,就像字符串是一等对象一样,因此isObject对于字符串和对象必须返回true。

For the record the documentation covers this:

作为记录,文档涵盖了以下内容:

Checks if value is the language type of Object. (e.g. arrays, functions, objects, regexes, new Number(0), and new String(''))

检查 value 是否为 Object 的语言类型。(例如数组、函数、对象、正则表达式、new Number(0) 和 new String(''))

Wow! that really is a lot to test for without having a handy isObjectmethod. To be fair most return typeofas object, but the point of higher level methods, especially in libraries like lodash, is so the programmer can forget about that nonsense.

哇!如果没有一个方便的isObject方法,这真的是很多要测试的。公平地说,大多数返回typeofas object,但更高级的方法的重点,尤其是在像 lodash 这样的库中,程序员可以忘记那些废话。

If you care about the typeofan argument, then use typeof. If you care about objects that are not functions, you have a couple options: you can use typeofandcheck strings specially, or you can use isObject && !isFunction. I would prefer the latter. The latter does happen to say exactly what you are trying to convey so it really is the correct thing to code. If you think when you say "object" that you implicitly don't mean functions, then you do not think functions are first-class objects, or rather you would like your code to more closely resemble a language in which they're not. But then you can't blame lodash for being a library that extensively uses the fact that functions are first-class objects to make a language in which functions are first-class objects more expressive.

如果您关心typeofan 参数,请使用typeof. 如果您关心不是函数的对象,您有几个选择:您可以专门使用typeof检查字符串,或者您可以使用isObject && !isFunction. 我更喜欢后者。后者确实恰好说明了您想要传达的内容,因此它确实是编码的正确内容。如果您认为当您说“对象”时隐含地不是指函数,那么您并不认为函数是一流的对象,或者您希望您的代码更接近于一种语言,而它们不是。但是你不能责怪 lodash 是一个广泛使用函数是一流对象这一事实的库,使函数是一流对象的语言更具表现力。

I believe that was the bulk of your question. I believe the use case for isPlainObjectis to answer the question "is this just data?" or "is this code?" so objects created as pseudo-classes (newof something) don't count.

我相信这是你的大部分问题。我相信用例isPlainObject是回答“这只是数据吗?”的问题。或“这是代码吗?” 所以创建为伪类(new某物)的对象不计算在内。

回答by Ethan Lynn

Sometimes code speaks louder than words. Here's the source from lodash:

有时代码胜于雄辩。这是来自 lodash 的源代码:

function isObject(value) {
  var type = typeof value;
  return !!value && (type == 'object' || type == 'function');
}
function isPlainObject(value) {
  var Ctor;
  if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isHostObject(value) && !isArguments(value)) ||
      (!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) {
    return false;
  }
  var result;
  if (lodash.support.ownLast) {
    baseForIn(value, function(subValue, key, object) {
      result = hasOwnProperty.call(object, key);
      return false;
    });
    return result !== false;
  }
  baseForIn(value, function(subValue, key) {
    result = key;
  });
  return result === undefined || hasOwnProperty.call(value, result);
}

According to the lodash docs:

根据 lodash 文档:

isObject

对象

Checks if value is the language typeof Object. (e.g. arrays, functions, objects, regexes, new Number(0), and new String(''))

检查 value 是否为 Object的语言类型。(例如数组、函数、对象、正则表达式、new Number(0) 和 new String(''))

isPlainObject

是普通对象

Checks if value is a plain object, that is, an object created by the Object constructor or one with a [[Prototype]] of null.

检查 value 是否为普通对象,即由 Object 构造函数创建的对象或 [[Prototype]] 为 null 的对象。

What is an object?

什么是对象?

Perhaps the existence of an isObjectfunction is inherently a bit confusing when in JavaScript so many things act like objects. The key idea is that some values in JavaScript are considered primitives and others considered full-blown objects. Strings, numbers, and booleans are treated differently at an internal level than objects created with the object literal or constructor functions (this doesn't end up have much practical significance because primitives will automatically cast themselves to objects when necessary).

也许isObject函数的存在本质上有点令人困惑,因为在 JavaScript 中,很多东西都像对象一样。关键思想是 JavaScript 中的某些值被认为是原始值,而其他值被认为是成熟的对象。字符串、数字和布尔值在内部级别的处理方式与使用对象字面量或构造函数创建的对象不同(这最终没有太大的实际意义,因为基元会在必要时自动将自己强制转换为对象)。

The fact that typeof null === 'object'probably originates from the fact that objects are reference types and null is often returned from functions in lieu of an object. (A quirk which likely hearkens back to the concept of pointers and the NULLPTR in C and C++. The type void *has many values but the NULLPTR is still considered a valid value for the pointer type.)

这一事实typeof null === 'object'可能源于这样一个事实,即对象是引用类型,而 null 通常从函数返回,而不是从对象中返回。(一个怪癖可能会让人回想起 C 和 C++ 中指针和 NULLPTR 的概念。该类型void *有很多值,但 NULLPTR 仍然被认为是指针类型的有效值。)

回答by Daniel Kobe

  1. The lodash docsdescribes Lodash as "a JavaScript utility library delivering consistency, modularity, performance, & extras." isObjectand isPlainObjectwould be an extra. They are utility functions. I'm sure they were aware that they are different then typeof which is why they may be useful to some people. They probably thought the performance, consistency, and syntax of the native typeofdidn't warrant the making of .typeOfthat does the same thing.
  2. As mentioned above, the benefits are that it functions a little differently than typeof which may be handy to some people. Also lodash focuses on better performance as well, although I'm not sure if there performance of such a simple function could be significantly improved. It is said to improve the performance of loopsthough. You can test the performance difference using JSPerfand let us know! As far as why they decided to have .isObjectreturn true for functions is because functions in JS are objects. It is kind of deceiving when native typeofreturns function and not object. It is further convoluted by the fact that typeof []doesnt return array and instead returns object so why should it return function and not object on a function.
  3. Neither lodash or underscore appear to have a function that is the same as typeof. Underscore has _.isObjectwhich is the same as the lodash .isObject. You could use lodash's .isFunctionand .isObjectto create your own typeof function that returns the same thing native typeofwould.
  1. lodash文档将 Lodash 描述为“一个提供一致性、模块化、性能和附加功能的 JavaScript 实用程序库”。isObject并且isPlainObject将是额外的。它们是效用函数。我敢肯定他们知道它们与 typeof 不同,这就是为什么它们可能对某些人有用。他们可能认为原生的性能、一致性和语法typeof并不能保证.typeOf它做同样的事情。
  2. 如上所述,好处是它的功能与 typeof 有点不同,后者可能对某些人来说很方便。lodash 也专注于更好的性能,尽管我不确定这样一个简单函数的性能是否可以显着提高。据说它可以提高循环的性能。您可以使用JSPerf测试性能差异并告诉我们!至于为什么他们决定.isObject为函数返回 true 是因为 JS 中的函数是对象。当本机typeof返回函数而不是对象时,这是一种欺骗。typeof []不返回数组而是返回对象的事实进一步令人费解,那么为什么它应该返回函数而不是函数上的对象。
  3. lodash 或下划线似乎都没有与typeof. 下划线_.isObject与 lodash 相同.isObject。您可以使用 lodash's .isFunctionand.isObject创建自己的 typeof 函数,该函数返回与本机相同的内容typeof