阿卡不拉阿卡不拉
Vue3
阿卡的博客
Vue3
阿卡的博客
  • Vue3

    • 快速入门

      • 搭建工程 👌
      • 模板语法
      • 响应式基础
      • 响应式常用 API
      • 计算属性
      • 类与样式绑定
      • 条件和列表渲染
      • 事件处理
      • 表单处理
      • 生命周期
      • 侦听器
      • 组件介绍
      • Props
      • 自定义事件
      • 组件v-model
      • 插槽
      • 前端路由介绍
      • KeepAlive内置组件
      • 状态管理库
      • 组件库介绍
    • 深入本质

      • 虚拟DOM本质
      • 模板的本质
      • 组件树和虚拟DOM树
      • 数据拦截的本质
      • 响应式数据的本质
      • 响应式的本质
      • 响应式和组件渲染
      • 实现响应式系统 1
      • 实现响应式系统 2
      • 图解EFFECT
      • 实现响应式系统 3
      • 手写computed
      • 手写watch
      • 指令的本质
      • 插槽的本质
      • v-model的本质
      • setup 语法标签
      • 组件生命周期
      • keepalive 生命周期
      • keepalive的本质
      • key的本质
    • 细节补充

      • 【Vue】属性透传
      • 【Vue】依赖注入
      • 【Vue】组合式函数 👌
      • 【Vue】自定义指令
      • 【Vue】插件
      • 【Vue】Transition
      • 【Vue】TransitionGroup
      • 【Vue】Teleport
      • 【Vue】异步组件
      • 【Vue】Suspense
      • 【Router】路由模式
      • 【Router】路由零碎知识
      • 【Router】路由匹配语法
      • 【Router】路由组件传参
      • 【Router】内置组件和函数
      • 【Router】导航守卫
      • 【Router】过渡特效
      • 【Router】滚动行为
      • 【Router】动态路由
      • 【State】通信方式总结
      • 【State】Pinia 自定义插件
      • 【场景】封装树形组件
      • 【场景】自定义 ref 实现防抖
      • 【场景】懒加载
      • 【场景】虚拟列表
      • 【场景】虚拟列表优化
      • 【第三方库】VueUse
      • 【第三方库】vuedragable
      • 【第三方库】vue-drag-resize
      • 【第三方库】vue-chartjs
      • 【第三方库】vuelidate
      • 【第三方库】vue3-lazyload
      • 【场景】Websocket 聊天室
      • 【Vite】✨ 认识 Vite👌
      • 【Vite】配置文件 👌
      • 【Vite】✨ 依赖预构建 👌
      • 【Vite】构建生产版本 👌
      • 【Vite】环境变量与模式
      • 【Vite】CLI
      • 【Vite】Vite 插件
  • 笔面试

    • HTML

      • HTML 面试题汇总
      • 文档声明
      • 语义化
      • W3C 标准组织
      • SEO
      • iframe
      • 微格式
      • 替换元素
      • 页面可见性
    • CSS

      • CSS 面试题汇总
      • CSS 单位总结
      • 居中方式总结
      • 隐藏元素方式总结
      • 浮动
      • 定位总结
      • BFC
      • CSS 属性计算过程
      • CSS 层叠继承规则总结
      • @import 指令
      • CSS3 calc 函数
      • CSS3 媒体查询
      • 过渡和动画事件
      • 渐进增强和优雅降级
      • CSS3 变形
      • 渐进式渲染
      • CSS 渲染性能优化
      • 层叠上下文
      • CSS3 遮罩
    • JavaScript

      • JavaScript 面试题汇总
      • ✨ let、var、const 的区别
      • JS中的数据类型
      • 包装类型
      • 数据类型的转换
      • 运算符
      • ✨ 原型链
      • ✨ this 指向
      • ✨ 垃圾回收与内存泄漏
      • ✨ 执行栈和执行上下文
      • ✨ 作用域和作用域链
      • ✨ 闭包
      • DOM 事件的注册和移除
      • DOM 事件的传播机制
      • 阻止事件默认行为
      • 递归
      • ✨ 属性描述符
      • class 和构造函数区别
      • 浮点数精度问题
      • 严格模式
      • ✨ 函数防抖和节流
      • ✨ WeakSet 和 WeakMap
      • ✨ 深浅拷贝
      • 函数柯里化
      • Node 事件循环
      • 尺寸和位置
    • 浏览器

      • 浏览器面试题汇总
      • ✨ 浏览器的渲染流程
      • ✨ 资源提示关键词
      • 浏览器的组成部分
      • IndexedDB
      • ✨ File API
      • ✨ 浏览器缓存
      • ✨ 浏览器跨标签页通信
      • Web Worker
    • 网络

      • 网络面试题汇总
      • 五层网络模型 👌
      • 常见请求方法 👌
      • ✨cookie👌
      • 面试题
      • 加密
      • ✨JWT👌
      • ✨ 同源策略及跨域问题 👌
      • 文件上传
      • ✨ 输入 url 地址之后
      • 文件下载
      • ✨ session
      • ✨ TCP
      • ✨ CSRF 攻击
      • ✨XSS 攻击 👌
      • ✨ 网络性能优化
      • 断点续传
      • 域名和 DNS
      • SSL、TLS、HTTPS 的关系
      • ✨ HTTP 各版本差异 👌
      • HTTP 缓存协议
      • ✨ WebSocket
    • 工程化

      • CMJ 和 ESM
      • npx
      • ESLint
    • Vue2

      • Vue 面试题汇总相关
      • 组件通信方式总结
      • 虚拟 DOM
      • v-model
      • 数据响应式原理
      • diff
      • 生命周期详解
      • computed
      • 过滤器
      • 作用域插槽
      • 过度和动画
      • 优化
      • keep-alive
      • 长列表优化
      • 其他 API
      • 模式和环境变量
      • 更多配置
      • 更多命令
      • 嵌套路由
      • 路由切换动画
    • Vue3

      • ✨ Vue3 整体变化 👌
      • ✨ Vue3 响应式变化 👌
      • ✨ nextTick 实现原理 👌
      • 两道代码题 👌
      • Vue 运行机制
      • 渲染器核心功能
      • 事件绑定与更新

