首页 > 解决方案 > Javascript中的密封对象和检查对象的类型

问题描述

这个问题的背景

我最近开始使用 karma 和 Jasmine 在 Javascript 中进行测试驱动开发 (TDD)。我想开发适当的测试以及处理所有可能性的良好代码。这也意味着我要检查测试用例中正确类型的对象。

当前问题

从 AJAX 调用返回的 JSON 数据应与对象匹配,以确保返回正确的数据。为此,我想出了以下代码(简化);

var MyClass = {};
Object.defineProperties(MyClass, {
  Key: {
    value: '',
    writable: true
  }
});

var sealed = Object.seal(MyClass);
try {
  $.extend(sealed, jsonResponse);
  return sealed;
} catch(e) {
  return false;
}

这按预期工作,如果任何额外的属性是 JSON 的一部分,false则将返回。到目前为止,一切都很好。我还想做的是检查返回的数据是否是正确的“类型”。我可以通过使用函数而不是对象来做到这一点:

var MyClass = function() {
  this.key= '123';
}; 
var myClass = new MyClass();
console.log(myClass instanceof MyClass); // True

但是我想不出如何将两者结合起来,所以我既可以确保没有属性被添加到对象中,也可以检查正确的“类”。在第一种情况下typeof sealed,总是会产生“对象”。

限制

我还需要支持 Internet Explorer 11,所以我不能使用class MyClass { ... }.

标签: javascript

解决方案


如果我对您的理解正确,则组合版本将是:

// The "class"
var MyClass = function(data) {
  // Create all the allowed properties
  this.key = '123';

  // Seal the instance so others can't be created
  Object.seal(this);

  // If data was supplied, apply it
  if (data) {
    $.extend(this, data);
  }
}; 

// The instance that you'd use, note passing in the parsed JSON daa
var myClass = new MyClass(jsonResponse);
console.log(myClass instanceof MyClass); // true

现场示例:

// The "class"
var MyClass = function(data) {
  // Create all the allowed properties
  this.key = '123';

  // Seal the instance so others can't be created
  Object.seal(this);

  // If data was supplied, apply it
  if (data) {
    $.extend(this, data);
  }
};

var jsonResponse = {
  key: "456"
};

// The instance that you'd use, note passing in the parsed JSON daa
var myClass = new MyClass(jsonResponse);
console.log(myClass instanceof MyClass); // true
console.log(myClass.key);                // "456"
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

jsonResponse如果包含MyClass不允许的属性,则会引发错误,因为尝试在密封对象上创建新属性会引发错误。你在评论中说过这就是你想要的(这是有道理的)。

如果这不是您想要的,您可以编写自己的函数来复制属性,而不是使用$.extend

function applyData(obj, data) {
    var own = Object.prototype.hasOwnProperty.bind(obj);
    Object.keys(data).filter(own).forEach(function(key) {
        obj[key] = data[key];
    });
}

(或者,in如果您想允许设置继承的属性——例如,访问器属性——,请使用检查。)

(您可以只ifforEach回调中使用 an 而不是.filter(own)。)

然后使用该函数而不是$.extend.

现场示例:

// The function to apply the data
function applyData(obj, data) {
    var own = Object.prototype.hasOwnProperty.bind(obj);
    Object.keys(data).filter(own).forEach(function(key) {
        obj[key] = data[key];
    });
}

// The "class"
var MyClass = function(data) {
  // Create all the allowed properties
  this.key = '123';

  // Seal the instance so others can't be created
  Object.seal(this);

  // If data was supplied, apply it
  if (data) {
    applyData(this, data);
  }
}; 

var jsonResponse = {
  key: "456",
  extra: "will be ignored"
};

// The instance that you'd use, note passing in the parsed JSON daa
var myClass = new MyClass(jsonResponse);
console.log(myClass instanceof MyClass); // true
console.log(myClass.key);                // "456"
console.log(myClass.extra);              // undefined

但是你犯错误的方法是有道理的。


推荐阅读