JavaScript-修炼之路第五层

本文探讨了JavaScript中的Object与Array对象的详细用法,包括Object的静态方法如Object.keys、实例方法,以及如何利用Object冻结对象属性、检查对象类型。还介绍了Array的创建、不同构造方式的差异,以及数组的常用方法如concat、slice等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

五、标准库

1,Object 对象

JavaScript 原生提供 Object 对象(注意起首的 O 是大写),本章介绍该对象原生的各种方 法。 JavaScript 的所有其他对象都继承自 Object 对象,即那些对象都是 Object 的实例。 Object 对象的原生方法分成两类: Object 本身的方法与 Object 的实例方法。

(1) Object 对象本身的方法

所谓“本身的方法”就是直接定义在 Object 对象的方法。

1. Object.print = function (o) { console.log(o) };

(2) Object 的实例方法

所谓实例方法就是定义在 Object 原型对象 Object.prototype 上的方法。它可以被 Object 实 例直接使用。

1. Object.prototype.print = function () {
2.     console.log(this);
3. };
4.
5. var obj = new Object();
6. obj.print() // Object

上面代码中, Object.prototype 定义了一个 print 方法,然后生成一个 Object 的实 例 obj 。 obj 直接继承了 Object.prototype 的属性和方法,可以直接使用 obj.print 调 用 print 方法。也就是说, obj 对象的 print 方法实质上就是调 用 Object.prototype.print 方法。

Object 本身是一个函数,可以当作工具方法使用,将任意值转为对象。这个方法常用于保证某个值 一定是对象。 如果参数为空(或者为 undefined 和 null ), Object() 返回一个空对象。

1. var obj = Object();
2. // 等同于
3. var obj = Object(undefined);
4. var obj = Object(null);
5.
6. obj instanceof Object // true

instanceof 运算符用来验证,一个对象是否为指定的构造函数的实例。 obj instanceof Object 返回 true ,就表示 obj 对象是 Object 的实例。

1. var obj = Object(1);
2. obj instanceof Object // true
3. obj instanceof Number // true
4.
5. var obj = Object('foo');
6. obj instanceof Object // true
7. obj instanceof String // true
8.
9. var obj = Object(true);
10. obj instanceof Object // true
11. obj instanceof Boolean // true

如果 Object 方法的参数是一个对象,它总是返回该对象,即不用转换。

1. var arr = [];
2. var obj = Object(arr); // 返回原数组
3. obj === arr // true
4.
5. var value = {};
6. var obj = Object(value) // 返回原对象
7. obj === value // true
8.
9. var fn = function () {};
10. var obj = Object(fn); // 返回原函数
11. obj === fn // true

利用这一点,可以写一个判断变量是否为对象的函数。

1. function isObject(value) {
2.     return value === Object(value);
3. }
4.
5. isObject([]) // true
6. isObject(true) // false

Object 不仅可以当作工具函数使用,还可以当作构造函数使用,即前面可以使用 new 命令。 Object 构造函数的首要用途,是直接通过它来生成新对象。

1. var obj = new Object();

注意,通过 var obj = new Object() 的写法生成新对象,与字面量的写法 var obj = {} 是等价 的。或者说,后者只是前者的一种简便写法。

Object 构造函数的用法与工具方法很相似,几乎一模一样。使用时,可以接受一个参数,如果该参 数是一个对象,则直接返回这个对象;如果是一个原始类型的值,则返回该值对应的包装对象

1. var o1 = {a: 1};
2. var o2 = new Object(o1);
3. o1 === o2 // true
4.
5. var obj = new Object(123);
6. obj instanceof Number // true

虽然用法相似,但是 Object(value) 与 new Object(value) 两者的语义是不同的, Object(value) 表示将 value 转成一个对象, new Object(value) 则表示新生成一个对 象,它的值是 value 。

Object 的静态方法:所谓“静态方法”,是指部署在 Object 对象自身的方法。

