红宝书4-第八章对象、类与面向对象编程(4)


原型层级

在通过对象访问属性时,会按照这个属性的名称开始搜索。现在这个对象上找,没有再去找原型,返回他的值。
在这里插入图片描述
虽然可以通过实例读取原型对象上的值,但不可能通过实例重写这些值。如果在实例上添加了一个 与原型对象中同名的属性,那就会在实例上创建这个属性,这个属性会遮住原型对象上的属性。

只要给对象实例添加一个属性,这个属性就会遮蔽(shadow)原型对象上的同名属性,也就是虽然 不会修改它,但会屏蔽对它的访问。即使在实例上把这个属性设置为 null,也不会恢复它和原型的联 系。不过,使用 delete 操作符可以完全删除实例上的这个属性,从而让标识符解析过程能够继续搜索 原型对象。

function Person() {} 
 
Person.prototype.name = "Nicholas"; 
Person.prototype.age = 29; 
Person.prototype.job = "Software Engineer"; 
Person.prototype.sayName = function() {   console.log(this.name);  }; 
 
let person1 = new Person(); let person2 = new Person(); 
 
person1.name = "Greg"; console.log(person1.name);  // "Greg",来自实例 console.log(person2.name);  // "Nicholas",来自原型 
 
delete person1.name; console.log(person1.name);  // "Nicholas",来自原型 

hasOwnProperty()方法用于确定某个属性是在实例上还是在原型对象上

这个方法是继承自 Object 的,会在属性存在于调用它的对象实例上时返回 true

function Person() {} 
Person.prototype.name = "Nicholas"; 
Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function() {   console.log(this.name);  }; 
 
let person1 = new Person(); let person2 = new Person(); console.log(person1.hasOwnProperty("name")); // false 
 
person1.name = "Greg"; console.log(person1.name); // "Greg",来自实例 console.log(person1.hasOwnProperty("name")); // true 
 
console.log(person2.name); // "Nicholas",来自原型 console.log(person2.hasOwnProperty("name")); // false 
 
delete person1.name; console.log(person1.name); // "Nicholas",来自原型 console.log(person1.hasOwnProperty("name")); // false

在这个例子中,通过调用 hasOwnProperty()能够清楚地看到访问的是实例属性还是原型属性。 调用 person1.hasOwnProperty("name")只在重写 person1 上 name 属性的情况下才返回 true,表 明此时 name 是一个实例属性,不是原型属性。
在这里插入图片描述

原型和 in 操作符

in 操作符

无论该属性是在实例上还是在原型上,返回 true。

如果要确定某个属性是否存在于原型上,则可以像下 面这样同时使用 hasOwnProperty()和 in 操作符:

function hasPrototypeProperty(object, name)
{    
return !object.hasOwnProperty(name) && (name in object); 
}

只要通过对象可以访问,in 操作符就返回 true,而 hasOwnProperty()只有属性存在于实例上 时才返回 true。因此,只要 in 操作符返回 true 且 hasOwnProperty()返回 false,就说明该属性 是一个原型属性。

即便此时 原型对象还有 name 属性,但因为实例上的属性遮蔽了它,所以不会用到。

for-in

在 for-in 循环中使用 in 操作符时,可以通过对象访问且可以被枚举的属性都会返回,包括实例 属性和原型属性。遮蔽原型中不可枚举([[Enumerable]]特性被设置为 false)属性的实例属性也会 在 for-in 循环中返回,因为默认情况下开发者定义的属性都是可枚举的。

Object.keys()

这个方法接收一个对象作 为参数,返回包含该对象所有可枚举实例属性名称的字符串数组。
这里,keys 变量保存的数组中包含"name"、"age"、"job"和"sayName"。这是正常情况下通过 for-in 返回的顺序。而在 Person 的实例上调用时,Object.keys()返回的数组中只包含"name"和 "age"两个属性。

Object.getOwnPropertyNames()

列出所有实例属性,无论是否可以枚举。

let keys = Object.getOwnPropertyNames(Person.prototype); 
console.log(keys);   // "[constructor,name,age,job,sayName]" 

注意,返回的结果中包含了一个不可枚举的属性 constructor。Object.keys()和 Object. getOwnPropertyNames()在适当的时候都可用来代替 for-in 循环。

Object.getOwnProperty- Symbols()

在 ECMAScript 6新增符号类型之后,相应地出现了增加一个 Object.getOwnPropertyNames() 的兄弟方法的需求,因为以符号为键的属性没有名称的概念。因此,Object.getOwnProperty- Symbols()方法就出现了,这个方法与 Object.getOwnPropertyNames()类似,只是针对符号而已:

let k1 = Symbol('k1'),     
k2 = Symbol('k2');
let o = {   [k1]: 'k1',   [k2]: 'k2' }; 
 
console.log(Object.getOwnPropertySymbols(o)); // [Symbol(k1), Symbol(k2)]

属性枚举顺序

for-in 循环、Object.keys()Object.getOwnPropertyNames()Object.getOwnProperty- Symbols()以及 Object.assign()在属性枚举顺序方面有很大区别。for-in 循环和 Object.keys() 的枚举顺序是不确定的,取决于 JavaScript引擎,可能因浏览器而异。
Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()和 Object.assign() 的枚举顺序是确定性的。先以升序枚举数值键,然后以插入顺序枚举字符串和符号键。在对象字面量中 定义的键以它们逗号分隔的顺序插入。

义的键以它们逗号分隔的顺序插入。 let k1 = Symbol('k1'),     k2 = Symbol('k2'); 
 
let o = {   1: 1,   first: 'first',   [k1]: 'sym2',   second: 'second',   0: 0 }; 
 
o[k2] = 'sym2'; o[3] = 3; o.third = 'third'; o[2] = 2; 
 
console.log(Object.getOwnPropertyNames(o)); // ["0", "1", "2", "3", "first", "second", "third"] 
 
console.log(Object.getOwnPropertySymbols(o)); // [Symbol(k1), Symbol(k2)]
posted @ 2020-10-12 17:28  jacksonni  阅读(128)  评论(0编辑  收藏  举报