创建一个对象,先来看一段代码:
// 例如创建一个 Person 的构造函数, 让人有名字, 可以说话
function Person ( name ) {
this.name = name;
// 可以说话, 需要有一个方法
this.sayHello = function () {
console.log( '你好, 我是 ' + this.name );
};
}
var p1 = new Person( '小明' );
var p2 = new Person( '大伟' );
console.log( p1.sayHello == p2.sayHello ); // false
说明p1的sayHello()方法 和 p2的sayHello()方法 不相同。也就间接的说明每次创建一个实例化对象的时候都会在内存中重新开辟一个存储空间进行创建,一旦频繁创建实例化的对象时,会造成内存资源的浪费 。
解决方法
将公共的方法提取出来进行函数封装 (缺点: 代码比较乱、命名冲突几率大)
将所有方法都绑定到对象的属性上(虽然 提高了代码可读性、也减少了命名冲突,貌似还是可以进行优化)
JS 原生就支持解决方法:
每个函数都有一个 prototype(原型)属性,将共享的方法绑定到 .prototype 原型上 即可
// 例如创建一个 Person 的构造函数, 让人有名字, 可以说话
function Person ( name ) {
this.name = name;
}
// 可以说话, 需要有一个方法
Person.prototype.sayHello = function () {
console.log( '你好, 我是 ' + this.name );
};
var p1 = new Person( '小明' );
var p2 = new Person( '大伟' );
console.log( p1.sayHello == p2.sayHello ); // true 说明p1与p2访问的是同一组属性,同一个sayHello()方法。间接的说明用原型绑定公共的方法(共用一个存储空间)
原型的使用
constructor(构造器):每个构造函数被创建出来都有一个prototype(原型)属性,每个原型属性都有个constructor属性,该属性默认指向 原构造函数。
// 首先需要构造函数, 原则就是将独有的方法放在 对象中
function Person ( name, age, gender ) {
this.name = name;
this.age = age;
this.gender = gender;
}
将共享的方法放到默认的 .prototype 原型中,而独有的数据与行为放到对象中
// 将共享的方法提取出来
Person.prototype.sayHello = function () {
console.log( '你好, 我是 ' + this.name );
};
Person.prototype.eat = function () {
console.log( this.name + '在吃饭' );
};
Person.prototype.run = function () {
console.log( this.name + '在跑步. 已经跑了 ' + this.age + ' 年' );
};
// 直接给原型对象添加成员
var p1 = new Person( 'lilei', 19, '男' );
var p2 = new Person( 'hanmeimei', 18, '女' );
给原型 .prototype属性 重新赋值 ,将原来默认的 .prototype 覆盖掉
// 让 Person.prototype 指向另一个对象. 直接赋值
Person.prototype = {
constructor: Person, //constructor 默认指向 Object ,将其改为指向 原构造函数
sayHello: function () {
console.log( '第二种做法: 你好, 我是 ' + this.name );
}, eat: function () {
console.log( '第二种做法: ' + this.name + ' 在吃饭' );
}, run: function () {
console.log( '第二种做法: ' + this.name + ' 在跑步. 已经跑了 ' + this.age + ' 年' );
}
};
var p1 = new Person( 'lilei', 19, '男' );
var p2 = new Person( 'hanmeimei', 18, '女' );
如果访问的对象的某个方法时,实例化的对象上没有该方法,就会到该对象的原型 . prototype 上去找
下面是一个简单的 原型结构 分析图:
构造函数 ---------- 使用 prototype 属性 ----- 访问原型
实例对象 ---------- 使用 __proto__属性 ----- 访问原型
针对构造函数而言,原型就是 构造函数的 prototype 属性 --- 原型属性
针对实例对象而言,原型就是 实例对象的 __ptoto__属性 --- 原型对象