Object.keys(),Object.getOwnPropertyNames()

Object.keys 方法和 Object.getOwnPropertyNames 方法都用来遍历对象的属性。 Object.keys 方法的参数是一个对象,返回一个数组。该数组的成员都是该对象自身的(而不是继 承的)所有属性名。

1. var obj = {
2.     p1: 123,
3.     p2: 456
4. };
5.
6. Object.keys(obj) // ["p1", "p2"]

Object.getOwnPropertyNames 方法与 Object.keys 类似,也是接受一个对象作为参数,返回一 个数组,包含了该对象自身的所有属性名。

1. var obj = {
2.     p1: 123,
3.     p2: 456
4. };
5.
6. Object.getOwnPropertyNames(obj) // ["p1", "p2"]

对于一般的对象来说, Object.keys() 和 Object.getOwnPropertyNames() 返回的结果是一样 的。只有涉及不可枚举属性时,才会有不一样的结果。

1. var a = ['Hello', 'World'];
2.
3. Object.keys(a) // ["0", "1"]
4. Object.getOwnPropertyNames(a) // ["0", "1", "length"]

在 Object.getOwnPropertyNames 方法的返回结果中。 由于 JavaScript 没有提供计算对象属性个数的方法,所以可以用这两个方法代替。

1. var obj = {
2.     p1: 123,
3.     p2: 456
4. };
5.
6. Object.keys(obj).length // 2
7. Object.getOwnPropertyNames(obj).length // 2

除了上面提到的两个方法, Object 还有不少其他静态方法:

(1)对象属性模型的相关方法

        Object.getOwnPropertyDescriptor() :获取某个属性的描述对象。

        Object.defineProperty() :通过描述对象,定义某个属性。

        Object.defineProperties() :通过描述对象,定义多个属性。

(2)控制对象状态的方法

        Object.preventExtensions() :防止对象扩展。

        Object.isExtensible() :判断对象是否可扩展。

        Object.seal() :禁止对象配置。

        Object.isSealed() :判断一个对象是否可配置。

        Object.freeze() :冻结一个对象。

        Object.isFrozen() :判断一个对象是否被冻结。

(3)原型链相关方法

        Object.create() :该方法可以指定原型对象和属性,返回一个新的对象。         Object.getPrototypeOf() :获取对象的 Prototype 对象。

Object 的实例方法:除了静态方法,还有不少方法定义在 Object.prototype 对象。它们称为实例方法,所 有 Object 的实例对象都继承了这些方法。

Object 实例对象的方法,主要有以下六个。

        Object.prototype.valueOf() :返回当前对象对应的值。

        Object.prototype.toString() :返回当前对象对应的字符串形式。         Object.prototype.toLocaleString() :返回当前对象对应的本地字符串形式。         Object.prototype.hasOwnProperty() :判断某个属性是否为当前对象自身的属性,还是继承 自原型对象的属性。

        Object.prototype.isPrototypeOf() :判断当前对象是否为另一个对象的原型。         Object.prototype.propertyIsEnumerable() :判断某个属性是否可枚举。

Object.prototype.valueOf()

valueOf 方法的作用是返回一个对象的“值”,默认情况下返回对象本身。

1. var obj = new Object();
2. obj.valueOf() === obj // true

上面代码比较 obj.valueOf() 与 obj 本身,两者是一样的。 valueOf 方法的主要用途是,JavaScript 自动类型转换时会默认调用这个方法。

1. var obj = new Object();
2. 1 + obj // "1[object Object]"

上面代码将对象 obj 与数字 1 相加,这时 JavaScript 就会默认调用 valueOf() 方法,求 出 obj 的值再与 1 相加。所以,如果自定义 valueOf 方法,就可以得到想要的结果。

1. var obj = new Object();
2. obj.valueOf = function () {
3.     return 2;
4. };
5.
6. 1 + obj // 3

