我们用JavaScript处理对象

在本文中,作者(前端开发人员)概述了创建,修改和比较JavaScript对象的主要方法。


对象是JavaScript中的基本概念之一。 当我开始研究它们时,它们对我来说似乎很简单:如理论上所述,只有几个键和值。

一段时间之后,我才开始意识到这个话题比我想象的要复杂得多。 然后,我开始研究来自各种来源的信息。 他们中的一些人对这个主题有个很好的主意,但我无法立即看到整个图片。

在这篇文章中,我尝试涵盖了使用JS对象的所有方面,而没有深入探讨单个细节,但又不漏掉重要的细节,这些细节将帮助您理解该主题并在进一步学习时更加自信。

因此,让我们从基础开始。

对象


JavaScript中的对象只是属性的集合,每个属性都是一个键-值对。 可以使用点号( obj.a )或方括号( obj ['a'] )访问密钥。

请记住,如果密钥为:,应使用方括号:

  • 不是有效的JavaScript标识符(它带有空格,破折号,以数字开头...)
  • 是一个变量。

JS对象创建时获得的属性之一称为Prototype ,这是一个非常重要的概念。

样机


JavaScript中的每个对象都有一个称为Prototype的内部属性。 在大多数浏览器中,可以用__proto__来引用它。

原型是在JavaScript中提供属性继承的一种方法。 因此,您无需共享内存中的代码即可共享功能。 该方法通过在两个对象之间创建关系来工作。

简而言之,Prototype创建从一个对象到另一个对象的指针。

原型链

每次JS在对象中搜索属性,而没有直接在对象本身上找到它时,它都会检查原型对象中是否存在该属性。 如果其中没有属性,那么JS将继续在相关对象的原型中进行搜索。 这将一直持续到JS找到合适的属性或到达链的末尾。

让我们看一个例子:

var cons = function () {   this.a = 1;   this.b = 2; } var obj = new cons(); </i>  cons.prototype.b = 3; cons.prototype.c = 4; 

cons是一个构造函数(只是可以使用new运算符调用的函数)。

在第五行,我们创建一个新对象-cons的新副本。 创建后, obj也立即获得了原型属性。

现在,我们在cons对象的原型中添加属性( 'b','c' )。
考虑obj

obj.a // 1-一切都和以前一样obj.a仍为1。
obj.c - obj没有c属性! 但是,如前所述,JS现在将在obj原型中查找它并返回值4。

现在,让我们考虑一下obj.b的值什么以及删除obj.b时它将变成什么?

Obj.b是2。我们分配了属性b ,但我们为cons原型做了此操作,因此当我们检查obj.b时 ,我们仍然得到2。但是,删除obj.b之后, JS将不再能够在o bj处找到b 。 ,因此将继续在原型中搜索并返回值3。

接下来,我想简要地谈谈创建对象的各种方法,并简要介绍一下原型。

对象创建


对象文字: let obj = {a:1};
我们使用以下原型链创建了一个对象: obj ---> Object.prototype ---> null
如您所料, object.prototype是对象的原型,也是原型链的末尾。

Object.create(): var newObj = Object.create(obj);
NewObj将具有以下原型链: newObj ---> obj ---> Object.prototype ---> null

构造函数。 如上例所示,构造函数只是一个JS函数,它使我们可以使用new运算符创建它的新实例。

ES6课程:

 class rectangle { constructor(height, width) { this.height = height; this.width = width; } getArea() { return this.height * this.width; } } let square = new rectangle(2, 2); 

Square矩形构造函数的一个实例,因此我们可以调用square.getArea()// 4square.width以及从object.prototype继承的所有函数。

哪种方法更好? 如果计划创建多个实例,则可以使用ES6或构造函数。 如果您打算一次创建一个对象,则最好指定一个文字,因为这是最简单的方法。

现在,当我们了解了原型并熟悉了创建新对象的所有方法时,我们可以继续讨论与对象相关的最令人困惑的方面之一。

比较和修改对象


在JavaScript中,对象是引用类型

当我们创建一个对象时, 让obj = {a:1}; ,变量obj获取对象内存中的地址,但不获取其值! 必须了解这种差异,否则可能会发生错误。 当我们创建另一个对象时, 使newObj = obj ,实际上是创建一个指向内存obj特定区域的指针 ,而不是一个全新的对象。

这意味着通过执行newObj.a = 2 ,我们实际上更改了obj,以便obj.a变为2!

这种方法很容易导致错误的出现,因此许多公司使用不可变的对象。 无需更改已经创建的对象,您将再次必须创建一个新对象(原始对象的副本)并对其进行更改。 这就是像Redux这样的库如何工作的重要性,总的来说,这是函数式编程的基本概念之一。 在这里阅读更多。