✨ 属性描述符

经典真题

  • JavaScript 中对象的属性描述符有哪些?分别有什么作用?

属性描述符详解

在 JavaScript 中,对象的属性可以分为两种:

  • 数据属性:它的本质就是一个数据
  • 存取器属性:它的本质是一个函数,但是可以将它当作普通属性来使用,当给该属性赋值时,会运行相应的 setter 函数,当获取该属性的值时,会运行相应的 getter 函数。除了存取器,还有一些其他的关键字,用以表示当前属性是否可写、是否有默认值、是否可枚举等,这些关键字就是属性描述符。

属性描述符是 ECMAScript 5 新增的语法,它其实就是一个内部对象,用来描述对象的属性的特性。

属性描述符的结构

在定义对象、定义属性时,我们曾经介绍过属性描述符,属性描述符实际上就是一个对象。

属性描述符一共有 6 个,可以选择使用。

  • value:设置属性值,默认值为 undefined。
  • writable:设置属性值是否可写,默认值为 false。
  • enumerable:设置属性是否可枚举,即是否允许使用 for/in 语句或 Object.keys( ) 函数遍历访问,默认为 false。
  • configurable:当设置为 false 时,该属性的类型不能在数据属性和访问器属性之间更改,且该属性不可被删除,且其描述符的其他属性也不能被更改(但是,如果它是一个可写的数据描述符,则 value 可以被更改,writable 可以更改为 false)。默认值为 false。
  • get:取值函数,默认为 undefined。
  • set:存值函数,默认为 undefined。

注意这几个属性不是都可以一起设置,具体如下图:

image-20211021111647398

示例 1

下面示例演示了使用 value 读写属性值的基本用法。

var obj = {}; //定义空对象
Object.defineProperty(obj, "x", { value: 100 }); //添加属性x,值为100
console.log(Object.getOwnPropertyDescriptor(obj, "x").value); //返回100

示例 2

下面示例演示了使用 writable 属性禁止修改属性 x。

