目录
- 原型系统
- 原型
- 基本概念
- 示例
- 关系
- 改进创建对象的模式
- 单纯使用原型模式创建对象
- 综合构造函数和原型模式创建对象
- 原型
原型系统原型基本概念
原型(prototype)是函数特有的属性。
只要创建了一个函数,这个函数就会自动创建一个prototype属性(显式原型),并指向该函数的原型对象。
原型对象上都有一个constructor属性,指向prototype属性所在的函数(即函数本身)。
而对于每一个构造函数创建出的实例对象,内部都会有一个[[Prototype]]属性(隐式原型),同样指向函数的原型对象。
在Firefox、Safari和Chrome浏览器中,每个对象都可以通过__proto__属性访问到它们的[[Prototype]]属性。
对其他浏览器而言,这个属性对脚本是不可见的(使用
getPrototypeOf()
方法获取类的原型)。
示例
// 创建一个函数function Person(){};// 检查其是否有prototype这个属性console.log(Person.hasOwnProperty('prototype'));// 查看Person.prototype的内容console.log(Person.prototype);
从输出结果可以看出,Person
函数确实拥有prototype
这个属性,并且Person.prototype
这个原型对象包含了一个constructor
属性,指向了一个函数,这个函数就是构造函数Person()
。
// 补充上面Person()构造函数的原型Person.prototype.name = '张三';Person.prototype.age = 20;Person.prototype.sayName = function(){ console.log(this.name);}// 创建对象const person = new Person();// 输出对象console.log(person);
从输出结果可以看出,由于将属性和方法都放到了prototype
上,输出person
这个实例对象的时候是找不到name
、age
和sayName()
方法的,只有一个隐式原型对象[[Prototype]]
,在这个隐式原型对象上才能找到name
、age
和sayName()
方法,以及指向构造函数Person()
的constructor
属性。
使用原型的好处就是可以共享属性和方法。
// 创建两个对象const person1 = new Person();const person2 = new Person();console.log(person1 === person2); // falseconsole.log(person1.sayName === person2.sayName); // true
person1 === person2
会返回false
,因为它们是两个不同的对象。person1.sayName === person2.sayName
会返回true
,因为这两个不同的对象共享这同一个sayName()
方法。
关系
构造函数、原型对象和实例对象三个的关系如下图:
改进创建对象的模式单纯使用原型模式创建对象
这种模式将所有的属性和方法都进行共享。
function Person(){};// 将属性添加到原型上Person.prototype.name = '张三';Person.prototype.age = 20;Person.prototype.sayName = function(){ console.log(this.name);}const person1 = new Person();const person2 = new Person();
确实,方法共享后,节省了内存,但是缺点是属性也随之共享了。
例如上面这个例子,person1
和person2
这两个对象创建后的name
和age
都是张三
和20
。
可以再手动修改对象的属性:
person2.name = '李四';person2.age = 18;
综合构造函数和原型模式创建对象
将不需要共享的属性放在构造函数上,将共享的属性和方法放在原型对象上。
同时,可以使用默认值来改进构造函数。
function Person(name='张三', age=20){ this.name = name; this.age = age; }Person.prototype.sayName = function(){ console.log(this.name);}const person1 = new Person();const person2 = new Person('李四',18);console.log(person1);console.log(person2);
- Person()构造函数的参数列表:
name='张三',age=20
表示name
的默认值是'张三'
,age
的默认值是20
。
另一种设置默认值的方式是:
function Person(name,age){ this.name = name || '张三'; this.age = age || 20;}
如果
name
和age
没有传入参数,则为undefined
,在或运算符中会被转为false
,最终赋值给name
和age
的是或运算符右边的数据。
由于
person1
在创建的时候没有传入参数,所以默认是张三,20
;而person2
在创建的时候传入了参数,所以属性是李四,18
。输出两个对象,发现它们只有
name
和age
两个私有属性,sayName()
方法在原型对象上,是共享的。