上面代码自定义了 obj 对象的 valueOf 方法,于是 1 + obj 就得到了 3 。这种方法就相当 于用自定义的 obj.valueOf ,覆盖 Object.prototype.valueOf 。

Object.prototype.toString()

toString 方法的作用是返回一个对象的字符串形式,默认情况下返回类型字符串。

1. var o1 = new Object();
2. o1.toString() // "[object Object]"
3.
4. var o2 = {a:1};
5. o2.toString() // "[object Object]"

上面代码表示,对于一个对象调用 toString 方法,会返回字符串 [object Object] ,该字符串 说明对象的类型。 字符串 [object Object] 本身没有太大的用处,但是通过自定义 toString 方法,可以让对象在 自动类型转换时,得到想要的字符串形式。

1. var obj = new Object();
2.
3. obj.toString = function () {
4.     return 'hello';
5. };
6.
7. obj + ' ' + 'world' // "hello world"

上面代码表示,当对象用于字符串加法时,会自动调用 toString 方法。由于自定义 了 toString 方法,所以返回字符串 hello world 。 数组、字符串、函数、Date 对象都分别部署了自定义的 toString 方法,覆盖 了 Object.prototype.toString 方法。

1. [1, 2, 3].toString() // "1,2,3"
2.
3. '123'.toString() // "123"
4.
5. (function () {
6.     return 123;
7. }).toString()
8. // "function () {
9. // return 123;
10. // }"
11.
12. (new Date()).toString()
13. // "Tue May 10 2016 09:11:31 GMT+0800 (CST)"

上面代码中,数组、字符串、函数、Date 对象调用 toString 方法,并不会返回 [object Object] ,因为它们都自定义了 toString 方法,覆盖原始方法。

toString() 的应用:判断数据类型

Object.prototype.toString 方法返回对象的类型字符串,因此可以用来判断一个值的类型。

1. var obj = {};
2. obj.toString() // "[object Object]"

上面代码调用空对象的 toString 方法,结果返回一个字符串 object Object ,其中第二 个 Object 表示该值的构造函数。这是一个十分有用的判断数据类型的方法。 由于实例对象可能会自定义 toString 方法,覆盖掉 Object.prototype.toString 方法,所以为 了得到类型字符串,最好直接使用 Object.prototype.toString 方法。通过函数的 call 方法, 可以在任意值上调用这个方法,帮助我们判断这个值的类型。

1. Object.prototype.toString.call(value)

上面代码表示对 value 这个值调用 Object.prototype.toString 方法。 不同数据类型的 Object.prototype.toString 方法返回值如下。

        数值:返回 [object Number] 。

        字符串:返回 [object String] 。

        布尔值:返回 [object Boolean] 。

        undefined:返回 [object Undefined] 。

        null:返回 [object Null] 。

        数组:返回 [object Array] 。

        arguments 对象:返回 [object Arguments] 。

        函数:返回 [object Function] 。

        Error 对象:返回 [object Error] 。

        Date 对象:返回 [object Date] 。

        RegExp 对象:返回 [object RegExp] 。

        其他对象:返回 [object Object] 。

Object.prototype.toString 可以看出一个值到底是什么类型。

1. Object.prototype.toString.call(2) // "[object Number]"
2. Object.prototype.toString.call('') // "[object String]"
3. Object.prototype.toString.call(true) // "[object Boolean]"
4. Object.prototype.toString.call(undefined) // "[object Undefined]"
5. Object.prototype.toString.call(null) // "[object Null]"
6. Object.prototype.toString.call(Math) // "[object Math]"
7. Object.prototype.toString.call({}) // "[object Object]"
8. Object.prototype.toString.call([]) // "[object Array]"

利用这个特性,可以写出一个比 typeof 运算符更准确的类型判断函数。

