1.原型链模式
1.1 原型链构成
每一个函数数据类型(普通函数、类)都有一个天生自带的属性:prototype(原型),并且这个属性是一个对象数据类型的值
在prototype上,浏览器自动给它增加了一个constructor(构造函数)属性,属性是当前函数或类本身
每一个对象数据类型(普通对象,实例,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
先确定this的指向
把this替换成对应的代码
按照原型链查找机制查找结果
2.3 链式写法
执行完成一个方法后继续执行下一个方法
arr.sort(function(a,b){return a-b}).reverse().pop();
原理:arr执行sort方法->因为sort是Array.prototype上的公有方法,arr是Array的一个实例->数组才能使用Array原型上定义的属性和方法
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;
子类B继承父类A的所有属性和方法(私有和公有的)的方式,A的实例既有私有属性方法,又有公有属性和方法,所以用A的实例作为B的原型地址
继承特点:把父类的私有的和公有的都继承到子类原型上,都是子类公有的
核心:原型继承并不是把父类的属性和方法克隆一份给子类,而是让父类和子类之间增加了原型链的链接,以后子类的实例想要用父类的方法,需要一级级的向上查找来使用
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中类似重载的操作:根据传递单数的不同,实现不同的功能
重写:子类重写父类的方法