-
原型对象概念
在构造函数创建出来的时候,系统会默认帮构造函数创建并关联的一个新对象
自定义构造函数的原型对象默认是一个空对象。-
原型对象的作用
构造函数中的原型对象中的属性和方法可以被使用该构造函数创建出来的对象使用。
即以自定义构造函数方式创建出来的所有对象,自动拥有和共享该构造函数的原型对象中的所有属性和方法。-
如何访问构造函数的原型对象
① 构造函数.protoType
② 对象.proto(不推荐)
-
设置原型对象的属性和方法
① 利用对象的动态特性来为构造函数的原型对象添加属性和方法
② 替换原型对象
我们把"构造函数的原型对象"简化为"构造函数的原型"。
-
代码示例:
<script>
//01 提供构造函数
function Student(number,name,age) {
//02 设置对象的属性
this.number = number;
this.name = name;
this.age = age;
//03 设置对象的方法
this.read = function () {
console.log("阅读");
}
}
//02 使用构造函数来创建对象
var stu1 = new Student("20170201","严雯",18);
var stu2 = new Student("20170202","严二雯",16);
//03 设置构造函数的原型
//001 为构造函数的原型对象添加属性
Student.prototype.className = "神仙特技1班";
//002 为构造函数的原型对象添加方法
Student.prototype.description = function () {
console.log("我的名字是" + this.name + "学号是" + this.number + "班级是" + this.className);
};
//04 尝试访问
console.log(stu1.className);
console.log(stu2.className);
stu1.description();
stu2.description();
//05 原型属性和原型方法|实例属性和实例方法
//在构造函数内部设置的属性 => 实例属性
//在构造函数内部设置的方法 => 实例方法
//在构造函数的原型对象上设置的属性 => 原型属性
//在构造函数的原型对象上设置的方法 => 原型方法
//06 思考:如果实例属性(方法)和原型属性(方法)存在同名,那么对象在使用属性(方法)的时候,访问的是
//实例属性(方法)还是原型属性(方法)?
//说明:使用构造函数创建的对象在访问属性或调用方法的时候:
// ① 首先在内部查找有没有对应的实例属性(方法)
// ② 如果有,那么就直接使用,如果没有找到,那么就继续去构造函数的原型中查找
// ③ 原型对象中如果存在该属性或方法,那么就直接使用,如果不存在指定的属性则返回undefined,如果不存在指定的方法则报错
//07 注意点
//[01] 通常在创建对象之前设置构造函数的原型对象(提供共享的属性|方法)
//[02] 访问原型对象的正确方法是 构造函数.protoType 而不是 对象.protoType
</script>
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
-
原型对象的使用注意:
-
实例和实例化
实例化:通过构造函数创建具体对象的过程。
实例:通过构造函数实例化出来的对象,我们称之为该构造函数的一个实例。
** 注意:在说实例的时候,一定要指定是某个具体构造函数的实例**
代码示例:
<script>
//01 提供一个构造函数
function Person() {
}
//02 根据该构造函数创建对象 == 使用Person构造函数实例化对象
var p1 = new Person();
//p1 是构造函数Person的一个实例
</script>
-
原型的使用方法
① 利用对象的动态特性给原型添加属性|方法
- 如果要添加的方法过多,则有大量重复代码
② 直接替换原型对象
- 01 替换前后创建的对象所指向的原型对象不一致
- 02 替换原型对象会切断和之前的原型对象之间的关系
-
使用动态特性设置原型的属性和方法代码示例:
<script>
//01 提供一个空的构造函数
function Person() {
}
//02 设置原型对象的属性和方法
Person.prototype.name = "臧飒";
Person.prototype.age = 18;
//.......
Person.prototype.sayHello = function () {
console.log("hello");
}
//如果要添加大量的属性和方法,则会出现很多重复的代码
</script>
-
直接替换原型对象代码示例:
<script>
//01 提供一个空的构造函数
function Person() {
}
//在替换原型对象之前创建对象,其指向的原型对象为默认创建的空对象
var p1 = new Person();
//02 直接替换原型对象(以字面量的方式来创建原型对象)
Person.prototype = {
name:"默认的姓名",
age:"默认的年龄",
sayName:function () {
console.log("我的名字是" + this.name);
}
};
//在替换原型对象之后创建的对象,其指向的原型对象为替换过后的原型对象
var p2 = new Person();
//03 问题:替换原型对象前后所创建的对象,其指向的原型对象不是同一个
//04 打印验证
console.log(p1.name); //undefined
console.log(p2.name); //默认的姓名
//p1.sayName(); //调用的方法不存在
p2.sayName(); //我的名字是默认的姓名
</script>
总结:
01 使用字面量的方式创建原型对象(替换原型对象)会切断构造函数和之前的原型对象之间的联系
① 当替换构造函数的原型对象的时候,已经使用构造函数创建出来的对象指向的原型对象不会发生改变
02 在替换原型对象之前创建的对象和替换之后创建的对象所指向的原型对象不同
② 如果是替换了构造函数的原型对象,那么构造函数的新的原型对象和旧的原型对象之间没有任何关系
-
原型对象的使用注意:
① 访问属性:构造函数创建出来的对象在访问属性的时候,会先在实例内查找,如果没有找到则进一步到对应的原型对象中查找
② 设置属性:
在使用点语法进行赋值的时候,无法操作到对应的原型对象
如果该属性在对象中已经存在,则修改该属性的值
如果该属性在对象中尚未存在,则新增该属性
③ 设置原型对象的属性:
[01] 设置原型对象的属性,只能通过构造函数.Prototype的方式|替换原型对象的方式设置
[02] 如果原型对象的属性是值类型,那么只能通过Person.prototype.属性的方式修改其值
如果原型对象的属性是引用类型,那么可以通过对象名.引用对象.属性名的方式设置|修改
(1) 使用构造函数创建出来的多个对象的原型对象中的该属性指向的是同一块数据
(2) 某个对象对该原型对象属性进行了修改会影响到其他的对象
-
01 属性的访问逻辑
<script>
//01 提供一个简单的构造函数
function Person() {
this.name = "张三";
this.age = 20
}
//02 设置构造函数的原型对象
Person.prototype = {
name:"原型对象中的属性-姓名",
className:"原型对象中的属性-班级"
}
// Person.prototype.name = "李四";
// Person.prototype.className = "班级";
//03 使用构造函数创建对象
var p1 = new Person();
//04 访问属性
console.log(p1.name); //name属性在对象和对应的原型对象中均存在,则优先使用对象中的属性(就近原则)
console.log(p1.className); //className属性在对象中没有,则使用对应的原型对象中的属性
</script>
-
02 设置属性(值类型)
<script>
//01 提供构造函数
function Person() {
this.name = "对象中的属性-姓名"
}
//02 设置原型对象
Person.prototype.age = 20;
//03 使用构造函数创建对象
var p = new Person();
//04 设置属性(值类型)
p.age = 30; //思考:它设置了谁的值?
console.log(p.age); //30
//结论:在对对象的属性直接进行赋值操作的时候,无法操作到原型对象,只是对实例属性进行增加|修改
p.filmay = ["哥哥","姐姐","爸爸","妈妈"];
console.log(p.filmay); //["哥哥", "姐姐", "爸爸", "妈妈"]
//p.car.type = "汽车"; 注意:不能这样进行赋值(报错)
var p2 = new Person();
console.log(p2.age); //访问的是对象的原型属性(20)
console.log(p2.filmay); //undefined
</script>
-
03 设置原型对象的属性
<script>
//01 提供一个空的构造函数
function Person() {
}
//02 设置原型对象的属性(值类型)
Person.prototype.className = "逍遥派01班";
Person.prototype.car = {
type:"汽车",
number:"B 0888"
};
//03 创建对象
var p1 = new Person();
var p2 = new Person();
//如果原型对象中的属性是值类型,那么只能通过Person.prototype.属性的方式修改其值
//如果原型对象中的属性是引用类型,则情况不一样
console.log(p1.car.type); //汽车
console.log(p2.car.type); //汽车
p1.car.type = "火车";
console.log(p1.car.type); //火车
console.log(p2.car.type); //火车
//总结:如果原型对象中的某个属性为引用类型,则使用构造函数创建出来的多个对象的原型对象中的该属性指向的是同一块数据
// 某个对象对该原型对象属性进行了修改会影响到其他的对象
//补充说明
p1.car = "测试汽车";
console.log(p1.car); //测试汽车
console.log(p1.car.type); //undefined
//直接操作设置的是对象的实例属性,而非原型对象中的属性
console.log(p2.car.type); //火车
//构造器属性
console.log(Person.prototype.constructor);
console.log(p1.constructor);
console.log(Person.prototype.constructor == p1.constructor);
console.log(Person.prototype.constructor == p1.__proto__.constructor);
</script>
-
proto属性说明:
- proto是一个非标准属性
- 即ECMAScript中并不包含该属性,这只是某些浏览器为了方便开发人员开发和调试而提供的一个属性,不具备通用性
- 建议:在调试的时候可以使用该属性,但不能出现在正式的代码中
代码示例
<script>
//01 提供一个空的构造函数
function Person() {
}
//02 设置构造函数的原型对象的方法
Person.prototype.sayHello = function () {
console.log("hello");
}
//03 创建对象
var p = new Person();
//04 通过对象的__proto__设置原型对象
p.__proto__.sayHi = function () {
console.log("hi");
}
//05 尝试调用上面的方法
p.sayHello(); //hello
p.sayHi(); //hi
</script>
-
使用原型对象解决构造函数方式创建对象的问题
代码示例:
<script>
//01 提供构造函数
function Student(number,name,age) {
this.number = number;
this.name = name;
this.age = age;
}
//02 设置构造函数的原型方法
Student.prototype.description = function () {
console.log("我的名字是" + this.name + "学号是" + this.number + "年龄是" + this.age);
};
//03 使用构造函数来创建对象
var stu1 = new Student("20170201","严雯",18);
var stu2 = new Student("20170202","严二雯",16);
//04 调用创建该对象的构造函数的原型对象中的方法(检查原型方法)
stu1.description();
stu2.description();
//05 验证构造函数创建出来的多个对象共享原型对象的属性和方法
console.log(stu1.description == stu2.description);
</script>