1. var type = function (o){
2.     var s = Object.prototype.toString.call(o);
3.     return s.match(/\[object (.*?)\]/)[1].toLowerCase();
4. };
5.
6. type({}); // "object"
7. type([]); // "array"
8. type(5); // "number"
9. type(null); // "null"
10. type(); // "undefined"
11. type(/abcd/); // "regex"
12. type(new Date()); // "date"

在上面这个 type 函数的基础上,还可以加上专门判断某种类型数据的方法。

1. var type = function (o){
2.     var s = Object.prototype.toString.call(o);
3.     return s.match(/\[object (.*?)\]/)[1].toLowerCase();
4. };
5.
6. ['Null',
7.     'Undefined',
8.     'Object',
9.     'Array',
10.    'String',
11.    'Number',
12.    'Boolean',
13.    'Function',
14.    'RegExp'
15. ].forEach(function (t) {
16.     type['is' + t] = function (o) {
17.         return type(o) === t.toLowerCase();
18.     };
19. });
20.
21. type.isObject({}) // true
22. type.isNumber(NaN) // true
23. type.isRegExp(/abc/) // true

Object.prototype.toLocaleString()

Object.prototype.toLocaleString 方法与 toString 的返回结果相同,也是返回一个值的字符 串形式。

1. var obj = {};
2. obj.toString(obj) // "[object Object]"
3. obj.toLocaleString(obj) // "[object Object]"

这个方法的主要作用是留出一个接口,让各种不同的对象实现自己版本的 toLocaleString ,用来返 回针对某些地域的特定的值。

1. var person = {
2.     toString: function () {
3.         return 'Henry Norman Bethune';
4.     },
5.     toLocaleString: function () {
6.         return '小白';
7.     }
8. };
9.
10. person.toString() // Henry Norman Bethune
11. person.toLocaleString() // 小白

上面代码中, toString() 方法返回对象的一般字符串形式, toLocaleString() 方法返回本地的 字符串形式。 目前,主要有三个对象自定义了 toLocaleString 方法。

        Array.prototype.toLocaleString()

        Number.prototype.toLocaleString()

        Date.prototype.toLocaleString()

例如:日期的实例对象的 toString 和 toLocaleString 返回值就不一样,而 且 toLocaleString 的返回值跟用户设定的所在地域相关。

1. var date = new Date();
2. date.toString() // "Tue Jan 01 2018 12:01:33 GMT+0800 (CST)"
3. date.toLocaleString() // "1/01/2018, 12:01:33 PM"

Object.prototype.hasOwnProperty()

Object.prototype.hasOwnProperty 方法接受一个字符串作为参数,返回一个布尔值,表示该实例 对象自身是否具有该属性。

1. var obj = {
2.     p: 123
3. };
4.
5. obj.hasOwnProperty('p') // true
6. obj.hasOwnProperty('toString') // false

上面代码中,对象 obj 自身具有 p 属性,所以返回true 。toString属性是继承的,所以返回false 。

2,属性描述对象

JavaScript 提供了一个内部数据结构,用来描述对象的属性,控制它的行为,比如该属性是否可 写、可遍历等等。这个内部数据结构称为“属性描述对象”(attributes object)。每个属性都有自 己对应的属性描述对象,保存该属性的一些元信息。

下面是属性描述对象的一个例子。

1. {
2.     value: 123,
3.     writable: false,
4.     enumerable: true,
5.     configurable: false,
6.     get: undefined,
7.     set: undefined
8. }

属性描述对象提供6个元属性。

(1) value :value 是该属性的属性值,默认为 undefined 。

(2) writable :writable 是一个布尔值,表示属性值(value)是否可改变(即是否可写),默认为 true 。

(3) enumerable :enumerable 是一个布尔值,表示该属性是否可遍历,默认为 true 。如果设为 false ,会使得 某些操作(比如 for...in 循环、 Object.keys() )跳过该属性。

(4) configurable :configurable 是一个布尔值,表示可配置性,默认为 true 。如果设为 false ,将阻止某些操 作改写该属性,比如无法删除该属性,也不得改变该属性的属性描述对象( value 属性除外)。也就 是说, configurable 属性控制了属性描述对象的可写性。

