对象数据类型的作用:
这种分组的编写代码模式叫做
单例模式
;在单例模式中把对象名叫做命名空间
。
单例模式是一种项目开发中经常使用的模式,可以使用单例模式进行模块化开发。例:
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中叫做实例识别
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
new
运算符。 回顾数组的创建方式:
//字面量创建方式
var arr = [];
//实例创建方式--构造函数执行方式
var arr = new Array();
不管这两种哪种方式,arr都是Array类的实例
JS中所有的类都是函数数据类型的,通过new执行变成一个类,但是他本身也是一个普通函数
JS中所有的实例是对象数据类型的
相同点:都是形成一个私有作用域,然后形参赋值,然后预解析,代码从上至下执行(类和普通函数一样,类也具有普通函数的一面);
不同点:构造函数在代码执行之前,不用手动创建对象,浏览器会默认创建一个对象数据类型的值(这个对象就是当前类的实例,以当前实例为主体,将属性名和属性值赋给实例,this代表当前实例),浏览器会默认将创建的实例返回
this在构造函数中,就是当前类的实例;这也是this的第四种情况
虽然各个实例p1,p2都是CreatePerson的实例,都拥有write方法,但是不同实例之间的方法是不一样的;实例和实例之间是单独的个体,所以私有属性之间不相等
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} 引用数据类型,会替换原有返回值
}
检测某个实例是否属于这个类-->instanceof
var f1 = new Fn();
console.log(f1 instanceof Fn); //true
console.log(f1 instanceof Array); //false
console.log(f1 instanceof Obejct); //true-->所有实例都是对象数据类型的,而每一个对象数据类型都是Object内置类的实例
"in"检测某个属性是否属于这个对象--> attr in object,不管是公有属性还是私有属性,只要存在则返回true
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的私有属性上并没有这个方法,如何处理的呢?
1).通过对象名.属性名
的方式获取属性值的时候,首先在对象的私有属性上进行查找,如果私有存在这个属性,则获取的是私有的属性值;
2).如果私有的没有这个属性,则通过__proto__找到所属类的原型,原型上定义的属性和方法都是当前实例的公有属性和方法,原型上存在的话,获取的是公有的属性值;
3).如果原型上也没有,则通过原型上的__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
通过对一个实例的研究,得出这一个类的知识,认为所有的类的实例都具有这些特征,这个过程叫面向对象
所有编程语言都是面向对象开发的,类的继承、封装、多态
1).继承:子类继承父类中的属性和方法
2).多态:当前方法的多种形态(后台语言中,多态包含重载和重写)
3).重载:方法名相同,参数不一样(参数个数,参数类型);JS中不存在重载
JS中函数方法名一样的话,后边的会把前边的覆盖掉,最后只保留一个
js中类似重载的操作:根据传递单数的不同,实现不同的功能
重写:子类重写父类的方法