一、原型
1.那么首先,什么是原型呢?
每一个javascript对象在创建的时候就会有一个与之关联的对象B产生,对象B就是所说的“原型”。
(1) 原型也是一个对象,其他对象可以通过原型实现属性继承,
(2) js中的继承就是基于原型的继承;
(3) 所有对象在默认情况下都有一个原型,因为原型也是一个对象,所以每个原型自身
又有一个原型,默认的对象原型在原型链的顶端;
2.原型的定义及一些特点:
(1) 定义:原型是function对象的一个属性,它定义了构造函数制造出的对象的公共祖先。
通过该构造函数产生的对象,可以继承该原型的属性和方法。原型也是对象。
(2) 利用原型特点和概念,可以提取共有属性。
(3) 对象如何查看原型 — > 隐式属性 __proto__。
(4) 对象如何查看对象的构造函数 — > constructor。
(5) 每个对象都有 __proto__ 属性,但只有函数对象才有 prototype 属性
3.原型相关属性
(1) prototype:存在于构造函数中,指向这个构造函数的原型对象
(2) __proto__:存在于实例对象中,指向这个实例对象的原型对象
(3) constructor:存在于原型对象中,指向构造函数
(4) 若一个构造函数没有指定其原型,则其原型默认为Object.prototype
举例:
function Pro(name,age){
this.name=name;
this.age=age;
}
var pro1=new Pro();
var pro2=new Pro();
pro1.__proto__==pro2. __proto__;
pro1.__proto__==Pro.Prototype;
Pro.prototype.constructor==Pro();
Pro.prototype.__ptoto__==Object.prototype;
Object.prototype.constructor==Object();
结论:
(1) pro1.__proto__和pro2.prototype都指向构造函数Pro的原型对象;
(2) 如果使用new Person()创建多个对象,则多个对象都会同时指向Person构造函数的原型对象;
4. 原型方法
(1) getPrototypeOf():获取一个对象的原型
格式:Object. getPrototypeOf(对象)
(2) setPrototypeOf():设置一个对象的原型
格式:Object. setPrototypeOf(对象)
(3) hasOwnProperty():用于判断一个属性是本地属性,还是继承梓prototype对象的属性
格式:对象. hasOwnProperty(’属性名’):
(4) isPrototypeOf():判断对象a是否存在于另一个对象prototypeObject的原型链中(
只有存在继承关系时该方法才有用)
格式:prototypeObject.prototype.isPrototypeOf(a)
(5) instanceof:判断a是否是b的实例对象
格式:a.instanceof.b
二、原型链
1.那么首先,什么是原型链呢?
请看图:
A.protorype = { name: 'a' }
function A() ( this.name = 'A'; )
B.prototype = new A();
function B() { this.name = 'B'; }
C.prototype = new B();
function C(){ this.name = 'C'; }
- C继承B,B继承A,形成C->B->A的一条以原型为继承方式的原型链。
- 一个实例,至少应该拥有指向原型的proto属性,这是JavaScript中的对象系统的基础。
不过这个属性是不可见的,我们称之为“内部原型链”,以便和构造器的prototype所组成的
“构造器原型链”(亦即我们通常所说的“原型链”)区分开
2.原型链上属性的增删改查:
- 查:优先去实例对象上寻找是否有该属性,若没有,则再去其对应的原型上去寻找该属性,
若都没有,则返回undefined
- 增:直接给实例对象增添属性,则仅仅在实例对象上增添属性,若在原型上增添属性,
则在原型上增添属属性,则在原型上增添属性,其实例继承原型增添的属性。
- 删:delete仅仅能删除实例对象的属性,即构造函数原有的自己的属性和后来实例对象
增添的属性,还有关于delete的一点需注意,delete无法删除原型的属性和用var
定义的变量(即非window的属性)
- 改:更改实例对象的属性,则仅仅更改实例对象的属性值;更改原型的属性值,
则更改原型的属性值,继承该原型的对象对应属性值也会被更改;
三、继承
1.原型链是ECMAScript标准指定的默认继承方式
对象继承的发展:
(1) 传统模式:即正常的通过构造函数创建实例对象,来继承原型
缺点:继承了过多不必要的属性
(2) 借用其他构造函数(即通过call / apply来改变构造函数内this对象的引用)
缺点:无法继承借用构造函数的原型
A.prototype = {
name: 'a',
age: 18,
class: 1
}
function A(){
this.name = 'A';
}
function B(){
A.call(this);
}
var b = new B();
console.log(b.name); // A
console.log(b.age) // undefined
(3) 共享原型:即将其他构造函数的原型直接赋值给本构造函数的原型
缺点:两个原型会想回影响,更改其中一个原型,更一个对应的原型也会被更改。
A.prototype = {
name: 'a',
age: 18,
class: 1
}
function A(){
this.name = 'A';
}
B.prototype = A.prototype;
function B(){
this.name = 'B';
}
C.prototype = A.prototype;
function C() {
this.name = "C";
}
var a = new A();
var b = new B();
var c = new C();
console.log(a.age); // 18
console.log(b.age); //18
console.log(c.age); //18
// 原型继承成功
B.prototype.age = 20; //更改其中一个原型的age属性
console.log(b.age);//20
console.log(c.age); //20
// 继承A原型的B和C相互影响
(4) 圣杯模式:
每次继承的都是新创建的F构造函数实例,相互之间不会影响。其实此处针对F形成了闭包,Child引用了F,导致F不会销毁。
- 正常函数形式:
function inherit (Child, Parent) {
// 借用F这个中间量来继承,而不是直接共享原型
var F = function (){}
F.prototype = Parent.prototype;
Child.prototype = new F();
// 自定义构造函数原型时,同时要更正自定义原型的constructor,否则一般默认为Object(),次函数若不指定constructor,则constructor为Parent
Child.prototype.constructor = Child;
Child.prototype.uber = Parent; //记录真正继承的是谁
}
- 闭包形式:
var inherit = (function(){
var F = function (){};
return function (Child, Parent) {
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.prototype.uber = Parent;
}
})();