一文看懂js中所有属性访问api

js对象属性的遍历有很多方法,比如for in、Object.keys、Reflect.ownKeys等等,用起来不知道选哪个,记起来麻烦,本文将介绍所有属性访问相关api的能力和区别。

前置知识:对象属性的「3 种分类」

所有遍历方法的核心差异,本质都是「能遍历哪些类型的属性」,必须先明确对象属性的 3 类划分:

1. 可枚举属性 vs 不可枚举属性

属性的 enumerable 特性为 true 是「可枚举属性」,false 是「不可枚举属性」;

  • 默认:我们直接给对象定义的属性(obj.name = 'xxx')、字面量声明的属性,默认可枚举;
  • 不可枚举:JS 内置属性(比如 obj.toString、obj.__proto__)、通过 Object.defineProperty 手动设置 enumerable: false 的属性,都是不可枚举属性。

2. 自有属性(自有属性) vs 继承属性

  • 自有属性:对象自己本身拥有 的属性(挂载在对象自身,不在 __proto__ 原型链上);
  • 继承属性:从原型链 上继承过来的属性(比如 obj.hasOwnProperty、obj.toString 都是从 Object.prototype 继承的)。

3. 字符串属性 vs Symbol 属性

JS 对象的属性名,可以是「字符串」或「Symbol 类型」(ES6 新增);

  • 字符串属性:最常用的 obj.name、obj['age'] 都属于此类;
  • Symbol 属性:const s = Symbol('id'); obj[s] = 100,这种属性名是唯一的,默认不会被常规遍历方法捕获。

不同属性访问(遍历/判断)方法区别

方法 访问范围 能否访问 Symbol 能否访问不可枚举 能否访问继承属性 返回值 / 形式
JSON.stringify 可枚举字符串属性 否 否 否 JSON字符串
for...in 可枚举字符串属性 否 否 否 循环,直接拿 key
Object.keys 自有可枚举字符串属性 否 否 否 字符串数组
Object.values 自有可枚举字符串属性 否 否 否 值的数组
Object.entries 自有可枚举字符串属性 否 否 否 二维键值对数组
Object.getOwnPropertyNames 自有所有字符串属性 否 否 否 字符串数组
Object.getOwnPropertySymbols 自有所有 Symbol 属性 是 是 否 Symbol 数组
Reflect.ownKeys 自有所有属性 是 是 否 混合类型数组
Object.getOwnPropertyDescriptors 自有所有属性 是 是 否 属性描述符对象
hasOwnProperty 自有所有属性 是 是 否 布尔
Object.hasOwn 自有所有属性 是 是 否 布尔
in 所有属性 是 是 是 布尔
Reflect.has 所有属性 是 是 是 布尔

规律小结

  • 最局限的5个方法刚好也是最常用的 JSON.stringify,for...in,Object.keys ,Object.values ,Object.entries ,既无法访问symbol属性,也无法访问不可枚举和继承属性。
  • 带own的api,都无法访问继承属性
  • in操作符和Reflect.has 可以访问到所有属性

关于继承的说明

本文中的继承,是指原型链访问,并非面向对象中的类继承(extends)。

js 复制代码
class User  {
    name = "小王"; //自有属性
    work() {}
}
const p = new User();

这里的p对象的name属性,是它的自有属性,而work方法继承自原型链,因此own类方法无法访问到work。

es6中的继承,沿用了es5中的属性私有,方法公开的继承最佳实践,因此es6中extends自父类的成员属性,也是该实例自有属性。

附

测试代码和截图如下

