珠峰培训

js中原型链模式详解-了解原型模式

作者:jiangwen

2016-08-07 22:30:21

399

1.原型链模式

1.1 原型链构成

  1. 每一个函数数据类型(普通函数、类)都有一个天生自带的属性:prototype(原型),并且这个属性是一个对象数据类型的值

  2. 在prototype上,浏览器自动给它增加了一个constructor(构造函数)属性,属性是当前函数或类本身

  3. 每一个对象数据类型(普通对象,实例,prototype)也自带一个属性:__proto__ (两边各自两个下划线),属性值是指向当前实例所属原型

1.2 提取共有

基于构造函数的原型模式,解决方法或属性共有的问题,把实例之间相同的属性和方法提取为共有的

function CreatePerson(name,age){
this.name = name;
this.age = age;
}
CreatePerson.prototype.write = function(){
console.log(this.name,this.age);
}
var p1 = new CreatePerson("Amy",12);
var p2 = new CreatePerson("Bob",14);

想让谁公有就把谁提取到CreatePerson.prototype上即可

console.log(p1.write === p2.write); //true

将新生成的对象的__prop__属性赋值为构造函数的prototype属性,使得通过构造函数创建的所有对象可以共享相同的原型,这意味着同一个构造函数创建的所有对象都继承自一个相同的对象,因此它们都是同一个类的对象。

在JavaScript中,每一个函数都有默认的原型对象属性prototype,该对象默认包含了两个成员属性:constructor和__proto__。

1.3 基类Object

1、Object是所有对象数据类型的基类(最顶层的类),在它的原型上没有__proto__属性

2、 f1通过__proto__可以向上级查找,不管找到多少级,最后总能找到Object

console.log(f1 instanceof Object); //true

3、f1.hasOwnProperty(x) ==> hasOwnProperty是f1的属性。

但是f1的私有属性上并没有这个方法,如何处理的呢?

1).通过对象名.属性名的方式获取属性值的时候,首先在对象的私有属性上进行查找,如果私有存在这个属性,则获取的是私有的属性值;

2).如果私有的没有这个属性,则通过__proto__找到所属类的原型,原型上定义的属性和方法都是当前实例的公有属性和方法,原型上存在的话,获取的是公有的属性值;

3).如果原型上也没有,则通过原型上的__proto__继续向上查找,一直找到Object的prototype为止

这种查找模式叫原型链模式

4、在IE浏览器中,原型模式也是同样原理,但是IE浏览器害怕通过__proto__把公有的属性修改,所以禁止使用__proto__查找

f1.sum = function(){}  //修改私有的
f1.__proto__.sum = function(){} //修改公有的 //IE禁用
Fn.prototype.sum = function(){} //修改公有的

2.原型扩展

2.1 批量设置原型上的公有属性和方法

1、起别名

var pro = Fn.prototype;  //把原来原型指向的地址赋给pro,他们操作的是同一个内存空间
pro.getY = function(){}

2、重构原型对象

Fn.prototype = {
a: function(){

},
b: function(){

}
};
f.a();
f.b();
console.log(f.constructor); //function Object

自己新开辟一个堆内存,存放公有属性和方法,把浏览器原来提供的Fn.prototype替换掉,并在空闲时销毁

1). 只有浏览器天生提供的Fn.prototype堆内存中才会有constructor,而自己开辟的堆内存中没有这个属性,所以f的构造方法会找到Object;constructor的指向就不是Fn而是Object

为了和原来的保持一致,需要手动增加constructor指向

contrunstor: Fn

2).用这种方式给内置类增加公有属性

Array.prototype.unique = function(){
//数组驱重的方法
}
Array.prototype = {
constructor : Array,
unique : function()
{

}
//这样做会把之前存在于原型上的方法和属性给替换掉,所以用这种方式修改内置类的话浏览器会屏蔽掉,无法修改
};

但是可以一个个的修改内置的方法,当方法名和内置方法名重复的时候,会替换掉内值方法

所以在内置类原型上增加方法一定要加上特殊前缀,以示区别,如:

Array.prototype.sort = function(){
console.log("OK");
}
//这样将修改内值方法sort,失去排序功能

2.2 原型中的this