(5) get:get 是一个函数,表示该属性的取值函数(getter),默认为 undefined 。

(6) set :set 是一个函数,表示该属性的存值函数(setter),默认为 undefined 。

Object.getOwnPropertyDescriptor()

Object.getOwnPropertyDescriptor() 方法可以获取属性描述对象。它的第一个参数是目标对象, 第二个参数是一个字符串,对应目标对象的某个属性名。

1. var obj = { p: 'a' };
2.
3. Object.getOwnPropertyDescriptor(obj, 'p')
4. // Object { value: "a",
5. // writable: true,
6. // enumerable: true,
7. // configurable: true
8. // }

上面代码中, Object.getOwnPropertyDescriptor() 方法获取 obj.p 的属性描述对象。 注意, Object.getOwnPropertyDescriptor() 方法只能用于对象自身的属性,不能用于继承的属 性。

1. var obj = { p: 'a' };
2.
3. Object.getOwnPropertyDescriptor(obj, 'toString')
4. // undefined

上面代码中, toString 是 obj 对象继承的属性, Object.getOwnPropertyDescriptor() 无法 获取。

Object.getOwnPropertyNames()

Object.getOwnPropertyNames 方法返回一个数组,成员是参数对象自身的全部属性的属性名,不管 该属性是否可遍历。

1. var obj = Object.defineProperties({}, {
2.     p1: { value: 1, enumerable: true },
3.     p2: { value: 2, enumerable: false }
4. });
5.
6. Object.getOwnPropertyNames(obj)
7. // ["p1", "p2"]

上面代码中, obj.p1 是可遍历的, obj.p2 是不可遍历的。 Object.getOwnPropertyNames 会 将它们都返回。 这跟 Object.keys 的行为不同, Object.keys 只返回对象自身的可遍历属性的全部属性名。

1. Object.keys([]) // []
2. Object.getOwnPropertyNames([]) // [ 'length' ]
3.
4. Object.keys(Object.prototype) // []
5. Object.getOwnPropertyNames(Object.prototype)
6. // ['hasOwnProperty',
7. // 'valueOf',
8. // 'constructor',
9. // 'toLocaleString',
10. // 'isPrototypeOf',
11. // 'propertyIsEnumerable',
12. // 'toString']

上面代码中,数组自身的 length 属性是不可遍历的, Object.keys 不会返回该属性。第二个例 子的 Object.prototype 也是一个对象,所有实例对象都会继承它,它自身的属性都是不可遍历的。

Object.defineProperty(), Object.defineProperties()

Object.defineProperty() 方法允许通过属性描述对象,定义或修改一个属性,然后返回修改后的 对象,它的用法如下。

1. Object.defineProperty(object, propertyName, attributesObject)

Object.defineProperty 方法接受三个参数,依次如下。

        object:属性所在的对象

        propertyName:字符串,表示属性名

        attributesObject:属性描述对象

举例来说,定义 obj.p 可以写成下面这样。

1. var obj = Object.defineProperty({}, 'p', {
2.     value: 123,
3.     writable: false,
4.     enumerable: true,
5.     configurable: false
6. });
7.
8. obj.p // 123
9.
10. obj.p = 246;
11. obj.p // 123

上面代码中, Object.defineProperty() 方法定义了 obj.p 属性。由于属性描述对象 的 writable 属性为 false ,所以 obj.p 属性不可写。注意,这里 的 Object.defineProperty 方法的第一个参数是 {} (一个新建的空对象), p 属性直接定义 在这个空对象上面,然后返回这个对象,这是 Object.defineProperty() 的常见用法。 如果属性已经存在, Object.defineProperty() 方法相当于更新该属性的属性描述对象。 如果一次性定义或修改多个属性,可以使用 Object.defineProperties() 方法。

