javascript 里面的object

Object的定义

首先看一下ECMA-262文档上关于Object的定义

6.1.7 The Object Type An Object is logically a collection of properties. Each property is either a data property, or an accessor property: A data property associates a key value with an ECMAScript language value and a set of Boolean attributes. An accessor property associates a key value with one or two accessor functions, and a set of Boolean attributes. The accessor functions are used to store or retrieve an ECMAScript language value that is associated with the property

An accessor property associates a key value with one or two accessor
functions, and a set of Boolean attributes. The accessor functions are
used to store or retrieve an ECMAScript language value that is
associated with the property

ECMA-262 将对象定义为一组属性的无序集合。严格来说,这意味着对象就是一组没有特定顺序的值。对象的每个属性或方法都由一个名称来标识,这个名称映射到一个值。正因为如此(以及其他还未讨论的原因),可以把 ECMAScript 的对象想象成一张散列表,其中的内容就是一组名/值对,值可以是数据或者函数。

创建对象的方法

在JavaScript中,几乎所有的对象都是Object类型的实例,它们都会从Object.prototype继承属性和方法。Object 构造函数为给定值创建一个对象包装器。Object构造函数,会根据给定的参数创建对象,具体有以下情况:

  • 如果给定值是 null 或 undefined,将会创建并返回一个空对象 如果传进去的是一个基本类型的值,则会构造其包装类型的对象
  • 如果传进去的是引用类型的值,仍然会返回这个值,经他们复制的变量保有和源对象相同的引用地址 当以非构造函数形式被调用时,Object 的行为等同于 new Object()。

创建对象的2种方式:

  • 对象初始化器(Object initialiser)或对象字面量(literal)
    { [ nameValuePair1[, nameValuePair2[, …nameValuePairN] ] ] }
  • 以构造函数形式来调用
    new Object([value])

下面2种创建Object的方式是等效的

let person = new Object();
person.name = "Nicholas";
person.age = 29;
person.job = "Software Engineer";
person.sayName = function() {
	console.log(this.name);
};

let person = {
	name: "Nicholas",
	age: 29,
	job: "Software Engineer",
	sayName() {
		console.log(this.name);
	}
};
构造函数及原型链

ECMAScript 中的构造函数是用于创建特定类型对象的。像 Object 和 Array 这样的原生构造函数,运行时可以直接在执行环境中使用。当然也可以自定义构造函数,以函数的形式为自己的对象类型定义属性和方法

要创建 Person 的实例,应使用 new 操作符。以这种方式调用构造函数会执行如下操作。

  1. 在内存中创建一个新对象。
  2. 这个新对象内部的[[Prototype]]特性被赋值为构造函数的 prototype 属性。
  3. 构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)。
  4. 执行构造函数内部的代码(给新对象添加属性)。
  5. 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。

每个函数都会创建一个 prototype 属性,这个属性是一个对象,包含应该由特定引用类型的实例共享的属性和方法。实际上,这个对象就是通过调用构造函数创建的对象的原型。使用原型对象的好处是,在它上面定义的属性和方法可以被对象实例共享。

无论何时,只要创建一个函数,就会按照特定的规则为这个函数创建一个 prototype 属性(指向原型对象)。默认情况下,所有原型对象自动获得一个名为 constructor 的属性,指回与之关联的构造函数。对前面的例子而言, Person.prototype.constructor 指向 Person。然后,因构造函数而异,可能会给原型对象添加其他属性和方法。

在自定义构造函数时,原型对象默认只会获得 constructor 属性,其他的所有方法都继承自Object。每次调用构造函数创建一个新实例,这个实例的内部[[Prototype]]指针就会被赋值为构造函数的原型对象

在通过对象访问属性时,会按照这个属性的名称开始搜索。搜索开始于对象实例本身。如果在这个实例上发现了给定的名称,则返回该名称对应的值。如果没有找到这个属性,则搜索会沿着指针进入原型对象,然后在原型对象上找到属性后,再返回对应的值

