读书可以改变一个人的气质和格局。有知识藏于心,便是对灵魂的顶级化妆。
今日学习内容
1、JS 红皮书 P228-232 第八章:对象、类与面向对象编程
今日笔记
1、原型层级: 在通过对象访问属性时,会按照这个属性的名称开始搜索。搜索开始于对象实例本身。如果在这个实例上发现了给定的名称,则返回该名称对应的值。如果没有找到这个属性,则搜索会沿着指针进入原型对象,然后在原型对象上找到属性后,再返回对应的值。因此,在调用 person1.sayName()时,会发生两步搜索。首先,JavaScript 引擎会问:“person1 实例有 sayName 属性吗?”答案是没有。然后,继续搜索并问:“person1 的原型有 sayName 属性吗?”答案是有。于是就返回了保存在原型上的这个函数。在调用 person2.sayName()时,会发生同样的搜索过程,而且也会返回相同的结果。这就是原型用于在多个对象实例间共享属性和方法的原理。
虽然可以通过实例读取原型对象上的值,但不可能通过实例重写这些值。如果在实例上添加了一个与原型对象中同名的属性,那就会在实例上创建这个属性,这个属性会遮住原型对象上的属性。下面看一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 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); console.log(person2.name);
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); console.log(person2.name); delete person1.name; console.log(person1.name);
|
hasOwnProperty()方法用于确定某个属性是在实例上还是在原型对象上。这个方法是继承自 Object的,会在属性存在于调用它的对象实例上时返回 true,如下面的例子所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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")); person1.name = "Greg"; console.log(person1.name); console.log(person1.hasOwnProperty("name")); console.log(person2.name); console.log(person2.hasOwnProperty("name")); delete person1.name; console.log(person1.name); console.log(person1.hasOwnProperty("name"));
|
注意 ECMAScript 的 Object.getOwnPropertyDescriptor()方法只对实例属性有效。要取得原型属性的描述符,就必须直接在原型对象上调用 Object.getOwnPropertyDescriptor()。
2、原型和in操作符: 有两种方式使用 in 操作符:单独使用和在 for-in 循环中使用。在单独使用时,in 操作符会在可以通过对象访问指定属性时返回 true,无论该属性是在实例上还是在原型上。来看下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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")); console.log("name" in person1); person1.name = "Greg"; console.log(person1.name); console.log(person1.hasOwnProperty("name")); console.log("name" in person1); console.log(person2.name); console.log(person2.hasOwnProperty("name")); console.log("name" in person2); delete person1.name; console.log(person1.name); console.log(person1.hasOwnProperty("name")); console.log("name" in person1);
|
在上面整个例子中,name 随时可以通过实例或通过原型访问到。因此,调用”name” in persoon1时始终返回 true,无论这个属性是否在实例上。如果要确定某个属性是否存在于原型上,则可以像下面这样同时使用 hasOwnProperty()和 in 操作符:
1 2 3
| function hasPrototypeProperty(object, name){ return !object.hasOwnProperty(name) && (name in object); }
|
只要通过对象可以访问,in 操作符就返回 true,而 hasOwnProperty()只有属性存在于实例上时才返回 true。因此,只要 in 操作符返回 true 且 hasOwnProperty()返回 false,就说明该属性是一个原型属性。来看下面的例子:
1 2 3 4 5 6 7 8 9 10 11
| 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")); person.name = "Greg"; console.log(hasPrototypeProperty(person, "name"));
|
在 for-in 循环中使用 in 操作符时,可以通过对象访问且可以被枚举的属性都会返回,包括实例属性和原型属性。遮蔽原型中不可枚举([[Enumerable]]特性被设置为 false)属性的实例属性也会在 for-in 循环中返回,因为默认情况下开发者定义的属性都是可枚举的。
要获得对象上所有可枚举的实例属性,可以使用 Object.keys()方法。这个方法接收一个对象作为参数,返回包含该对象所有可枚举属性名称的字符串数组。比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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); let p1 = new Person(); p1.name = "Rob"; p1.age = 31; let p1keys = Object.keys(p1); console.log(p1keys);
|
如果想列出所有实例属性,无论是否可以枚举,都可以使用 Object.getOwnPropertyNames():
1 2
| let keys = Object.getOwnPropertyNames(Person.prototype); console.log(keys);
|
在 ECMAScript 6 新增符号类型之后,相应地出现了增加一个 Object.getOwnPropertyNames()的兄弟方法的需求,因为以符号为键的属性没有名称的概念。因此,Object.getOwnPropertySymbols()方法就出现了,这个方法与 Object.getOwnPropertyNames()类似,只是针对符号而已:
1 2 3 4 5 6 7 8
| let k1 = Symbol('k1'), k2 = Symbol('k2'); let o = { [k1]: 'k1', [k2]: 'k2' }; console.log(Object.getOwnPropertySymbols(o));
|