本文主要是介绍《每天十分钟》-红宝书第4版-对象、类与面向对象编程(四),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
原型层级
通过对象访问属性时,会按照这个属性的名称开始搜索。搜索开始于对象实例本身。如果在这个实例上发现了给定的名称,则返回该名称对应的值。如果没有找到这个属性,则搜索会沿着指针进入原型对象,然后在原型对象上找到属性后,再返回对应的值。因此,在调用 person1.sayName()时,会发生两步搜索。首先,JavaScript 引擎会问:“person1 实例有 sayName 属性吗?”答案是没有。然后,继续搜索并问:“person1 的原型有 sayName 属性吗?”答案是有。于是就返回了保存在原型上的这个函数。在调用 person2.sayName()时,会发生同样的搜索过程,而且也会返回相同的结果。这就是原型用于在多个对象实例间共享属性和方法的原理。
注意啦:
虽然可以通过实例读取原型对象上的值,但不可能通过实例重写这些值。如果在实例上添加了一个
与原型对象中同名的属性,那就会在实例上创建这个属性,这个属性会遮住原型对象上的属性。下面看
一个例子:
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",来自原型
console.log()访问 person1.name 时,会先在实例上搜索个属性。因为这个属性在实例上存在,所以就不会再搜索原型对象了
注意重点
只要给对象实例添加一个属性,这个属性就会遮蔽(shadow)原型对象上的同名属性,也就是虽然
不会修改它,但会屏蔽对它的访问。即使在实例上把这个属性设置为 null,也不会恢复它和原型的联
系。不过,使用 delete 操作符可以完全删除实例上的这个属性,从而让标识符解析过程能够继续搜索
原型对象。
比如上面的name
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
原型和 in 操作符
有两种方式使用 in 操作符:单独使用和在 for-in 循环中使用。在单独使用时,in 操作符会在可以通过对象访问指定属性时返回 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
console.log("name" in person1); // true
person1.name = "Greg";
console.log(person1.name); // "Greg",来自实例
console.log(person1.hasOwnProperty("name")); // true
console.log("name" in person1); // true
console.log(person2.name); // "Nicholas",来自原型
console.log(person2.hasOwnProperty("name")); // false
console.log("name" in person2); // true
delete person1.name;
console.log(person1.name); // "Nicholas",来自原型
console.log(person1.hasOwnProperty("name")); // false
console.log("name" in person1); // true
在上面整个例子中,name 随时可以通过实例或通过原型访问到。因此,调用"name" in persoon1时始终返回 true,无论这个属性是否在实例上。如果要确定某个属性是否存在于原型上,则可以像下面这样同时使用hasOwnProperty()和 in 操作符:
function hasPrototypeProperty(object, name){ return !object.hasOwnProperty(name) && (name in object);
}
function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() { console.log(this.name);
};
let person = new Person();
console.log(hasPrototypeProperty(person, "name")); // true
person.name = "Greg";
console.log(hasPrototypeProperty(person, "name")); // false
要获得对象上所有可枚举的实例属性,可以使用 Object.keys()方法。这个方法接收一个对象作为参数,返回包含该对象所有可枚举属性名称的字符串数组。比如:
function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function() { console.log(this.name);
};
let keys = Object.keys(Person.prototype);
console.log(keys); // "name,age,job,sayName"
let p1 = new Person();
p1.name = "Rob";
p1.age = 31;
let p1keys = Object.keys(p1);
console.log(p1keys); // "[name,age]"
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 循环。
在 ECMAScript 6 新增符号类型之后,相应地出现了增加一个 Object.getOwnPropertyNames()的兄弟方法的需求,因为以符号为键的属性没有名称的概念。因此,Object.getOwnPropertySymbols()方法就出现了,这个方法与 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.getOwnPropertySymbols()和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)]
随着过了过年又长了一岁,马上小学四年级了哈哈,今天欣赏一首感叹时光的
浣溪沙·游蕲水清泉寺
宋 苏轼
游蕲水清泉寺,寺临兰溪,溪水西流。
山下兰芽短浸溪,松间沙路净无泥,潇潇暮雨子规啼。
谁道人生无再少?门前流水尚能西!休将白发唱黄鸡。
这篇关于《每天十分钟》-红宝书第4版-对象、类与面向对象编程(四)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!