js 复制代码
  class Persion {
        alive = true; //继承属性
        study() {}
      }
      class User extends Persion {
        name = "小王"; //自有属性
        work() {}
      }
      const p = new User();
      //wealth 不可枚举
      Object.defineProperty(p, "wealth", {
        enumerable: false,
        writable: true,
        value: 1000000,
      });
      p.age = 18; //age自有属性
      const CAREER = Symbol("career");
      const ID = Symbol("id");
      p[ID] = "9523"; //ID symbol属性
      Object.defineProperty(p, CAREER, {
        enumerable: false,
        writable: true,
        value: "detective",
      });
      console.group("for in");
      for (const key in p) {
        console.log(key); // alive name age
      }
      console.groupEnd("for in");
      console.log(Object.keys(p)); // ['alive', 'name', 'age']
      console.log(Object.values(p)); //[true, '小王', 18]
      console.log(Object.entries(p)); // '[["alive",true],["name","小王"],["age",18]]'
      // -----------------------可得到非symbol自有属性,无法获取不可枚举属性
      console.log("Object.getOwnPropertyNames", Object.getOwnPropertyNames(p)); // ['alive', 'name', 'wealth', 'age']
      //------------------------可得到symbol属性,包括不可枚举的symbol
      console.log(
        "Object.getOwnPropertySymbols",
        Object.getOwnPropertySymbols(p)
      ); //[Symbol(id),Symbol(career)]
      //------------------------可得到所有自有属性,包括不可枚举和symbol属性,无法获取继承属性
      console.log("Reflect.ownKeys", Reflect.ownKeys(p)); // ['alive', 'name', 'wealth', 'age', Symbol(id),Symbol(career)]
      //------------------------可得到所有自有属性,包括不可枚举和symbol属性,无法获取继承属性
      console.log(
        "Object.getOwnPropertyDescriptors",
        Object.getOwnPropertyDescriptors(p)
      );

      //继承属性返回false
      console.group("p.hasOwnProperty");
      ["alive", "study", "work", "name", "age", "wealth", ID, CAREER].forEach(
        (name) => {
          console.log(
            `p.hasOwnProperty(${name.toString()})`,
            p.hasOwnProperty(name)
          );
        }
      );
      console.groupEnd();
      //继承属性返回false
      console.group("Object.hasOwn");
      ["alive", "study", "work", "name", "age", "wealth", ID, CAREER].forEach(
        (name) => {
          console.log(
            `Object.hasOwn(p, ${name.toString()})`,
            Object.hasOwn(p, name)
          );
        }
      );
      console.groupEnd();
      // 可判断所有属性,包括不可枚举/symbol/继承
      console.group("in");
      ["alive", "study", "work", "name", "age", "wealth", ID, CAREER].forEach(
        (name) => {
          console.log(`${name.toString()} in p`, name in p);
        }
      );
      console.groupEnd();
      // 可判断所有属性,包括不可枚举/symbol/继承
      console.group("Reflect.has");
      ["alive", "study", "work", "name", "age", "wealth", ID, CAREER].forEach(
        (name) => {
          console.log(
            `Reflect.has(p, ${name.toString()})`,
            Reflect.has(p, name)
          );
        }
      );
      console.groupEnd();
相关推荐
奕鼎竜瑆6 小时前
Solid 前端响应式开发从零到精通
前端·人工智能
动恰客流统计6 小时前
景区客流统计怎么做?兼顾管控与运营的实施方案解析
大数据·前端·人工智能
派小心.7 小时前
A/B测试工具免费版够用吗?
前端·数据分析
前端snow7 小时前
ai agent --- redis 缓存
前端
duanshu_8 小时前
官网表单与下载异常怎么排查?五个环节定位故障
前端·自动化·flyweight
Zhou1411368 小时前
SpringBoot_03_Web开发
前端·spring boot·python
辻弋2018 小时前
【无标题】
大数据·服务器·前端·搜索引擎·开源软件
FluxArt9 小时前
GPT Image 2.5 中文提示词教程:Flux Art 可复用模板
前端·gpt·算法
搬砖的小码农_Sky10 小时前
AI Agent:Windows上Node.js安装后 npm -v报错
前端·npm·node.js·人机交互
计算机魔术师10 小时前
半年翻倍!OpenAI再融300亿,估值冲到1.4万亿
前端