1. var obj = Object.defineProperties({}, {
2.     p1: { value: 123, enumerable: true },
3.     p2: { value: 'abc', enumerable: true },
4.     p3: { get: function () { return this.p1 + this.p2 },
5.         enumerable:true,
6.         configurable:true
7.     }
8. });
9.
10. obj.p1 // 123
11. obj.p2 // "abc"
12. obj.p3 // "123abc"

上面代码中, Object.defineProperties() 同时定义了 obj 对象的三个属性。其中, p3 属性 定义了取值函数 get ,即每次读取该属性,都会调用这个取值函数。 注意,一旦定义了取值函数 get (或存值函数 set ),就不能将 writable 属性设 为 true ,或者同时定义 value 属性,否则会报错。

1. var obj = {};
2.
3. Object.defineProperty(obj, 'p', {
4.     value: 123,
5.     get: function() { return 456; }
6. });
7. // TypeError: Invalid property.
8. // A property cannot both have accessors and be writable or have a value
9.
10. Object.defineProperty(obj, 'p', {
11.     writable: true,
12.     get: function() { return 456; }
13. });
14. // TypeError: Invalid property descriptor.
15. // Cannot both specify accessors and a value or writable attribute

上面代码中,同时定义了 get 属性和 value 属性,以及将 writable 属性设为 true ,就会 报错。 Object.defineProperty() 和 Object.defineProperties() 参数里面的属性描述对象, writable 、 configurable 、 enumerable 这三个属性的默认值都为 false 。

1. var obj = {};
2.     Object.defineProperty(obj, 'foo', {});
3.     Object.getOwnPropertyDescriptor(obj, 'foo')
4. // {
5. // value: undefined,
6. // writable: false,
7. // enumerable: false,
8. // configurable: false
9. // }

上面代码中,定义 obj.foo 时用了一个空的属性描述对象,就可以看到各个元属性的默认值。

Object.prototype.propertyIsEnumerable()

实例对象的 propertyIsEnumerable() 方法返回一个布尔值,用来判断某个属性是否可遍历。注意, 这个方法只能用于判断对象自身的属性,对于继承的属性一律返回 false 。

1. var obj = {};
2. obj.p = 123;
3.
4. obj.propertyIsEnumerable('p') // true
5. obj.propertyIsEnumerable('toString') // false

上面代码中, obj.p 是可遍历的,而 obj.toString 是继承的属性。

元属性

属性描述对象的各个属性称为“元属性”,因为它们可以看作是控制属性的属性。

value:性是目标属性的值。

1. var obj = {};
2. obj.p = 123;
3.
4. Object.getOwnPropertyDescriptor(obj, 'p').value
5. // 123
6.
7. Object.defineProperty(obj, 'p', { value: 246 });
8. obj.p // 246

上面代码是通过 value 属性,读取或改写 obj.p 的例子。

writable:是一个布尔值,决定了目标属性的值(value)是否可以被改变。

1. var obj = {};
2.
3. Object.defineProperty(obj, 'a', {
4.     value: 37,
5.     writable: false
6. });
7.
8. obj.a // 37
9. obj.a = 25;
10. obj.a // 37

上面代码中, obj.a 的 writable 属性是 false 。然后,改变 obj.a 的值,不会有任何效 果。注意,正常模式下,对 writable 为 false 的属性赋值不会报错,只会默默失败。但是,严格模式 下会报错,即使对 a 属性重新赋予一个同样的值。

1. 'use strict';
2. var obj = {};
3.
4. Object.defineProperty(obj, 'a', {
5.     value: 37,
6.     writable: false
7. });
8.
9. obj.a = 37;
10. // Uncaught TypeError: Cannot assign to read only property 'a' of object

上面代码是严格模式,对 obj.a 任何赋值行为都会报错。 如果原型对象的某个属性的 writable 为 false ,那么子对象将无法自定义这个属性。