属性

属性分两种:数据属性和访问器属性。

为了将某个特性标识为内部特性,规范会用两个中括号把特性的名称括起来

(来源于ECMA文档的6.1.7.2 节 Internal methods and internal slots are identified within this specification using names enclosed in double square brackets [[ ]].)

数据属性(data property)

数据属性包含一个保存数据值的位置。值会从这个位置读取,也会写入到这个位置。数据属性有 4
个特性描述它们的行为

  • [[Configurable]]:表示属性是否可以通过 delete删除并重新定义,是否可以修改它的特性,以及是否可以把它改为访问器属性。默认情况下,所有直接定义在对象上的属性的这个特性都是
    true,如前面的例子所示。
  • [[Enumerable]]:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是
    true,如前面的例子所示。
  • [[Writable]]:表示属性的值是否可以被修改。默认情况下,所有直接定义在对象上的属性的这个特性都是 true,如前面的例子所示。
  • [[Value]]:包含属性实际的值。这就是前面提到的那个读取和写入属性值的位置。这个特性的默认值为 undefined

要修改属性的默认特性,就必须使用 Object.defineProperty()方法。这个方法接收 3 个参数:
要给其添加属性的对象、属性的名称和一个描述符对象。最后一个参数,即描述符对象上的属性可以包含: configurable、 enumerable、 writable 和 value,跟相关特性的名称一一对应。根据要修改
的特性,可以设置其中一个或多个值。比如:

let person = {};
person.age = 27;
Object.defineProperty(person, "name", {
	enumerable:false,
	configurable:true,
	writable: false,
 	value: "chadm"
});
console.log(person.name); // 输出 "chadm"
person.name = "Hello";  // 在vscode里面运行在Nodejs会报错
console.log(person.name); // 输出 "chadm"

// 遍历属性的时候,只打印了age,没有打印name
for(let value in person) {
    console.log(typeof(person[value]) + " ; " +value +" ;" + person[value])
}

// 删除属性后,打印值为undefined
delete person.name
console.log(person.name); // undefined
访问器属性(accessor property)

访问器属性不包含数据值。相反,它们包含一个获取( getter)函数和一个设置( setter)函数,不过这两个函数不是必需的。在读取访问器属性时,会调用获取函数,这个函数的责任就是返回一个有效值。在写入访问器属性时,会调用设置函数并传入新值,这个函数必须决定对数据做出什么修改。

访问器属性有 4 个特性描述它们的行为。

  • [[Configurable]]:表示属性是否可以通过 delete删除并重新定义,是否可以修改它的特性,以及是否可以把它改为数据属性。默认情况下,所有直接定义在对象上的属性的这个特性都是 true。
  • [[Enumerable]]:表示属性是否可以通过 for-in 循环返回。默认情况下,所有直接定义在对象上的属性的这个特性都是
  • true。 [[Get]]:获取函数,在读取属性时调用。默认值为 undefined。
  • [[Set]]:设置函数,在写入属性时调用。默认值为 undefined。

访问器属性是不能直接定义的,必须使用 Object.defineProperty()

let person2 = {
    year_:2021,
    age:27
};
Object.defineProperty(person2, "name", {
    enumerable:false,
    configurable:true,
    writable: false,
    value: "Nicholas"
});
console.log(person2.name); // "Nicholas"
// person2.name = "Greg";
console.log(person2.name); // "Nicholas
for(let value in person2) {
    console.log(typeof(person2[value]) + " ; " +value +" ;" + person2[value])
}
delete person2.name
console.log(person2.name); // undefined

Object.defineProperty(person2,"year",{
    get() {
        return this.year_;
    },
    set(newValue) {
        if(newValue > 2021) {
            this.year_ = newValue;
            this.age = newValue - 2021
        }
    },
    enumerable:false
})
// 打印的时候没有输出year相关的
for(let value in person2) {
    console.log(typeof(person2[value]) + " ; " +value +" ;" + person2[value])
}

