有没有办法在 Javascript 中确定一个对象是使用对象文字符号还是使用构造方法创建的?
在我看来,您只是访问它的父对象,但是如果您传入的对象没有对其父对象的引用,我认为您无法说出这一点,对吗?
有没有办法在 Javascript 中确定一个对象是使用对象文字符号还是使用构造方法创建的?
在我看来,您只是访问它的父对象,但是如果您传入的对象没有对其父对象的引用,我认为您无法说出这一点,对吗?
你想要的是:
Object.getPrototypeOf(obj) === Object.prototype
这将检查该对象是否是一个普通对象,该对象是使用new Object()
或{...}
不是某个子类创建的Object
。
我刚刚在一个甜蜜的 hackfest 中遇到了这个问题和线程,其中涉及评估一个对象是使用 {} 还是 new Object() 创建的圣杯任务(我还没有弄清楚。)
无论如何,我惊讶地发现这里发布的 isObjectLiteral() 函数与我为 Pollen.JS 项目编写的 isObjLiteral() 函数之间的相似之处。我相信这个解决方案是在我提交 Pollen.JS 之前发布的,所以 - 向你致敬!我的好处是长度......不到一半(包括您的设置程序时),但两者都会产生相同的结果。
看一看:
函数 isObjLiteral(_obj) { 变量 _test = _obj; return ( typeof _obj !== 'object' || _obj === null ? 错误的 : ( (功能 () { 而(!假){ if ( Object.getPrototypeOf( _test = Object.getPrototypeOf(_test) ) === null) { 休息; } } 返回 Object.getPrototypeOf(_obj) === _test; })() ) ); }
此外,一些测试的东西:
var _cases= { _objLit:{}, _objNew:新对象(), _function:新函数(), _array:新数组(), _string:新字符串(), _image:新图像(), _bool: 真 }; 控制台.dir(_cases); for ( _cases 中的 var _test ) { 控制台.组(_test); 控制台.dir({ 类型:typeof _cases[_test], 字符串:_cases[_test].toString(), 结果:isObjLiteral(_cases[_test]) }); 控制台.groupEnd(); }
或者在 jsbin.com 上...
当你到达那里时一定要打开 firebug - 对文档的调试是为 IE 爱好者准备的。
编辑:我将“对象文字”解释为使用对象文字或Object
构造函数创建的任何内容。这就是 John Resig 最有可能的意思。
我有一个功能,即使.constructor
已被污染或对象是在另一个框架中创建的,它也能正常工作。请注意Object.prototype.toString.call(obj) === "[object Object]"
(有些人可能认为)不会解决这个问题。
function isObjectLiteral(obj) {
if (typeof obj !== "object" || obj === null)
return false;
var hasOwnProp = Object.prototype.hasOwnProperty,
ObjProto = obj;
// get obj's Object constructor's prototype
while (Object.getPrototypeOf(ObjProto = Object.getPrototypeOf(ObjProto)) !== null);
if (!Object.getPrototypeOf.isNative) // workaround if non-native Object.getPrototypeOf
for (var prop in obj)
if (!hasOwnProp.call(obj, prop) && !hasOwnProp.call(ObjProto, prop)) // inherited elsewhere
return false;
return Object.getPrototypeOf(obj) === ObjProto;
};
if (!Object.getPrototypeOf) {
if (typeof ({}).__proto__ === "object") {
Object.getPrototypeOf = function (obj) {
return obj.__proto__;
};
Object.getPrototypeOf.isNative = true;
} else {
Object.getPrototypeOf = function (obj) {
var constructor = obj.constructor,
oldConstructor;
if (Object.prototype.hasOwnProperty.call(obj, "constructor")) {
oldConstructor = constructor;
if (!(delete obj.constructor)) // reset constructor
return null; // can't delete obj.constructor, return null
constructor = obj.constructor; // get real constructor
obj.constructor = oldConstructor; // restore constructor
}
return constructor ? constructor.prototype : null; // needed for IE
};
Object.getPrototypeOf.isNative = false;
}
} else Object.getPrototypeOf.isNative = true;
这是测试用例的 HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<!-- Online here: http://code.eligrey.com/testcases/all/isObjectLiteral.html -->
<title>isObjectLiteral</title>
<style type="text/css">
li { background: green; } li.FAIL { background: red; }
iframe { display: none; }
</style>
</head>
<body>
<ul id="results"></ul>
<script type="text/javascript">
function isObjectLiteral(obj) {
if (typeof obj !== "object" || obj === null)
return false;
var hasOwnProp = Object.prototype.hasOwnProperty,
ObjProto = obj;
// get obj's Object constructor's prototype
while (Object.getPrototypeOf(ObjProto = Object.getPrototypeOf(ObjProto)) !== null);
if (!Object.getPrototypeOf.isNative) // workaround if non-native Object.getPrototypeOf
for (var prop in obj)
if (!hasOwnProp.call(obj, prop) && !hasOwnProp.call(ObjProto, prop)) // inherited elsewhere
return false;
return Object.getPrototypeOf(obj) === ObjProto;
};
if (!Object.getPrototypeOf) {
if (typeof ({}).__proto__ === "object") {
Object.getPrototypeOf = function (obj) {
return obj.__proto__;
};
Object.getPrototypeOf.isNative = true;
} else {
Object.getPrototypeOf = function (obj) {
var constructor = obj.constructor,
oldConstructor;
if (Object.prototype.hasOwnProperty.call(obj, "constructor")) {
oldConstructor = constructor;
if (!(delete obj.constructor)) // reset constructor
return null; // can't delete obj.constructor, return null
constructor = obj.constructor; // get real constructor
obj.constructor = oldConstructor; // restore constructor
}
return constructor ? constructor.prototype : null; // needed for IE
};
Object.getPrototypeOf.isNative = false;
}
} else Object.getPrototypeOf.isNative = true;
// Function serialization is not permitted
// Does not work across all browsers
Function.prototype.toString = function(){};
// The use case that we want to match
log("{}", {}, true);
// Instantiated objects shouldn't be matched
log("new Date", new Date, false);
var fn = function(){};
// Makes the function a little more realistic
// (and harder to detect, incidentally)
fn.prototype = {someMethod: function(){}};
// Functions shouldn't be matched
log("fn", fn, false);
// Again, instantiated objects shouldn't be matched
log("new fn", new fn, false);
var fn2 = function(){};
log("new fn2", new fn2, false);
var fn3 = function(){};
fn3.prototype = {}; // impossible to detect (?) without native Object.getPrototypeOf
log("new fn3 (only passes with native Object.getPrototypeOf)", new fn3, false);
log("null", null, false);
log("undefined", undefined, false);
/* Note:
* The restriction against instantiated functions is
* due to the fact that this method will be used for
* deep-cloning an object. Instantiated objects will
* just have their reference copied over, whereas
* plain objects will need to be completely cloned.
*/
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
var doc = iframe.contentDocument || iframe.contentWindow.document;
doc.open();
doc.write("<body onload='window.top.iframeDone(Object);'>");
doc.close();
function iframeDone(otherObject){
// Objects from other windows should be matched
log("new otherObject", new otherObject, true);
}
function log(msg, a, b) {
var pass = isObjectLiteral(a) === b ? "PASS" : "FAIL";
document.getElementById("results").innerHTML +=
"<li class='" + pass + "'>" + msg + "</li>";
}
</script>
</body>
</html>
听起来你正在寻找这个:
function Foo() {}
var a = {};
var b = new Foo();
console.log(a.constructor == Object); // true
console.log(b.constructor == Object); // false
对象的构造函数属性是指向用于构造它的函数的指针。在上面的例子b.constructor == Foo
中。如果对象是使用大括号(数组文字符号)或使用创建的,new Object()
那么它的构造函数属性将为== Object
.
更新: crescentfresh 指出,$(document).constructor == Object
而不是等于 jQuery 构造函数,所以我做了更多的挖掘。似乎通过使用对象字面量作为对象的原型,您的构造函数属性几乎一文不值:
function Foo() {}
var obj = new Foo();
obj.constructor == Object; // false
但:
function Foo() {}
Foo.prototype = { objectLiteral: true };
var obj = new Foo();
obj.constructor == Object; // true
在这里的另一个答案中有一个很好的解释,这里有一个更复杂的解释。
我认为其他答案是正确的,并且没有真正的方法可以检测到这一点。
对象字面量是您用来定义对象的表示法——在 javascript 中,它始终采用由大括号括起来的名称-值对的形式。一旦执行此操作,就无法判断该对象是否是由该表示法创建的(实际上,我认为这可能过于简单,但基本上是正确的)。你只有一个对象。这是 js 的一大优点,因为有很多捷径可以做一些可能需要更长的时间来编写的事情。简而言之,文字符号取代了必须编写:
var myobject = new Object();
我有同样的问题,所以我决定这样做:
function isPlainObject(val) {
return val ? val.constructor === {}.constructor : false;
}
// Examples:
isPlainObject({}); // true
isPlainObject([]); // false
isPlainObject(new Human("Erik", 25)); // false
isPlainObject(new Date); // false
isPlainObject(new RegExp); // false
//and so on...
没有办法区分从对象字面量构建的对象和从其他方式构建的对象之间的区别。
这有点像问你是否可以通过分配值'2'或'3-1'来确定是否构造了数字变量;
如果您需要这样做,您必须将一些特定的签名放入您的对象文字中以便以后检测。
现在有一个更优雅的解决方案可以准确地回答您的问题:
function isObject(value) {
return value !== null && value !== undefined && Object.is(value.constructor, Object)
}
// Test stuff below //
class MyClass extends Object {
constructor(args) {
super(args)
}
say() {
console.log('hello')
}
}
function MyProto() {
Object.call(this)
}
MyProto.prototype = Object.assign(Object.create(Object.prototype), {
constructor: MyProto,
say: function() {
console.log('hello')
}
});
const testsCases = {
objectLiteral: {},
objectFromNew: new Object(),
null: null,
undefined: undefined,
number: 123,
function: new Function(),
array: new Array([1, 2, 3]),
string: new String('foobar'),
image: new Image(),
bool: true,
error: new Error('oups'),
myClass: new MyClass(),
myProto: new MyProto()
}
for (const [key, value] of Object.entries(testsCases)) {
console.log(`${key.padEnd(15)} => ${isObject(value)}`)
}
最好的祝福
11 岁的问题是我的整洁解决方案,对边缘案例建议持开放态度;步骤 -> 仅查找对象然后比较以检查属性 -> 对象文字没有长度、原型和边缘情况 stringyfy 属性。
尝试测试 JSON 和 Object.create(Object.create({cool: "joes"}))。
"use strict"
let isObjectL = a => {
if (typeof a !=='object' || ['Number','String','Boolean', 'Symbol'].includes(a.constructor.name)) return false;
let props = Object.getOwnPropertyNames(a);
if ( !props.includes('length') && !props.includes('prototype') || !props.includes('stringify')) return true;
};
let A={type:"Fiat", model:"500", color:"white"};
let B= new Object();
let C = { "name":"John", "age":30, "city":"New York"};
let D= '{ "name":"John", "age":30, "city":"New York"}';
let E = JSON.parse(D);
let F = new Boolean();
let G = new Number();
console.log(isObjectL(A));
console.log(isObjectL(B));
console.log(isObjectL(C));
console.log(isObjectL(D));
console.log(isObjectL(E));
console.log(isObjectL(JSON));
console.log(isObjectL(F));
console.log(isObjectL(G));
console.log(isObjectL(
Object.create(Object.create({cool: "joes"}))));
console.log(isObjectL());
显示内部工作的另一个变体
isObject=function(a) {
let exclude = ['Number','String','Boolean', 'Symbol'];
let types = typeof a;
let props = Object.getOwnPropertyNames(a);
console.log((types ==='object' && !exclude.includes(a.constructor.name) &&
( !props.includes('length') && !props.includes('prototype') && !props.includes('stringify'))));
return `type: ${types} props: ${props}
----------------`}
A={type:"Fiat", model:"500", color:"white"};
B= new Object();
C = { "name":"John", "age":30, "city":"New York"};
D= '{ "name":"John", "age":30, "city":"New York"}';
E = JSON.parse(D);
F = new Boolean();
G = new Number();
console.log(isObject(A));
console.log(isObject(B));
console.log(isObject(C));
console.log(isObject(D));
console.log(isObject(E));
console.log(isObject(JSON));
console.log(isObject(F));
console.log(isObject(G));
console.log(isObject(
Object.create(Object.create({cool: "joes"}))));
typeof obj === 'object' && obj !== null && Object.getPrototypeOf(obj) === Object.prototype
下面全部返回false
123
null
undefined
'abc'
false
true
[]
new Number()
new Boolean()
() => {}
function () {}
对杰西的回答有所改进