充分了解JavaScript中【对象】的概念(二)

简介: 这篇文章我们继续来讲解JavaScript中【对象】的概念,因为这是一系列的文章,所以建议从第一篇文章开始看,没看过之前文章的小伙伴,可以点击以上链接进行查看。

一、对象的属性


定义:一个对象内部的每个名/值对就是该对象的一个属性,例如 {x:1 ,y:2} 中, x:1 就是该对象的一个属性。


属性有两种类型:


  1. 自有属性: 直接定义在对象中的属性,例如 let obj = {x:1}中,属性x就是该对象的自有属性


  1. 继承属性: 在对象的原型中定义的属性,例如 Object.prototype 中的属性 toString


属性特性一共有四种:


  1. 值: 顾名思义,表示该属性的值


  1. 可写性: 表示是否可以设置该属性的值


  1. 可枚举性: 表示是否可以通过 for / in 循环返回属性的值


  1. 可配置性: 表示是否可以删除或修改该属性


注意:这里列举了 属性的类型属性的特性 ,在下面讲解属性的相关知识时,都会涉及到,所以大家请先尽力记住,这对下面的理解有帮助。


(1)属性的查询与设置


当我们要设置或者获取一个对象的属性时,我们一般都是这么做的


    let obj = {  name: 'Lpyexplore',  age: 21,  gender: 0}
    let name = obj.name     //获取obj对象中的name属性  Lpyexplorelet age = obj["age"]    //获取obj对象中的age属性    21let fn = obj.toString   //获取obj对象中的继承属性toString    [Function: toString] obj["like"] = "python"  //给obj对象设置一个like属性,值为pythonobj.weight = 128        //给obj对象设置一个weight属性,值为128obj.age = 22            //修改obj对象中的age属性,值改为22
    console.log(obj.height) //获取obj对象中的height属性console.log(obj)        //查看一下obj对象的变化/*
    undefined
    {  name: 'Lpyexplore',  age: 22,  gender: 0,  like: 'python',  weight: 128}
    */


    从这个例子中我们可以看到,查询一个对象的属性有两种方式,第一种是通过点(.)来访问到某属性,即 对象.属性名 ; 第二种是通过方括号([ ])来访问某属性,即 对象[属性名]。当访问对象中不存在的属性时,会返回undefined


    那么给对象设置一个属性值就更简单了,查询到该属性值后,直接给它赋值就可以设置属性值(若对象内不存在该属性)或修改属性值(对象内已存在该属性值)


    (2)属性的删除


    删除对象的自有属性,需要用到运算符 delete,直接来看例子


      let obj = {    x:1,    y:2,    z:3}
      delete obj.x         //删除对象obj中的属性x,返回truedelete obj["y"]      //删除对象obj中的属性y,返回truedelete obj.toString  //toString是对象obj的原型中的属性,属于继承属性,无法删除该属性,但仍然返回true
      console.log(obj.x)   //查询对象obj中的属性xconsole.log(obj)     //查看对象obj
      /*undefined{z:3}*/


      在这个例子中可以看到,我们准备删除对象obj中的继承属性toString时,未做任何操作,所以 delete 只能删除对象的自有属性


      (3)属性的检测


      我们有时需要检测对象的属性,即判断该对象中是否有某个属性 、该属性是否为该对象的自有属性 、该对象是否是可枚举的等等


      • 通过 in 运算符判断属性是否存在


      let obj = {  x: 1,  y: undefined}
      "x" in obj        //返回true,表示对象obj中有属性x"y" in obj        //返回true,表示对象obj中有属性y,只不过值为undefined"z" in obj        //返回false,表示对象obj中不存在属性zdelete obj.x      //删除对象obj中的x属性"x" in obj        //返回false,表示对象obj中不存在属性x


      • 通过对象的 hasOwnProperty( )方法判断属性是否为自有属性


      let obj = {  x:1}
      obj.hasOwnProperty("x")     //返回true,表明属性x存在,且为obj的自有属性obj.hasOwnProperty("z")     //返回false,属性zu不存在于对象obj中obj.hasOwnProperty("toString")  //返回false,属性toString是obj的继承属性,不是自有属性


      • 通过对象的 propertyIsEnumerable( )方法判断属性是否为自有属性,且该属性具有可枚举性


      let obj = Object.create({x:1}) //新建一个对象obj,继承于对象 {x:1}obj.y = 2                   //给obj对象设置一个属性y,其值为2
      obj.propertyIsEnumerable("y")  //返回true,表示属性y为该对象的自有属性,且具有可枚举性obj.propertyIsEnumerable("x")  //返回false,因为属性x是继承属性,继承于对象{x:1}Object.prototype.propertyIsEnumerable("toString")   //返回false,虽然属性toString是对象Object.prototype的自有属性,但它不具有可枚举性


      这里提到了可枚举性, 我们来举个例子


        let obj = {  x:1,   y:2,  z:3}
        for(let i in obj) {    //遍历对象obj的自有属性  console.log(i)}
        // 输出  1  2  3

        可以看到对象obj的自有属性都被 for / in 全部遍历了出来,这就是可枚举性的体现。

        其实在ES5中,提供了两个便利属性的函数,我们来了解一下


        • Object.keys( )


        这个函数是会返回一个数组,数组中的元素就是对象中可枚举的自有属性名,来看一下例子


          let obj = {    x:1,    y:2,    z:3}
          Object.keys(obj)   //    ['x', 'y', 'z']


          • Object.gerOwnPropertyNames( )


          这个函数与Object.keys() 类似,区别就在于,该函数返回的是对象中所有自有属性的名称,即不管属性是否具有可枚举性都能被返回。


          let obj = {    x:1,    y:2,    z:3}
          Object.gerOwnPropertyNames(obj)   //    ['x', 'y', 'z']


          注意: 因为我们还没有讲到如何将一个属性变为可枚举或变为不可枚举,所以这两个函数的区别没办法很好的体现,接下来我们就来讲解一下如何设置属性的三个特性,其中也包括如何设置可枚举性。等了解完如何设置属性的可枚举性后,我们再来尝试一下这两个函数的区别,应该就很好理解了。


          (4)特殊的属性


          常见的对象属性一般都是名/值对的形式,即 x:1 这样的,我们把这种形式的属性叫做数据属性。在ES5中,提供了一种新的属性形式,叫做存储器属性,该属性可以用两种方法定义,他们分别是 getter 和 setter ,存储器属性在对象中的存在形式不是名/值对的样子,而是类似于我们平时定义函数的样子,function fn() {}。定义存储器属性就是用get(getter)或set(setter) 代替关键字 function,fn就是该属性的名字,有点抽象,来看例子吧


            let obj = {  x:1,  y:2,  get r() {     //用getter方法定义了属性r,在查询该属性时,调用该函数    return this.x + 2      },  set r(data) {   //用setter方法定义了属性r,在给属性r赋值时,调用该函数,并将值作为该函数的参数    this.y * data;    return this.y  }}
            let r = obj.r        //查询对象obj中的属性r,返回 3obj.r = 7            //给对象obj的属性r赋值为7,  返回 14


            从上面这个例子中可以得出以下的结论


            1. 用getter方法定义了属性,在查询该属性值时,会调用getter方法定义的函数名为该属性的函数


            1. 用setter方法定义了函数,在给该属性赋值时,会调用setter方法定义的函数名为该属性的函数


            1. 通过前两条结论,可以知道,如果一个存储器属性具有getter方法,则该属性可读;如果具有setter方法,则该属性可写;同时拥有两个方法的话,则该属性是一个可读写的属性。


            (5)属性的特性


            在第四部分的开头,我们说了属性有四个特性,即值 、可写性 、可枚举性 、可配置性,忘记了的小伙伴翻到前面再看一下。


            一般我们创建的数据属性,都是具有这四个特性的(值 、可写性 、可枚举性 、可配置性), 存储器属性是不具有值和可写性两个特性的,但他也具有四个特性,他们分别为:读取(get)、写入(set)、可枚举性 和 可配置性。


            在这里我们先引入一个概念,也是ES5定义的一个对象,叫做属性描述符,这个对象就代表了属性的四个特性。属性描述符对象里的属性有 value(值)writable(可写性)enumerable(可枚举性)configurable(可配置性)get(读取)set(写入)


            通过 Object.getOwnPropertyDescriptor( ) 可以获得一个对象中某个属性的属性描述符。该方法第一个参数为对象,第二个参数为需要查询的该对象中的属性名。接下来我们来实战一下


            • 查看数据属性的属性描述符


            //先创建一个对象objlet obj = {  x:1}
            Object.getOwnPropertyDescriptor(obj, "x")    //查询对象obj中属性x的属性描述符
            // 返回 {value: 1, writable: true, enumerable: true, configurable: true}


            • 查看存储器属性的属性描述符


              //先创建一个对象objlet obj = {  get r() {    return this.x + 1  },  set r(data) {    this.x = data  }}
              Object.getOwnPropertyDescriptor(obj, "r")   //查询对象obj中属性r的属性描述符
              // 返回 {get: [Function: get r], set: [Function: set r], enumerable: true, configurable: true}


              getOwnPropertyDescriptor()只能获取到一个属性的属性描述符,如果我们想要修改某个属性的特性的话,我们需要用到另一个方法,即 Object.defineProperty() ,他的第一个参数是对象;第二个参数是需要创建或者修改的属性名;第三个参数是属性描述符对象。


              直接来看两个实战例子


              • 修改对象中属性的特性


                //创建一个对象let obj = {  x:1}
                //先用propertyIsEnumerable()来测试一下对象obj中的属性x是否还具有可枚举性obj.propertyIsEnumerable("x")   //返回true,说明此时属性x是具有可枚举性的
                //修改对象obj中属性x的属性特性Object.defineProperty(obj, "x", {  value: 2,                //属性x的值变为2  writable: true,          //属性x具有可写性  enumerable: false,       //属性x不具有可枚举性  configurable: true       //属性x具有可配置性})
                //利用 propertyIsEnumerable()来测试一下对象obj中的属性x是否还具有可枚举性obj.propertyIsEnumerable("x")      // 返回 false,说明对象obj中属性x已经不具有可枚举性了


                • 给对象创建一个属性,并设置该属性的特性


                  //创建对象objlet obj = {  x:2}
                  //给对象obj创建一个属性,并配置好该属性的特性Object.defineProperty(obj, "r", {  get: function() {return this.x + 1},      //给存储器属性r设定一个get函数  set: function(data) {this.x *= data}, //给存储器属性r设定一个set函数  enumerable: true,             //存储器属性r具有可枚举性  configurable: true            //存储器属性r具有可配置性})
                  //查询对象obj中的属性robj.r                // 返回 3
                  //给对象obj中的属性r赋值obj.r = 3  
                  //查询对象obj中的属性x的值obj.x        //返回  6


                  • 给对象同时创建多个属性,并为每个属性配置属性特性


                  这里要用到另一个方法,即 Object.defineProperties() ,这个方法跟 Object.defineProperty() 类似。前者一共有两个参数,第一个参数为对象;第二个参数为一个对象,并且该对象内部是以名/值对的形式存在的,即 需要修改的属性名: 属性描述符对象。接下来我们直接来看实战例子


                    let obj = {  x:1}
                    Object.defineProperties(obj, {  x: {    value: 3,    writable: true,    enumerable: true,    configurable: true  },  y: {    value: 10,    writable: true,    enumerable: true,    configurable: true  }})
                    console.log(obj)
                    // 返回 {x:3, y:10}


                    在这里简单总结一下


                    1. Object.defineProperty() 这个方法有两个作用,第一个是修改属性的特性;第二个作用就是给一个对象创建属性


                    1. Object.defineProperty()Object.getOwnPropertyDescriptor() 可以搭配着使用,尤其是在你不知道一个属性的特性的时候,可以先用后者获取它的属性描述符对象,再根据属性描述符对象调用前者去修改属性的特性。


                    1. 无论是 Object.defineProperty() 还是 Object.defineProperties() ,他们都只能修改对象的自有属性,无法修改他们的继承属性。


                    好了,因为【对象】的概念较多,所以今天的文章就讲到这里,之后我会继续更新,欢迎大家继续追更~

                    相关文章
                    |
                    29天前
                    |
                    JavaScript 前端开发
                    如何在 JavaScript 中使用 __proto__ 实现对象的继承?
                    使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
                    |
                    17天前
                    |
                    Web App开发 JavaScript 前端开发
                    Node.js 是一种基于 Chrome V8 引擎的后端开发技术,以其高效、灵活著称。本文将介绍 Node.js 的基础概念
                    Node.js 是一种基于 Chrome V8 引擎的后端开发技术,以其高效、灵活著称。本文将介绍 Node.js 的基础概念,包括事件驱动、单线程模型和模块系统;探讨其安装配置、核心模块使用、实战应用如搭建 Web 服务器、文件操作及实时通信;分析项目结构与开发流程,讨论其优势与挑战,并通过案例展示 Node.js 在实际项目中的应用,旨在帮助开发者更好地掌握这一强大工具。
                    39 1
                    |
                    1月前
                    |
                    Web App开发 JavaScript 前端开发
                    如何确保 Math 对象的方法在不同的 JavaScript 环境中具有一致的精度?
                    【10月更文挑战第29天】通过遵循标准和最佳实践、采用固定精度计算、进行全面的测试与验证、避免隐式类型转换以及持续关注和更新等方法,可以在很大程度上确保Math对象的方法在不同的JavaScript环境中具有一致的精度,从而提高代码的可靠性和可移植性。
                    |
                    20天前
                    |
                    JSON 前端开发 JavaScript
                    JavaScript中对象的数据拷贝
                    本文介绍了JavaScript中对象数据拷贝的问题及解决方案。作者首先解释了对象赋值时地址共享导致的值同步变化现象,随后提供了五种解决方法:手动复制、`Object.assign`、扩展运算符、`JSON.stringify`与`JSON.parse`组合以及自定义深拷贝函数。每种方法都有其适用场景和局限性,文章最后鼓励读者关注作者以获取更多前端知识分享。
                    18 1
                    JavaScript中对象的数据拷贝
                    |
                    17天前
                    |
                    缓存 前端开发 JavaScript
                    JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式
                    本文深入解析了JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式(Hash路由和History路由)、优点及挑战,并通过实际案例分析,帮助开发者更好地理解和应用这一关键技术,提升用户体验。
                    47 1
                    |
                    1月前
                    |
                    JavaScript 前端开发 图形学
                    JavaScript 中 Math 对象常用方法
                    【10月更文挑战第29天】JavaScript中的Math对象提供了丰富多样的数学方法,涵盖了基本数学运算、幂运算、开方、随机数生成、极值获取以及三角函数等多个方面,为各种数学相关的计算和处理提供了强大的支持,是JavaScript编程中不可或缺的一部分。
                    |
                    2月前
                    |
                    设计模式 JavaScript 前端开发
                    探索JavaScript中的闭包:从基础概念到实际应用
                    在本文中,我们将深入探讨JavaScript中的一个重要概念——闭包。闭包是一种强大的编程工具,它允许函数记住并访问其所在作用域的变量,即使该函数在其作用域之外被调用。通过详细解析闭包的定义、创建方法以及实际应用场景,本文旨在帮助读者不仅理解闭包的理论概念,还能在实际开发中灵活运用这一技巧。
                    |
                    2月前
                    |
                    缓存 JavaScript 前端开发
                    JavaScript中数组、对象等循环遍历的常用方法介绍(二)
                    JavaScript中数组、对象等循环遍历的常用方法介绍(二)
                    43 1
                    |
                    2月前
                    |
                    缓存 JavaScript 前端开发
                    深入了解JavaScript的闭包:概念与应用
                    【10月更文挑战第8天】深入了解JavaScript的闭包:概念与应用
                    |
                    2月前
                    |
                    JavaScript 前端开发 大数据
                    在JavaScript中,Object.assign()方法或展开语法(...)来合并对象,Object.freeze()方法来冻结对象,防止对象被修改
                    在JavaScript中,Object.assign()方法或展开语法(...)来合并对象,Object.freeze()方法来冻结对象,防止对象被修改
                    31 0