1. var proto = Object.defineProperty({}, 'foo', {
2.     value: 'a',
3.     writable: false
4. });
5.
6. var obj = Object.create(proto);
7.
8. obj.foo = 'b';
9. obj.foo // 'a'

上面代码中, proto 是原型对象,它的 foo 属性不可写。 obj 对象继承 proto ,也不可以再 自定义这个属性了。如果是严格模式,这样做还会抛出一个错误。 但是,有一个规避方法,就是通过覆盖属性描述对象,绕过这个限制。原因是这种情况下,原型链会被 完全忽视。

1. var proto = Object.defineProperty({}, 'foo', {
2.     value: 'a',
3.     writable: false
4. });
5.
6. var obj = Object.create(proto);
7. Object.defineProperty(obj, 'foo', {
8.     value: 'b'
9. });
10.
11. obj.foo // "b"

enumerable:(可遍历性)返回一个布尔值,表示目标属性是否可遍历。

JavaScript 的早期版本, for...in 循环是基于 in 运算符的。我们知道, in 运算符不管某 个属性是对象自身的还是继承的,都会返回 true 。

1. var obj = {};
2. 'toString' in obj // true

上面代码中, toString 不是 obj 对象自身的属性,但是 in 运算符也返回 true ,这导致 了 toString 属性也会被 for...in 循环遍历。 这显然不太合理,后来就引入了“可遍历性”这个概念。只有可遍历的属性,才会被 for...in 循环遍 历,同时还规定 toString 这一类实例对象继承的原生属性,都是不可遍历的,这样就保证 了 for...in 循环的可用性。 具体来说,如果一个属性的 enumerable 为 false ,下面三个操作不会取到该属性。

        for..in 循环

        Object.keys 方法

        JSON.stringify 方法

因此, enumerable 可以用来设置“秘密”属性。

1. var obj = {};
2.
3. Object.defineProperty(obj, 'x', {
4.     value: 123,
5.     enumerable: false
6. });
7.
8. obj.x // 123
9.
10. for (var key in obj) {
11.     console.log(key);
12. }
13. // undefined
14.
15. Object.keys(obj) // []
16. JSON.stringify(obj) // "{}"

上面代码中, obj.x 属性的 enumerable 为 false ,所以一般的遍历操作都无法获取该属性, 使得它有点像“秘密”属性,但不是真正的私有属性,还是可以直接获取它的值。 注意, for...in 循环包括继承的属性, Object.keys 方法不包括继承的属性。如果需要获取对 象自身的所有属性,不管是否可遍历,可以使用 Object.getOwnPropertyNames 方法。 另外, JSON.stringify 方法会排除 enumerable 为 false 的属性,有时可以利用这一点。如 果对象的 JSON 格式输出要排除某些属性,就可以把这些属性的 enumerable 设为 false 。

configurable:configurable (可配置性)返回一个布尔值,决定了是否可以修改属性描述对象。 configurable 为 false 时, value 、 writable 、 enumerable 和 configurable 都不能被修改了。

1. var obj = Object.defineProperty({}, 'p', {
2.     value: 1,
3.     writable: false,
4.     enumerable: false,
5.     configurable: false
6. });
7.
8. Object.defineProperty(obj, 'p', {value: 2})
9. // TypeError: Cannot redefine property: p
10.
11. Object.defineProperty(obj, 'p', {writable: true})
12. // TypeError: Cannot redefine property: p
13.
14. Object.defineProperty(obj, 'p', {enumerable: true})
15. // TypeError: Cannot redefine property: p
16.
17. Object.defineProperty(obj, 'p', {configurable: true})
18. // TypeError: Cannot redefine property: p

上面代码中, obj.p 的 configurable 为 false 。然后,改 动 value 、 writable 、 enumerable 、 configurable ,结果都报错。 注意, writable 只有在 false 改为 true 会报错, true 改为 false 是允许的。