平等

从以上内容还可以得出结论,即使两个对象具有相同的属性,它们也永远不会相等。 这是由于JS实际上会比较对象在内存中的位置,并且两个对象永远不在同一内存单元中。

 // Two distinct objects with the same properties are not equal var fruit = {name: 'apple'}; var fruitbear = {name: 'apple'}; fruit === fruitbear; // return false // here fruit and fruitbear are pointing to same object var fruit = {name: 'apple'}; var fruitbear = fruit; fruit === fruitbear; // return true 

因此,您很可能已经想过,考虑到对对象的不变性的要求,如何比较对象或如何对对象执行各种操作。

考虑几种可能性。

物件变更

显然,很明显,我们不应该更改对象,因此我们想创建相应对象的副本并更改其属性。 Object.assign()可以解救

 var obj = { a : 1, b : 2}; var newObj = Object.assign({}, obj,{a:2}) // {a : 2, b : 2 } 

如果要更改obja属性的值,则可以使用object.assign创建obj的副本并进行更改。

该示例表明,我们首先创建一个空对象,然后复制obj值并进行更改,最终得到一个新的随时可用的对象。

请注意,此方法不适用于深度复制。 说到深度复制,我们意味着您需要复制具有一个或多个属性的对象。

 const obj = {a : 1, b : { a : 1 } }; // b property is an object 

Object.assign()复制对象的属性,因此,如果属性的值是指向对象的指针,则仅复制该指针。

深层复制需要递归操作。 您可以在此处编写函数,也可以只使用Lodash库中的_.cloneDeep方法。

对象比较

处理对象有一种很酷的方法-线转换。 在下面的示例中,我们将两个对象都转换为字符串并进行比较:

 JSON.stringify(obj1) === JSON.stringify(obj2) 

这种方法是合理的,因为最后我们比较表示值类型指针的字符串。 坏消息是它并不总是有效,主要是因为不能保证对象的一个​​或另一个属性顺序。

另一个好的解决方案是使用Lodash的_.isEqual方法,该方法执行对象的深层比较。

在我们完成之前,让我们看一下有关对象的一些常见问题。 这将有助于深入研究该主题并将所获得的知识付诸实践。

阅读答案之前,请尝试先考虑一下解决方案。

如何找出物体的长度?


为了获得答案,有必要对对象的所有属性进行逐一排序并对其进行计数。 有几种方法可以进行此迭代:

  • 在中 。 此方法涵盖了对象及其原型链的所有可数属性。 我们已经熟悉了原型(并且,我希望学习了材料),因此很明显, for in的使用并不总是正确的以获得对象的属性。
  • Object.keys 。 此方法返回一个具有其所有键(属于指定对象)的可计数属性的数组。 这种方法更好,因为我们只处理对象的属性,而不求助于prototype的属性。 但是,在某些情况下, 某些属性的可枚举属性设置为false,而object.keys最终将其跳过,并且得到的结果不正确。 这种情况很少发生,但是在这种情况下, getOwnPropertyNames将派上用场
  • getOwnPropertyNames返回一个包含对象自己所有键(可数和不可数)的数组。

还值得一提:

  • Object.values对其自身的计数属性进行迭代 ,并返回具有相应的数组。
  • Object.entries迭代其自身的计数属性,并返回包含键及其值的数组。

我认为您注意到上面列出的大多数方法都返回一个数组。 这是一个充分利用JavaScript方法处理数组的机会。

一种这样的方法是array.length 。 最后,我们可以写

 let objLength = Object.getOwnPropertyNames(obj).length; 

如何检查对象是否为空?


  1. JSON.stringify(myObj)===“ {}” 。 在这里,我们再次使用字符串转换工具,它使检查对象是否为空(比较字符串而不是对象)变得容易。
  2. !Object.keys(myobj).length // true 。 如前所述,将对象的键转换为数组可能非常有用。 在这里,我们使用从Array.prototype继承的便捷属性length ,并检查数组中键的长度。 在JS中, 0变为false,因此加 我们把它变成真实。 其他任何数字都将变为false。

总结


我希望现在您对创建对象和使用它们更有信心。 让我们总结一下:

  • 请记住,对象属于引用类型,这意味着建议在不更改原始对象的情况下使用它们。
  • 通过原型属性和原型链结交朋友。
  • 了解使用对象的辅助工具。 请记住,您可以将对象转换为字符串,使用其键获取数组或使用我们遇到的一组方法简单地对其属性进行迭代。

祝您学习JavaScript对象好运。

图片

Source: https://habr.com/ru/post/zh-CN419193/


All Articles