共计 3821 个字符,预计需要花费 10 分钟才能阅读完成。
JavaScript – Prototype
JavaScript 是一门动态语言, 你可以在任何时候向对象上添加属性, 如下
function Student() {
this.name = 'LeBron James';
this.gender = 'Male';
}
var studObj1 = new Student();
studObj1.age = 15;
alert(studObj1.age); // 15
var studObj2 = new Student();
alert(studObj2.age); // undefined
正如上面的实例, age
属性附加在 studObj1
实例上. 然而 studObj2
实例没有这个属性, 因为 age
属性只在 studObj1
实例上定义了.
那么, 如果想在后期添加一个属性且能被所有的实例所共享, 该怎么办? 答案这就今天主角 Prototype
.
Prototype
是一个对象, 默认情况下与 JavaScript 中的任何一个函数或对象有关, 只是唯一区别在于函数的 prototype
属性是 可访问
和可修改
的,而 对象
的prototype
属性是不可见的.
默认情况下任何一个函数包含 Prototype
对象, 如下图:
prototype
对象是一种特殊类型的可枚举对象, 可以将需要附加属添加到其上,这些属性将在其构造函数的所有实例之间共享。
因此, 把上面的示例中使用函数的 prototype
来添加属性, 以便于所有对象中都可以访问到, 如下:
function Student() {
this.name = 'LeBron James';
this.gender = 'M';
}
Student.prototype.age = 15;
var studObj1 = new Student();
alert(studObj1.age); // 15
var studObj2 = new Student();
alert(studObj2.age); // 15
使用 字面量
或 通过 new 关键字和构造函数
的方式创建的每一个对象都包含 __proto__
属性, 该属性指向创建此对象的函数的 原型对象
.
你可以在谷歌和火狐开发者调试工具中查看该属性(__proto__
) , 根据下面的示例:
function Student() {
this.name = 'LeBron James';
this.gender = 'M';
}
var studObj = new Student();
console.log(Student.prototype); // object
console.log(studObj.prototype); // undefined
console.log(studObj.__proto__); // object
console.log(typeof Student.prototype); // object
console.log(typeof studObj.__proto__); // object
console.log(Student.prototype === studObj.__proto__); // true
正如上面例子看到, 函数通过 [[函数名称]].prototype
方式访问到原型对象. 但是, 对象 (实例) 并没有暴露出 prototype
属性,而是使用 __proto__
来访问它.
注意
原型对象是一个特殊类型的枚举对象, 不能通过
for...in
或forEach
进行迭代.
为什么不可枚举, 聪明肯定会想到 属性描述符
, 下面可以通过Object.prototype
这段代码来验证:
Object.getOwnPropertyDescriptors(Object.__proto__)
//
{constructor: {value: ƒ, writable: true, enumerable: false, configurable: true}
hasOwnProperty: {value: ƒ, writable: true, enumerable: false, configurable: true}
isPrototypeOf: {value: ƒ, writable: true, enumerable: false, configurable: true}
propertyIsEnumerable: {value: ƒ, writable: true, enumerable: false, configurable: true}
toLocaleString: {value: ƒ, writable: true, enumerable: false, configurable: true}
toString: {value: ƒ, writable: true, enumerable: false, configurable: true}
valueOf: {value: ƒ, writable: true, enumerable: false, configurable: true}
}
enumerable
为 true
表示该属性可枚举, 也就是能通过 for in , forEach, Object.keys 来进行迭代. 注意上例中 enumerable
属性都是 false
.
Object 对象的原型
前面提及到, 原型对象在对象中是不可见. 使用 Object.getPrototypeOf(obj)
方法来访问实例的原型对象. (这也是推荐方式, __proto__
并不是标准属性, 在 IE11 以下其它浏览器中没有实现).
function Student() {
this.name = 'LeBron James';
this.gender = 'M';
}
var studObj = new Student();
Student.prototype.sayHi= function(){alert("Hi");
};
var studObj1 = new Student();
var proto = Object.getPrototypeOf(studObj1);
// returns Student's prototype object
alert(proto.constructor);
// returns Student function
Object 原型对象包含如下 属性
和 方法
属性 | 描述 |
---|---|
constructor |
返回创建该实例的构造函数 |
__proto__ |
指向创建该实例的构造函数的原型对象. |
方法 | 描述 |
---|---|
hasOwnProperty() |
返回一个布尔值,指示对象是否包含指定的属性作为该对象的直接属性,而不是通过原型链继承。 |
isPrototypeOf() |
返回一个布尔值,指示指定的对象是否位于调用此方法的对象的原型链中。 |
propertyIsEnumerable() |
返回一个布尔值,该布尔值指示指定的属性是否可枚举。 |
toLocaleString() |
返回本地格式的字符串. |
toString() |
返回对象字符串形式. |
valueOf() |
返回指定对象的原始值. |
Chrome 和 Firfox 将对象的原型表示为 __proto__
, 而内部引用为 [[Prototype]]
. IE 不支持, 只有 IE11
包含它.
修改原型
如上所述, 每个对象都能链接到函数的原型对象. 如果您更改了函数的原型, 则只有新对象将链接到更改后的原型. 所有其他现有对象仍然链接到旧的函数原型. 下面实例来演示这个场景:
function Student() {
this.name = 'LeBron James';
this.gender = 'M';
}
Student.prototype.age = 15;
var studObj1 = new Student();
alert('studObj1.age =' + studObj1.age); // 15
var studObj2 = new Student();
alert('studObj2.age =' + studObj2.age); // 15
Student.prototype = {age : 20};
var studObj3 = new Student();
alert('studObj3.age =' + studObj3.age); // 20
alert('studObj1.age =' + studObj1.age); // 15
alert('studObj2.age =' + studObj2.age); // 15
使用原型
原型对象被 JavaScript 引擎用来做两件事:
- 查找对象的属性和方法
- 在 JavaScript 中实现继承
function Student() {
this.name = 'LeBron James';
this.gender = 'M';
}
Student.prototype.sayHi = function(){alert("Hi");
};
var studObj = new Student();
studObj.toString();
在上面的示例, toString()
方法在 Student
中没有定义, 那么它是如何以及从哪里找到 toString()
的呢?
在这里,原型出现了. 首先, JavaScript 引擎检查 studObj 是否存在 toString
方法?. 如果没有找到, 那么它使用 studObj
的 __proto__
链接指向 Student 函数
的 原型对象
. 如果它仍然无法找到它那么它会在往上层并检查 Object 函数的原型对象, 因为所有对象都是从 JavaScript 中的 Object 派生的, 并查找 toString()
方法. 因此, 它在 Object 函数的原型对象中找到 toString()
方法,因此我们可以调用 studObj.toString()
.
查找方式, 如下图所示
上述就是原型基本知识点以及应用.