1. var obj = Object.defineProperty({}, 'p', {
2.     writable: true,
3.     configurable: false
4. });
5.
6. Object.defineProperty(obj, 'p', {writable: false})
7. // 修改成功

至于 value ,只要 writable 和 configurable 有一个为 true ,就允许改动。

1. var o1 = Object.defineProperty({}, 'p', {
2.     value: 1,
3.     writable: true,
4.     configurable: false
5. });
6.
7. Object.defineProperty(o1, 'p', {value: 2})
8. // 修改成功
9.
10. var o2 = Object.defineProperty({}, 'p', {
11.     value: 1,
12.     writable: false,
13.     configurable: true
14. });
15.
16. Object.defineProperty(o2, 'p', {value: 2})
17. // 修改成功

另外, writable 为 false 时,直接目标属性赋值,不报错,但不会成功。

1. var obj = Object.defineProperty({}, 'p', {
2.     value: 1,
3.     writable: false,
4.     configurable: false
5. });
6.
7. obj.p = 2;
8. obj.p // 1

上面代码中, obj.p 的 writable 为 false ,对 obj.p 直接赋值不会生效。如果是严格模 式,还会报错。

可配置性决定了目标属性是否可以被删除(delete)。

1. var obj = Object.defineProperties({}, {
2.     p1: { value: 1, configurable: true },
3.     p2: { value: 2, configurable: false }
4. });
5.
6. delete obj.p1 // true
7. delete obj.p2 // false
8.
9. obj.p1 // undefined
10. obj.p2 // 2

上面代码中, obj.p1 的 configurable 是 true ,所以可以被删除, obj.p2 就无法删除。

存取器:除了直接定义以外,属性还可以用存取器(accessor)定义。其中,存值函数称为 setter ,使用 属性描述对象的 set 属性;取值函数称为 getter ,使用属性描述对象的 get 属性。

一旦对目标属性定义了存取器,那么存取的时候,都将执行对应的函数。利用这个功能,可以实现许多 高级特性,比如定制属性的读取和赋值行为。

1. var obj = Object.defineProperty({}, 'p', {
2.     get: function () {
3.         return 'getter';
4.     },
5.     set: function (value) {
6.         console.log('setter: ' + value);
7.     }
8. });
9.
10. obj.p // "getter"
11. obj.p = 123 // "setter: 123"

上面代码中, obj.p 定义了 get 和 set 属性。 obj.p 取值时,就会调用 get ;赋值时, 就会调用 set 。 JavaScript 还提供了存取器的另一种写法。

1. // 写法二
2. var obj = {
3.     get p() {
4.         return 'getter';
5.     },
6.     set p(value) {
7.         console.log('setter: ' + value);
8.     }
9. };

上面两种写法,虽然属性 p 的读取和赋值行为是一样的,但是有一些细微的区别。第一种写法,属 性 p 的 configurable 和 enumerable 都为 false ,从而导致属性 p 是不可遍历的;第二 种写法,属性 p 的 configurable 和 enumerable 都为 true ,因此属性 p 是可遍历的。 实际开发中,写法二更常用。 注意,取值函数 get 不能接受参数,存值函数 set 只能接受一个参数(即属性的值)。 存取器往往用于,属性的值依赖对象内部数据的场合。

1. var obj ={
2.     $n : 5,
3.     get next() { return this.$n++ },
4.     set next(n) {
5.         if (n >= this.$n) this.$n = n;
6.         else throw new Error('新的值必须大于当前值');
7.     }
8. };
9.
10. obj.next // 5
11.
12. obj.next = 10;
13. obj.next // 10
14.
15. obj.next = 5;
16. // Uncaught Error: 新的值必须大于当前值

上面代码中, next 属性的存值函数和取值函数,都依赖于内部属性 $n 。

对象的拷贝:将一个对象的所有属性,拷贝到另一个对象,可以用下面的方法实现。

1. var extend = function (to, from) {
2.     for (var property in from)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值