什么是原型
《JavaScript高级程序设计》中说: 创建的每个构造函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象。而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
简单的说:
在构造函数创建出来的时候,系统会默认的帮构造函数创建并关联一个对象,这个对象就是原型。 原型对象中的属性和方法可以被构造函数实例化的对象访问。
创建一个Person构造函数
function Person(){
}
如何访问构造函数中的原型
访问方式:
- 使用构造函数访问原型,方式:构造函数.prototype
console.log(Person.prototype); // 输出一个Object对象
使用实例对象访问原型,方式:实例对象.__proto__(这个知识点后续会讲,.__proto__是非标准的属性)
构造函数实例化对象使用new操作符.
var p = new Person();
// 实例对象访问原型的方式:p.__proto__
console.log(p.__proto__);
在默认情况下,所有的原型对象都会自动获得一个constructor(构造函数)属性,指向对应的构造函数。(这个稍后会介绍)
也就是Person.prototype.constructor指向Person。
(这个在后续会继续讲解)
如何给原型对象添加属性和方法呢?
原型默认的是一个空对象,可以使用.点语法和[]中括号的方式给原型添加属性和方法。具体可以看这里,我的对象的动态特性–动态给对象添加属性和方法中有介绍。
现在我们给原型对象添加属性和方法(记得哦,原型(prototype)是构造函数的原型哦,一定是构造函数.prototype即Person.prototype,不是对象./proto,它是非标准的属性,只用来做测试用)
Person.prototype.name = "kong";
Person.prototype.learn = function(){
console.log(" how to learn prototype?");
}
原型的作用
原型的属性和方法可以被使用该构造函数创建出来的对象使用
这里我们用Person实例化的p对象对原型中的属性和方法进行访问。
p.learn();//输出 how to learn prototype?
console.log(p.name);//kong
p.study(); //Uncaught TypeError: p.study is not a function因为对象本身和原型中都没有该方法,故会报错
对象去访问属性和方法的规则:
1.会首先在对象自己内部进行查找,如果找到了,就直接使用
2.如果没有找到,就去原型中查找,查找到之后,使用
3.如果原型中还没有, 如果是属性,就是Undefined
4.如果是方法,就报错
再继续来个例子验证这些规则吧:
function Car(brand,price){
this.brand = brand;
this.price =price;
}
Car.prototype.sayName =function(){
console.log("我是"+this.brand);
}
Car.prototype.brand ="我是车啊"
var car1 = new Car("BMW","40万");
var car2 = new Car("BENZ","60万");
console.log(car1.brand); // BMW
car1.sayName();// 我是BMW
car2.sayName(); // 我是BENZ
console.log(car2.name);// undefined
car2.run(); // car2.run is not a function
使用原型来解决构造函数存在的问题
还记得我们上篇讲javascript面向对象之—构造函数的时候,说道
把sayName()函数的定义转移到构造函数外部,而在构造函数内部,我们将sayName()属性设置成等于全局的sayName函数。这样一来,由于sayName包含的一个指向函数的指针,因此person1和person2对象就共享了全局作用域中定义的同一个sayName()函数。但是使用这种方式存在的问题
1.全局变量增多,造成污染
2.代码结构混乱,不易维护
这种情况可以使用原型解决。
通过上述演示的所有的例子,我们知道 :构造函数的原型对象中的成员(属性和方法),可以被该构造函数创建出来的所有对象访问,而且,所有的对象共享该对象。
所以,我们可以将构造函数中需要创建的函数,放到原型对象中存储,这样就解决 全局变量污染的问题 以及 代码结构混乱的问题。
可以修改为这样子:
function Person(name,age,job){
this.name =name;
this.age = age;
this.job = job;
// this.sayName = sayName;
}
/* function sayName(){
console.log(this.name)
}*/
Person.prototype.sayName = function(){
console.log(this.name)
}
var person1 = new Person("Kong",23,"Software Enginner");
var person2 = new Person("diligentkong",24,"doctor");
person1.sayName();
person2.sayName();
原型的使用方法
1.利用对象的动态特性给原型对象添加成员
2.直接替换原型对象
示例:利用Car的例子,给原型对象添加成员(即属性和方法)
Car.prototype.width = "250cm";
Car.prototype.seats = function(){
console.log("我是5个座位的!");
}
示例:直接替换原型对象
Car.prototype = {
msg : "我的原型是被替换了吗?",
notice: "是的呀!"
}
打印输出:console.log(Car.prototype);
如果使用第二种方式使用原型,那么会出现如下问题:
在替换原型之前创建的对象的原型 和 在替换原型对象之后的创建的对象
的原型 不是同一个!
替换前:
替换后:
总结:一定要记得prototype是构造函数的属性哦,与实例化的对象无关!