什么是对象原型
JavaScript 中所有的对象都有一个内置属性,称为它的 prototype (原型)。它本身是一个对象,故原型对象也会有它自己的原型,逐渐构成了原型链 。原型链终止于拥有 null
作为其原型的对象上。
准确地说,这些属性和方法定义在Object的构造器函数(constructor functions)之上的prototype
属性上,而非对象实例本身。
因此可以看出它的优点: 所有对象实例可以共享它包含的属性和方法。
看下面例子
javascript
function Person() {}
console.log(Person.prototype) // {constructor: ƒ}
Person.prototype.name = '张三'
Person.prototype.age = 18
Person.prototype.getName = function () {
console.log('name:' + this.name)
}
var p1 = new Person()
p1.getName() // name:张三
var p2 = new Person()
p2.getName() // name:张三
console.log(p1.getName == p2.getName) // true
console.log(p1.name === p2.name) // true
console.log(Person.prototype.constructor) // ƒ Person() {}
console.log(Person.prototype.constructor.prototype) // {name: "张三", age: 18, getName: ƒ, constructor: ƒ}
console.log(p1.prototype) // undefind
console.log(p1.constructor) // ƒ Person() {}
console.log(p1) // Person {}
由上可得:
Person 有个默认的属性prototype
,函数可以有属性。每个函数都有一个特殊的属性叫作原型(prototype)
。
p1和p2访问的都是同一个getName()
函数,即他们的属性和方法都是共享的。
默认情况下所有的函数都有一个constructor
(构造函数)属性 。该属性指向了prototype
属性所在函数。如:Person.prototype.constructor 指向的是 Person。
打印 p1
:
p1有个__proto__
属性,该属性内还有:Person的实例、constructor、__proto__
。以此提供了一个原型链。
prototype 属性:继承成员被定义的地方
prototype
属性的值是一个对象,我们希望被原型链下游的对象继承的属性和方法,都被储存在其中。
Object.prototype.watch()、``Object.prototype.valueOf()
等等成员,适用于任何继承自 Object()
的对象类型,包括使用构造器创建的新的对象实例。
Object.is()
、Object.keys()
,以及其他不在 prototype
对象内的成员,不会被"对象实例"或"继承自 Object()
的对象类型"所继承。这些方法/属性仅能被 Object()
构造器自身使用
yaml
console.log(Person.prototype) // {name: "张三", age: 18, getName: ƒ, constructor: ƒ}
console.log(Object.prototype) //{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, ...}
prototype
属性包含(指向)一个对象,你在这个对象中定义需要被继承的属性和方法。
create()
create()
实际做的是从指定原型对象创建一个新的对象。
javascript
function Person(name, age) {
this.name = name
this.age = age
this.getName = function () {
console.log('name:' + this.name)
}
}
var p1 = new Person('张三', 18)
var p3 = Object.create(p1)
console.log(p3)
constructor
每个实例对象都从原型中继承了一个constructor属性,该属性指向了用于构造此实例对象的构造函数。
arduino
// 获得对象实例的构造器的名字
console.log(p1.constructor.name) // Person
修改原型
为Person
构造器的 prototype
属性添加一个新的方法。
javascript
function Person(name, age, sex) {
this.name = name
this.age = age
this.sex = sex
this.getName = function () {
console.log('name:' + this.name)
}
}
var p1 = new Person('张三', 18, '男')
Person.prototype.getSex = function() {
console.log('sex:' + this.sex);
}
p1.getSex() // sex:男
getSex
方法虽在 p1
实例后定义,但p1
也可使用。旧有对象实例的可用功能被自动更新了 。
这种继承模型下,上游对象的方法不会复制到下游的对象实例中;下游对象本身虽然没有定义这些方法,但浏览器会通过上溯原型链、从上游对象中找到它们。这种继承模型提供了一个强大而可扩展的功能系统。
一种极其常见的对象定义模式是,在构造器(函数体)中定义属性、在 prototype
属性上定义方法。如此,构造器只包含属性定义,而方法则分装在不同的代码块,代码更具可读性。
javascript
// 构造器及其属性定义
function Test(a,b,c,d) {
// 属性定义
};
// 定义第一个方法
Test.prototype.x = function () { ... }
// 定义第二个方法
Test.prototype.y = function () { ... }
继承
例子
javascript
function Person(name, age, sex) {
this.name = name
this.age = age
this.sex = sex
this.getName = function () {
console.log('name:' + this.name)
}
}
构造函数继承
csharp
var p1 = new Person('张三', 18, 'nan')
p1.getName()
原型式继承
继承的对象函数并不是通过复制而来,而是通过原型链继承(通常被称为 原型式继承 ------ prototypal inheritance) 。
javascript
function Person(name, age, sex) {
this.name = name
this.age = age
this.sex = sex
this.getName = function () {
console.log('name:' + this.name)
}
}
Person.prototype.getSex = function() {
console.log('sex:' + this.sex);
}
如果一个新的函数要继承上来构造函数的属性和方法,还有增加新的属性和方法:
javascript
function Teacher(name, age, sex, profession) {
Person.call(this, name, age, sex);
this.profession = profession;
}
var t1 = new Teacher('王二', 30, '男', '老师')
console.log(t1) // Teacher {name: "王二", age: 30, sex: "男", profession: "老师", getName: ƒ}