如何确定是否使用new实例化变量

时间:2014-05-09 17:10:01

标签: javascript

如何确定是否使用new实例化Javascript变量,即是OOP意义上的对象。对象文字{a: 1}或数组['a', 'b']不应导致误报。

根据评论,对用例的进一步说明:

序列化和传输对象数据(使用开箱即用的普通JSON)会使它们在接收端显示为哑对象文字。需要手动恢复以使它们作为对象完全恢复活力,以便它们具有方法和所有类似对象的东西。我正在研究是否可以以优雅的方式完成这种复兴,作为解决方案的一部分,我需要知道是否可以回答这个原始问题。

请不要对此用例感到困惑,它涉及很多甚至更多不属于本问题范围的事情。让我们把它们留在别处。

根据评论,进一步的代码示例:

(从Typescript生成)

var C = (function () {
    function C() {
        this.v = 1;
    }
    C.prototype.f = function () {
        console.log('v=' + this.v);
    };
    return C;
})();

var c = new C();

c.f();

var cSerializedCopy = JSON.parse(JSON.stringify(c));

cSerializedCopy.f();

将提供以下输出:

v=1

.../test.js:17
cSerializedCopy.f();
                ^
TypeError: Object #<Object> has no method 'f'
    at Object.<anonymous> (.../test.js:17:17)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:901:3

显然,你认为这两个“对象”有些不同。这可以通过一些简单而防水的方式来确定吗?

3 个答案:

答案 0 :(得分:1)

如评论中所示,如果有人对变量的属性或原型进行了充分的讨论,他们可以打破任何尝试来解决这个问题,但这应该对您的用例非常可靠:

function wasConstructed(obj) {
    var c = obj.constructor;
    return !(c === Object || c === Array || 
             c === Number || c === String || 
             c === Boolean);
}

这将返回false

  • 使用数组文字和对象文字创建的对象
  • 使用new Object()new Array()
  • 创建的对象
  • 原始值

true几乎所有其他内容。

一些警告:

  • 如果使用var z = Object.create(x);创建对象,则wasConstructed(z)将生成与wasConstructed(x)相同的值
  • 手动覆盖对象&#39; .constructor属性可以破坏此实现

顺便提一下,您可以使用以下方法作为确定用于创建对象的构造函数名称的相当可靠的方法。您可以将此作为在序列化期间存储该信息的通用方法:

&#13;
&#13;
// Returns the name of fn if fn is a function, or null otherwise
function getFuncName(fn) {
  var funcNameRegex = /^\s*function\s*([^(]*)\(/,
    results = funcNameRegex.exec(fn.toString());
  return (results && results.length > 1) ? results[1] : null;
}

function getConstructorName(obj) {
  return getFuncName(obj.constructor);
}

var d = new Date();
var cName = getConstructorName(d); // returns "Date"

console.log(cName);
&#13;
&#13;
&#13;

这里的一个潜在缺陷是,如果构造函数是匿名的,这将无法正确确定名称:

&#13;
&#13;
// Returns the name of fn if fn is a function, or null otherwise
function getFuncName(fn) {
  var funcNameRegex = /^\s*function\s*([^(]*)\(/,
    results = funcNameRegex.exec(fn.toString());
  return (results && results.length > 1) ? results[1] : null;
}

function getConstructorName(obj) {
  return getFuncName(obj.constructor);
}

var C = function() {};
var i = new C();
var cName = getConstructorName(i);

console.log(cName);
&#13;
&#13;
&#13;

还有一个选项是在构造函数上使用name属性,这可以部分避免上述问题,但请注意,这将 完全不在IE中工作,因为它不会支持Function#name

&#13;
&#13;
var C = function() {};
var i = new C();
var cName = i.constructor.name;

console.log(cName);
&#13;
&#13;
&#13;

答案 1 :(得分:0)

好的,举个例子:

// I am going to use a Gizmo class to demonstrate my point
function Gizmo(prop1, prop2, prop3) {
  this.prop1 = prop1;
  this.prop2 = prop2;
  this.prop3 = prop3;

  // Now, this will flag what kind of object I have
  this.isGizmo = true;
}

// Make the object
var myGizmo = new Gizmo("The cat", "Thing 1", "Thing 2");

// Serialize the object
var gizmoJson = JSON.stringify(myGizmo);
console.log(gizmoJson);

// Now, go to rebuild the object:
var rawObject = JSON.parse(gizmoJson);

// Note that rawObject needs revived.
console.log("Checking if rawObject is a Gizmo: ");
console.log(rawObject instanceof Gizmo);

if (rawObject.isGizmo) {
  var myGizmo2 = new Gizmo(rawObject.prop1, rawObject.prop2, rawObject.prop3);

  console.log(myGizmo2);
  console.log("Checking if myGizmo2 is a Gizmo: ");
  console.log(myGizmo2 instanceof Gizmo);
}

答案 2 :(得分:0)

问题领域很难,因为Javascript的原型继承和对象模型与问题中隐含引用的更传统的OOP语言略有不同。由于这些事实和基本概念的不同定义,可能没有明确的答案。可能应该对原始问题采取另一种观点。

对于寻找相同问题的其他人,注释中指出的一个解决方案是使用类型知识并使用instanceof运算符。

c instanceof C // true

cSerializedCopy instanceof C // false