var obj = {};
Object.defineProperty(obj, "x", {
    value: 1, //设置属性默认值为1
    writable: false, //禁止修改属性值
});
obj.x = 2; //修改属性x的值
console.log(obj.x); // 1 说明修改失败

在正常模式下,如果 writable 为 false,重写属性值不会报错,但是操作失败,而在严格模式下则会抛出异常。

示例 3

configurable 可以禁止修改属性描述符,当其值为 false 时,value、writable、enumerable 和 configurable 禁止修改,同时禁止删除属性。

在下面示例中,当设置属性 x 禁止修改配置后,下面操作都是不允许的,其中 obj.x=5; 若操作失败,则后面 4 个操作方法都将抛出异常。

var obj = Object.defineProperty({}, "x", {
    configurable: false, // 禁止配置
});
obj.x = 5; //试图修改其值
console.log(obj.x); //修改失败,返回undefined
Object.defineProperty(obj, "x", { value: 2 }); //抛出异常
Object.defineProperty(obj, "x", { writable: true }); //抛出异常
Object.defineProperty(obj, "x", { enumerable: true }); //抛出异常
Object.defineProperty(obj, "x", { configurable: true }); //抛出异常

当 configurable 为 false 时,如果把 writable=true 改为 false 是允许的。只要 writable 或 configurable 有一个为 true,则 value 也允许修改。

get 和 set 函数

除了使用点语法或中括号语法访问属性的 value 外,还可以使用访问器,包括 set 和 get 两个函数。

其中,set( ) 函数可以设置 value 属性值,而 get( ) 函数可以读取 value 属性值。

借助访问器,可以为属性的 value 设计高级功能,如禁用部分特性、设计访问条件、利用内部变量或属性进行数据处理等。

示例 1

下面示例设计对象 obj 的 x 属性值必须为数字。为属性 x 定义了 get 和 set 特性,obj.x 取值时,就会调用 get;赋值时,就会调用 set。

var obj = Object.create(Object.prototype, {
    _x: {
        //数据属性
        value: 1, //初始值
        writable: true,
    },
    x: {
        //访问器属性
        get: function () {
            //getter
            return this._x; //返回_x属性值
        },
        set: function (value) {
            //setter
            if (typeof value != "number") {
                throw new Error("请输入数字");
            }
            this._x = value; //赋值
        },
    },
});
console.log(obj.x); //1
obj.x = "2"; //抛出异常

示例 2

JavaScript 也支持一种简写方法。针对示例 1,通过以下方式可以快速定义属性。

var obj = {
    _x: 1, // 定义 _x 属性
    get x() {
        return this._x;
    }, //定义 x 属性的 getter
    set x(value) {
        //定义 x 属性的 setter
        if (typeof value != "number") {
            throw new Error("请输入数字");
        }
        this._x = value; // 赋值
    },
};
console.log(obj.x); //1
obj.x = 2;
console.log(obj.x); //2

取值函数 get( ) 不能接收参数,存值函数 set( ) 只能接收一个参数,用于设置属性的值。

操作属性描述符

属性描述符是一个内部对象,无法直接读写,可以通过下面几个函数进行操作。

  • Object.getOwnPropertyDescriptor( ):可以读出指定对象私有属性的属性描述符。
  • Object.defineProperty( ):通过定义属性描述符来定义或修改一个属性,然后返回修改后的描述符。
  • Object.defineProperties( ):可以同时定义多个属性描述符。
  • Object.getOwnPropertyNames( ):获取对象的所有私有属性。
  • Object.keys( ):获取对象的所有本地可枚举的属性。
  • propertyIsEnumerable( ):对象实例方法,直接调用,判断指定的属性是否可枚举。

示例 1

在下面示例中,定义 obj 的 x 属性允许配置特性,然后使用 Object.getOwnPropertyDescriptor( ) 函数获取对象 obj 的 x 属性的属性描述符。修改属性描述符的 set 函数,重设检测条件,允许非数值型数字赋值。