function Fn(){
this.x = 100;
this.y = 200;
this.getY = function(){
console.log(this.y);
}
}
Fn.prototype = {
constructor: Fn,
y : 300,
getX : function()
{
console.log(this.x);
},
getY : function(){
console.log(this.y);
}
};
var f = new Fn;
f.getX();
f.__proto__.getX();

在类中的this:this.xxx=xxx ==>this指当前类的实例

某个方法中的this,看执行的时候”.”前边是谁就是谁

f.getX();  //this->f 
console.log(f.x); //100
f.__proto__.getX(); // this->f.__proto__
//f.__proto__.x->undefiend先向公有方法中查找,然后向Object中查找,没有则为undefined
Fn.prototype.getX(); //undefined
f.getY(); //200
f.__proto__.getY(); //300 this->f.__proto__.getY(),即公有方法中的getY(),公有中y:300
  1. 先确定this的指向

  2. 把this替换成对应的代码

  3. 按照原型链查找机制查找结果

2.3 链式写法

执行完成一个方法后继续执行下一个方法

  1. arr.sort(function(a,b){return a-b}).reverse().pop();

  2. 原理:arr执行sort方法->因为sort是Array.prototype上的公有方法,arr是Array的一个实例->数组才能使用Array原型上定义的属性和方法

  3. sort方法执行完成后返回的是一个数组,可以继续执行下面的方法,reverse返回的也是一个数组;pop返回的是被删除的那个元素

2.4 原型链继承

for(var key in obj){
//默认可以把私有的和在所属类原型上扩展的方法和属性都能遍历到,但是一般情况下遍历对象只遍历私有的即可
if(obj.propertyIsEnumerable){
console.log(key);
//只将私有属性和方法遍历出来,自定义的方法和属性以及公有的方法和属性将不被遍历
}
if(obj.hasOwnProperty(key)){
//console.log(key);
//只遍历私有的
}
//后者方式更普遍
}

Object.create(proto);创建一个拥有指定原型和若干指定属性的对象;
proto一个对象,作为新创建对象的原型;
Object.create(proto);创建一个新对象,把proto作为这个对象的原型;
propertyIsEnumerable该值指示指定属性是否为对象的一部分以及该属性是否是可枚举的。

2.5 原型继承方式

function A(){
this.x = 100;
}
A.prototype.getX = function(){
console.log(this.x);
}
function B(){
this.y = 200;
}
B.prototype = new A;
  1. 子类B继承父类A的所有属性和方法(私有和公有的)的方式,A的实例既有私有属性方法,又有公有属性和方法,所以用A的实例作为B的原型地址

  2. 继承特点:把父类的私有的和公有的都继承到子类原型上,都是子类公有的

  3. 核心:原型继承并不是把父类的属性和方法克隆一份给子类,而是让父类和子类之间增加了原型链的链接,以后子类的实例想要用父类的方法,需要一级级的向上查找来使用

3.除原型链外的其他继承方式

  • call继承

  • 把父类的私有的属性和方法克隆一份,作为子类私有的属性

  • 冒充对象继承

  • 把父类私有的和公有的克隆一份给子类私有的

  • 混合模式继承

  • 原型继承+call继承

  • 寄生组合式继承

  • 私有的只拿私有的(call),公有的拿公有的(Object.create())

  • 中间类继承法(不兼容)

4.面向对象

对象:js中万物皆对象,是泛指

类:对象的具体细分

实例:某一个类别中具体的一个事物

内置类:Array, Number, String, Boolean ,Null , Undefined, Object, RegExp, Date, Function

  • HTMLCollection元素集合类,通过getElementByTagName获取的元素集合都是它的实例
  • NodeList节点集合类,通过getElementsByName获取的节点集合都是它的实例

通过对一个实例的研究,得出这一个类的知识,认为所有的类的实例都具有这些特征,这个过程叫面向对象

所有编程语言都是面向对象开发的,类的继承、封装、多态

1).继承:子类继承父类中的属性和方法

2).多态:当前方法的多种形态(后台语言中,多态包含重载和重写)

3).重载:方法名相同,参数不一样(参数个数,参数类型);JS中不存在重载

JS中函数方法名一样的话,后边的会把前边的覆盖掉,最后只保留一个

js中类似重载的操作:根据传递单数的不同,实现不同的功能

重写:子类重写父类的方法