构造函数模式中拥有了类和实例的概念,并且实例和实例之间是相互独立开的即实例识别。
基于函数的原型模式解决了方法和属性公有的问题,把实例之间相同的属性和方法提取成公有的属性和方法。
每一个函数数据类型(普通函数,类)都有一个自带属性:prototype(原型),且这个属性是一个对象数据类型的值(浏览器会开辟一个堆内存)。并且在 prototype 上浏览器给它加了一个属性 constructor(构造函数),属性值是当前函数(类)本身。每一个类都把供实例调取的公共属性方法存储到自己的原型上。
每一个对象数据类型(普通对象,实例,数组,正则,Math,prototype...)也自带属性:__proto__
,属性值是当前实例所属类的原型(prototype),不过不确定是谁的实例都是Object的实例
案例图解:
function Fn(){
this.x = 100;
this.y = 200;
this.getX = function (){
console.log(this.x);
}
}
Fn.prototype.getX = function(){
console.log(this.x);
}
Fn.prototype.getY = function(){
console.log(this.y);
}
var f1 = new Fn;
var f2 = new Fn;

Object 是 JS 中所有对象数据类型的基类(最顶层的类)
1、f1 instanceof Object 为 true 是因为 f1 通过__proto__
可以向上级查找,不管有多少级最后总能查找到 Object。
2、在 Object.prototype 上没有__proto__
这个属性
原型链模式:
f1.hasOwnPrototype("x") // hasOwnPrototype 是 f1 的一个属性
但是 f1 的私有属性上并没有这个方法,那该如何处理呢?
a、通过“对象名 . 属性名”的方式获取属性值的时候,首先在对象的私有的属性上进行查找,如果私有中存在这个属性,则获取的是私有的属性值;如果私有作用域没有,则通过__proto__
找到所属类的原型(类的原型上定义的属性和方法都是当前实例公有的属性和方法),原型上存在的话, 获取的是公有的属性值;如果原型上面也没有,则继续通过原型上的__proto__
向上查找,一直找到Object.prototype为止...
这种查找机制就是我们的“原型链模式”
在原型模式中,this 常用的两种情况:
a、在类中的 this.xxx = xxx;this 当前类的实例(this博客中第4种)
b、在某个方法中的 this 看执行的时候 “.” 前面是谁 this 就是谁(this博客中第1种)。注意:
需要先确定 this 的指向(this 是谁),把 this 替换成对应的代码,按照原型链查找机制,一步步查找结果。
案例:
function Fn(){
this.x = 100
this.y = 200
this.getY = function(){
console.log(this.y)
}
}
Fn.prototype = {
constructor: Fn // 如果不设置 constructor: Fn 那么它的指向就不是 Fn 而是 Object
y: 300
getX: function(){
console.log(this.x)
}
getY: function(){
console.log(this.y)
}
}
var f = new Fn();
f.getX(); // this 是 f console.log(f.x) == 100
f.__proto__.getX() // this 是 f.__proto__ console.log(f.__proto__.x) == undefined
Fn.prototype.getX() // this 是 Fn.prototype console.log(Fn.prototype.x) == undefined
f.getY() // this 是 f console.log(f.y) == 200
f.__proto__.getY() // this 是 f.__proto__ console.log(f.__proto__.y) == 300
批量设置原型上的公有属性和方法:重构原型对象的方式
function Fn(){
this.x = 100
}
Fn.prototype = {
constructor: Fn
a: function(){},
b: function(){}
}
1、只有浏览器天生给 Fn.prototype 开辟的堆内存里有 constructor,而我们自己开辟的这个堆内存没有这个属性,这样 constructor 指向就不是 Fn 而是 Object 了。为了和原来的保持一直需要手动添加 constructor 的指向
2、内置类增加公有属性只能在原型上增加方法且方法名不能和内置的重复,例如:
Array.prototype.myname = function(){console.log("ok")}
如果使用重构的方法(设置 constructor: Array)会把之前的已经存在于原型上的属性和方法替换掉,所以浏览器会把这种方法屏蔽掉
for in 循环遍历的时候,默认可以把私有的和它所属原型上扩展(自己添加)的属性和方法都可以遍历到,一般情况下遍历一个对象只需要遍历私有的即可。可以使用两种方法处理:
Object.prototype.aaa = function(){}
var obj = {nane: "小明", age: "18"}
for(var key in obj){
// if(obj.propertyIsEnumerable(key)){ // 第一种方法
// console.log(key)
// }
if(obj.hasOwnProperty(key)){
console.log(key)
}
}
Object.create(proObj) 创建一个新的对象,但还是要把 proObj 作为这个对象的原型
var obj = {
getX: function(){}
}
var obj2 = Object.create(obj)

”原型继承“是 JS 中最常见的一种继承方式,子类想要继承父类中所有的属性(私有属性和公有属性),只需要让”子类.prototype = new 父类“即可。
原型继承特点:是把父类中的私有属性和公有属性都继承到了子类原型上即成为了子类的公有属性
核心:原型继承并不是把父类中的属性和方法克隆一份,而是让子类和父类增加了原型连接。
网友评论