var obj = Object.create(Object.prototype, {
    _x: {
        //数据属性
        value: 1, //初始值
        writable: true,
    },
    x: {
        //访问器属性
        configurable: true, //允许修改配置
        get: function () {
            //getter
            return this._x; //返回_x属性值
        },
        set: function (value) {
            if (typeof value != "number") {
                throw new Error("请输入数字");
            }
            this._x = value; //赋值
        },
    },
});
var des = Object.getOwnPropertyDescriptor(obj, "x"); //获取属性x的属性描述符
des.set = function (value) {
    //修改属性x的属性描述符set函数
    //允许非数值型的数字,也可以进行赋值
    if (typeof value != "number" && isNaN(value * 1)) {
        throw new Error("请输入数字");
    }
    this._x = value;
};
obj = Object.defineProperty(obj, "x", des);
console.log(obj.x); //1
obj.x = "2"; //把一个给数值型数字赋值给属性x
console.log(obj.x); //2

示例 2

下面示例先定义一个扩展函数,使用它可以把一个对象包含的属性以及丰富的信息复制给另一个对象。

【实现代码】

function extend(toObj, fromObj) {
    //扩展对象
    for (var property in fromObj) {
        //遍历对象属性
        if (!fromObj.hasOwnProperty(property)) continue; //过滤掉继承属性
        Object.defineProperty(
            //复制完整的属性信息
            toObj, //目标对象
            property, //私有属性
            Object.getOwnPropertyDescriptor(fromObj, property) //获取属性描述符
        );
    }
    return toObj; //返回目标对象
}

【应用代码】

var obj = {}; //新建对象
obj.x = 1; //定义对象属性
extend(obj, {
    get y() {
        return 2;
    },
}); //定义读取器对象
console.log(obj.y); //2

控制对象状态

JavaScript 提供了 3 种方法,用来精确控制一个对象的读写状态,防止对象被改变。

  • Object.preventExtensions:阻止为对象添加新的属性。
  • Object.seal:阻止为对象添加新的属性,同时也无法删除旧属性。等价于属性描述符的 configurable 属性设为 false。注意,该方法不影响修改某个属性的值。
  • Object.freeze:阻止为一个对象添加新属性、删除旧属性、修改属性值。

同时提供了 3 个对应的辅助检查函数,简单说明如下:

  • Object.isExtensible:检查一个对象是否允许添加新的属性。
  • Object.isSealed:检查一个对象是否使用了 Object.seal 方法。
  • Object.isFrozen:检查一个对象是否使用了 Object.freeze 方法。

示例

下面代码分别使用 Object.preventExtensions、Object.seal 和 Object.freeze 函数控制对象的状态,然后再使用 Object.isExtensible、Object.isSealed 和 Object.isFrozen 函数检测对象的状态。

var obj1 = {};
console.log(Object.isExtensible(obj1)); //true
Object.preventExtensions(obj1);
console.log(Object.isExtensible(obj1)); //false
var obj2 = {};
console.log(Object.isSealed(obj2)); //true
Object.seal(obj2);
console.log(Object.isSealed(obj2)); //false
var obj3 = {};
console.log(Object.isFrozen(obj3)); //true
Object.freeze(obj3);
console.log(Object.isFrozen(obj3)); //false

真题解答

  • JavaScript 中对象的属性描述符有哪些?分别有什么作用?

参考答案:

属性描述符一共有 6 个,可以选择使用。

  • value:设置属性值,默认值为 undefined。
  • writable:设置属性值是否可写,默认值为 false。
  • enumerable:设置属性是否可枚举,即是否允许使用 for/in 语句或 Object.keys( ) 函数遍历访问,默认为 false。
  • configurable:当设置为 false 时,该属性的类型不能在数据属性和访问器属性之间更改,且该属性不可被删除,且其描述符的其他属性也不能被更改(但是,如果它是一个可写的数据描述符,则 value 可以被更改,writable 可以更改为 false)。默认值为 false。
  • get:取值函数,默认为 undefined。
  • set:存值函数,默认为 undefined。

使用属性描述符的时候,get 和 set 以及 value 和 writable 这两组是互斥的,设置了 get 和 set 就不能设置 value 和 writable,反之设置了 value 和 writable 也就不可以设置 get 和 set。

-EOF-

最近更新:: 2025/7/16 12:57
Contributors: AK
Prev
递归
Next
class 和构造函数区别