对象数据类型的作用:把描述同一件事物的属性和方法放在同一段堆内存中,起到分组的作用,防止冲突;这样不同事物间即使属性名一样也不会发生冲突.这种分组的编写代码模式叫做单例模式
。
在单例模式中把对象名叫做命名空间
。
单例模式是一种项目开发中经常使用的模式,可以使用单例模式进行模块化开发。
模块化开发:对于一个较大的项目,需要多人协同开发,根据项目需求划分成几个功能板块,每个人负责一部分,同时开发,最后把每个人的代码进行合并
var person = {
name: "Amy",
age: 12,
write: function(){
alert(this.name,this.age);
}
};
person.write();
单例模式虽然解决了分组的问题,但是不能实现批量的生产,属于手工作业模式。
工厂模式把实现同一件事情的代码放到一个函数中,如果以后想实现这个功能,就不需要重新编写代码了,只需执行当前函数就可以了,这就叫做“函数的封装”。
函数的封装,是为了低耦合高内聚
,减少页面中的冗余代码,提高代码的重复利用率
function factory(name,age){
var obj = {};
obj.name = name;
obj.age = age;
obj.write = function(){
alert(this.name+"can write JS!");
}
return obj;
}
var p1 = factory("Amy",12);
var p2 = factory("Bob",14);
p1.write();
p2.write();
构造函数模式的目的就是为了创建自定义类
,并且创建这个类的实例
构造函数模式拥有了类和实例的概念,并且实例和实例之间是相互独立的,在JS中叫做实例识别
//字面量创建方式
var arr = [];
//实例创建方式--构造函数执行方式
var arr = new Array();
不管这两种哪种方式,arr都是Array类的实例函数代码执行的时候,
相同点:都是形成一个私有作用域,然后形参赋值,然后预解析,代码从上至下执行(类和普通函数一样,类也具有普通函数的一面);
不同点:构造函数在代码执行之前,不用手动创建对象,浏览器会默认创建一个对象数据类型的值(这个对象就是当前类的实例,以当前实例为主体,将属性名和属性值赋给实例,this代表当前实例),浏览器会默认将创建的实例返回
function CreatePerson(name,age){
//浏览器默认创建的对象就是实例p1
this.name = name;
this.age = age;
this.write = function(){
console.log(this.name,this.age);
}
//浏览器默认将创建的实例返回
}
var p1 = new CreatePerson('Amy',18);
var p2 = new CreatePerson('Bob',18);
var p3 = CreatePerson("Cindy",20);
//这样就是普通函数执行方式,并非构造函数执行,则函数中this指window(方法名前没有点),p3没有返回值,则为undefined
this在构造函数中,是当前类的实例(this的第 4 种情况)
console.log(p1.write === p2.write); //false
在构造函数中,new Fn()执行,如果Fn()中不需要传递参数的话,后边的()可以省略
function Fn(){
this.x = 100;
this.getX = function(){
console.log(this.x);
}
}
var f = new Fn;
在类中出现的this.xxx=xxx中,this都是当前类的实例,而某一个属性值(方法),方法中的this则需要看方法前面的点才能知道this是谁
var ss = f.getX;
ss(); //undefined-->window下没有x属性
function Fn(){
var num = 10;
//this.num
this.x = 100;
this.getX = function(){
console.log(this.x);
}
}
var f1 = new Fn();
console.log(f1.num); //undefined
var num只是当前形成私有作用域中的一个私有变量,只有this才能给当前实例增加属性和方法function Fn(){
this.x = 100;
this.getX = function(){
console.log(this.x);
}
//浏览器默认返回实例
//return 100; 基本数据类型
//return {"name":100} 引用数据类型,会替换原有返回值
}
var f1 = new Fn();
console.log(f1 instanceof Fn); //true
console.log(f1 instanceof Array); //false
console.log(f1 instanceof Obejct); //true-->所有实例都是对象数据类型的,而每一个对象数据类型都是Object内置类的实例
对于检测数据类型,typeof有自己的局限性,不能细分Obejct下的对象,例如数组,正则console.log('getX' in f1); //若返回true,则证明getX是f1的私有方法
console.log(f1.hasOwnProperty(getX)); //true-->说明getX是f1的私有属性
检测某一属性为该对象的公有属性function hasPubProperty(obj,attr){
return (attr in obj) && !obj.hasOwnProperty(attr);
}
object1.isPrototypeOf(object2);
类
的原型
基于构造函数的原型模式,解决方法或属性共有的问题,把实例之间相同的属性和方法提取为共有的
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 _。
f1通过_ proto _可以向上级查找,不管找到多少级,最后总能找到Object
console.log(f1 instanceof Object); //true
f1.hasOwnProperty(x) ==> hasOwnProperty是f1的属性。
但是f1的私有属性上并没有这个方法,如何处理的呢?
通过对象名.属性名
的方式获取属性值的时候,首先在对象的私有属性上进行查找,如果私有存在这个属性,则获取的是私有的属性值;如果私有的没有这个属性,则通过proto找到所属类的原型,原型上定义的属性和方法都是当前实例的公有属性和方法,原型上存在的话,获取的是公有的属性值,如果原型上也没有,则通过原型上的proto继续向上查找,一直找到Object的prototype为止
这种查找模式叫原型链模式
f1.sum = function(){} //修改私有的
f1.__proto__.sum = function(){} //修改公有的 //IE禁用
Fn.prototype.sum = function(){} //修改公有的
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,失去排序功能
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
执行完成一个方法后继续执行下一个方法
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该值指示指定属性是否为对象的一部分以及该属性是否是可枚举的。
function A(){
this.x = 100;
}
A.prototype.getX = function(){
console.log(this.x);
}
function B(){
this.y = 200;
}
B.prototype = new A;
对象:js中万物皆对象,是泛指
类:对象的具体细分
实例:某一个类别中具体的一个事物
对象是一个抽象的概念
内置类:Array,Number,String,Boolean,Null,Undefined,Object,RegExp,Date,Function
HTMLCollection元素集合类,通过getElementByTagName获取的元素集合都是它的实例
NodeList节点集合类,通过getElementsByName获取的节点集合都是它的实例
通过对一个实例的研究,得出这一个类的知识,认为所有的类的实例都具有这些特征,这个过程叫面向对象
所有编程语言都是面向对象开发的,类的继承、封装、多态
1).继承:子类继承父类中的属性和方法
2).多态:当前方法的多种形态(后台语言中,多态包含重载和重写)
3).重载:方法名相同,参数不一样(参数个数,参数类型);JS中不存在重载
JS中函数方法名一样的话,后边的会把前边的覆盖掉,最后只保留一个
js中类似重载的操作:根据传递单数的不同,实现不同的功能
重写:子类重写父类的方法