person2.year = 2022;
console.log(person2.age); //输出1

ECMAScript 提供了 Object.defineProperties()方法。这个方法可以通过多个描述符一次性定义多个属性。它接收两个参数:要为之添加或修改属性的对象和另一个描述符对象,其属性与要添加或修改的属性一一对应

使用 Object.getOwnPropertyDescriptor()方法可以取得指定属性的属性描述符。这个方法接
收两个参数:属性所在的对象和要取得其描述符的属性名。返回值是一个对象,对于访问器属性包含
configurable、 enumerable、 get 和 set 属性,对于数据属性包含 configurable、 enumerable、
writable 和 value 属性

let person3 = {};
Object.defineProperties(person3,{
    name:{
        enumerable:false,
        configurable:true,
        writable: false,
        value: "chadm"
    },
    year: {
        get() {
            return this.year_;
        },
        set(newValue) {
            if(newValue > 2021) {
                this.year_ = newValue;
                this.age = newValue - 2021
            }
        },
        enumerable:false
    }

})

let descriptor = Object.getOwnPropertyDescriptor(person3, "name");
console.log(descriptor.configurable)  // true
console.log(descriptor.enumerable)  // false
console.log(descriptor.value)   // chadm
console.log(descriptor.writable)  // false

let descriptor2 = Object.getOwnPropertyDescriptor(person3, "year");
console.log(descriptor2.configurable)  // false
console.log(descriptor2.enumerable) // false
console.log(typeof descriptor2.get) // function
console.log(typeof descriptor2.set) // function
对象的其它操作
合并对象

ECMAScript 6 专门为合并对象提供了 Object.assign()方法。这个方法接收一个目标对象和一个或多个源对象作为参数,然后将每个源对象中可枚举( Object.propertyIsEnumerable()返回 true)和自有( Object.hasOwnProperty()返回 true)属性复制到目标对象。以字符串和符号为键的属性会被复制。对每个符合条件的属性,这个方法会使用源对象上的[[Get]]取得属性的值,然后使用目标对象上的[[Set]]设置属性的值

Object.assign()实际上对每个源对象执行的是浅复制。如果多个源对象都有相同的属性,则使用最后一个复制的值。 此外,从源对象访问器属性取得的值,比如获取函数,会作为一个静态值赋给目标对象。换句话说,不能在两个对象间转移获取函数和设置函数

let dest, src, result2;
dest = {
    age:31,
    id:"Hello"
};
src = { id: 'chadm' };
result2 = Object.assign(dest, src);
// Object.assign 修改目标对象
// 也会返回修改后的目标对象
console.log("Object.assign demo")
console.log(dest === result2); 
console.log(result2); // {age: 31, id: 'chadm'}
对象标识及相等判定

在 ECMAScript 6 之前,有些特殊情况即使是===操作符也无能为力

// 这些是===符合预期的情况
console.log(true === 1); // false
console.log({} === {}); // false
console.log("2" === 2); // false
// 这些情况在不同 JavaScript 引擎中表现不同,但仍被认为相等
console.log(+0 === -0); // true
console.log(+0 === 0); // true
console.log(-0 === 0); // true
// 要确定 NaN 的相等性,必须使用极为讨厌的 isNaN()
console.log(NaN === NaN); // false
console.log(isNaN(NaN)); // true

为改善这类情况, ECMAScript 6 规范新增了 Object.is(),这个方法与===很像,但同时也考虑到了上述边界情形。这个方法必须接收两个参数

console.log(Object.is(true, 1)); // false
console.log(Object.is({}, {})); // false
console.log(Object.is("2", 2)); // false
// 正确的 0、 -0、 +0 相等/不等判定
console.log(Object.is(+0, -0)); // false
console.log(Object.is(+0, 0)); // true
console.log(Object.is(-0, 0)); // false
// 正确的 NaN 相等判定
console.log(Object.is(NaN, NaN)); // true

ECMAScript 6 开始正式支持类和继承

参考文档:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object
ECMA-262.pdf

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值