js 面向对象几种数据模式
一、单例模式:
把描述同一事物的属性和方法放在同一内存空间下,实现了分组的作用,防止同一属性或者方法冲突。我们把这种分组编写代码的模式叫做单例模式即普通的对象。
单例模式是项目开发中最常用的一种开发模式,可以使用单例模式实现模块开发
二、工厂模式
单例模式虽然实现了分组的作用,但是不能进行批量的生产,属于手工作业模式——》工厂模式
通俗的讲:为实现同一件事情,把相同的代码放到同一个函数,即函数的封装——》低耦合高内聚,减少代码冗余,提高代码重复使用率
三、构造函数模式
构造函数模式的目的:创建一个自定义类,并且创建这个类的一个实例
在构造函数模式中浏览器默认返回当前类的实例,如果我们手动返回一个基本数据类型的值,则当前的类还是浏览器默认返回的类的实例,如果手动返回一个引用数据类型的值则将浏览器默认返回的当前类的实例替换。
构造函数模式和工厂模式(普通函数)的区别:
1、执行的时候。工厂模式:普通函数执行。构造函数模式:var p1 = new Fn(),通过new执行之后,fn就是一个类。为了规范函数名字第一个单词大写,p1就是当前类的一个实例,不是变量。JS中的所有的类都是函数数据类型的,所有的实例都是对象数据类型的。如果Fn中不带参数则Fn后面的括号可以省略 var p1 = new Fn;
2、函数体中的代码执行的时候:
(1)相同点:都形成一个私有作用域:形参赋值—》域解释—》代码从上到下执行
(2)不同点:类在执行之前不用手动创建obj这个对象了,浏览器会默认的隐式创建一个对象数据类型的值,这个对象就是当前类的一个实例p1,接下来代码从上到下执行,以当前类的实例为主题,而this指得就是当前类的一个实例p1,然后分别给当前类赋值属性名和属性值。浏览器默认的会把创建的实例返回
instanceof:如何检测某个实例是否属于这个类?
var f1 = new Fn()
console.log(f1 instanceof Fn) // true
console.log(f1 instanceof Array) //false
对象数据类型检测方面:typeof有自己到局限性,无法检测引用数据类型的值,不能细分object下的数组、对象、正则。。。
in:检测某个属性是否属于这个对象。不管当前属性是私有属性还是共有属性,都返回true。
· attr in object
console.log("name" in f1) //true
hasOwnproperty:检测某个属性是否属于这个对象的私有属性,只能检测是不是私有属性,如果不是私有属性则返回false
function Fn(){
this.name:"sssss";
this.getX:function(){
console.log("this.name")
}
}
var f1 = new Fn()
console.log(f1.hasOwnProperty("getX")) //true
检测某一个属性为该对象的共有属性?首先保证是一个属性,并且该属性不是私有的。
console.log("name" in f1 && !f1.hasOwnProperty("name"))
isPrototypeOf:
用于检测对象是否存在于另一个对象的原型链中。如果存在,返回true
,否则返回false
。
prototypeObject.isPrototypeOf( object )
// isPrototypeOf()
函数的返回值为Boolean类型。如果object
当前的原型链中存在prototypeObject
对象,则isPrototypeOf()
方法返回true
。原型链用于在同一个对象类型的不同实例之间共享功能。如果object
不是对象,或者prototypeObject
对象不出现在object
的原型链中,则该方法返回false
。
四、原型模式
构造函数模式中虽然拥有了类和实例的概念,并且实例和实例之间是独立的 —》js中称实例识别,生活中称品牌区分。
function CreatJsPerson(name,age){
this.name:name;
this.age:age;
this.writeJs:function(){
console.log("my name is " +this.name+" i can write JS");
}
}
var p1 = new CreatJsPerson("xiaowang",26);
var p2 = new CreatJsPerson("xiaoma",18);
console.log(p1.writeJs === p2.writeJs) // false 说名writeJs 是彼此私有的,所有不相等
给予构造函数模式的原型模式解决了方法或者属性共有的问题—》把相同的属性或者方法提取成共有的属性或者方法,想让谁共有,就把谁放在当前类的原型上即可,如:
function CreatJsPerson(name,age){
this.name:name;
this.age:age;
}
creatJsPerson.prototype.writeJs = function(){
console.log("my name is " +this.name+" i can write JS");
}
var p1 = new CreatJsPerson("xiaowang",26);
var p2 = new CreatJsPerson("xiaoma",18);
console.log(p1.writeJs === p2.writeJs) // true
1、每一个函数数据类型(普通的函数和类)都天生自带一个属性:prototype(原型),并且这个属性是一个对象数据类型的值,并且在prototype上浏览器天生给它加了一个属性constructor(构造函数),constructor的属性值是当前函数或者类本身
2、每一个对象数据类型(普通的对象,实例,prototype...)也天生再带一个属性:__proto__,属性值是当前实例所属类的原型(prototype);
function Fn(){
this.x = 100;
}
Fn.prototype.getX = function(){
console.log(this.x)
}
var f1 = new Fn;
var f2 = new Fn;
console.log(Fn.prototype.constructor === Fn) // true
原型模式扩展:
批量设置原型上的共有属性和方法,常见方式有两种
1、别名法: var pro = Fn.prototype; // 把原来原型指向的地址复制给变量pro; 现在他们操作的同一个作用空间。列:
pro.getX = function(){};
pro.getY = function(){};
.....
2、重构原型对象的方式:
原理:自己开辟一个新的堆内存空间,存储我们共有的属性和方法,把浏览器原来自动开辟的空间替换掉,浏览器在空闲时将自动开辟的空间释放
function Fn(){
this.x = 100;
}
Fn.prototype = {
construct:Fn, //为了和原来的保持一致,我们需要手动增加一个construct:Fn;
a:function(){
},
b:function(){
}
}
var f = new FN;
(1)、自定义类:console.log(f.construct) 注意点: // Object 原因:因为浏览器开辟的空间被释放了,只有浏览器自动开辟的空间中才有constructor属性,我们手动开辟的空间中没有construct属性,通过__proto__找到Object上的原型中的construct属性,所有属性值为Object。为了和原来的保持一致,我们需要手动增加一个construct:Fn;
(2)、内置类:如果用这种方式给内置类增加共有的属性和方法:
Array.prototype = {
constructor:Array,
unique:function(){
console.log("sa");
}
}
会导致一下问题:
1、之前在Array类的原型上有很多方法,我们之中方式会把之前已经存在原型上的属性和方法替换了,如果我们用这种方式修改的话,浏览器会自动屏蔽我们的方法;
2、但是我们可以一个个的修改内置类中的方法,当我们以以下方式在数组的原型上增加sort方法,如果方法名字一致,则会把之前内置类上的sort方法替换了,所有我们在数组的原型上增加方法我们加一个特殊的前缀,以防止修改内置类中的属性及方法
Array.prototype.sort = function(){
console.log("OK"); //会执行OK 不会进行排序
}
var arr = [1,2,3,4,5,3,2,7]
arr.sort() ; //
五、原型链模式:
f1.hasOwnPrototype("x") x是不是f1的私有属性
在当前f1这个实例的类上并没有hasOwnPrototype()这个方法,那是如何处理的呢?
当通过对象名.属性名的方式获取属性值得时候,首先会在对象的私有方法中找该属性,如果找到了该属性则用当前的属性值;
如果找不到则通过__proto__找到当前所属类的原型(在类的原型上定义的属性和方法是当前实例共有的),如果原型上存在的话,则获取的为共有的属性值;
如果原型上也没有,则通过原型上的属性__proto__ 继续向上查找,一直找到Object.prototy为止
这种查找的机制称之为原型链模式。
f1.hasOwnPrototype —》 f1.__proto__.__proto__.hasOwnPrototype
在IE浏览器中原型链模式也是同样的原理,但是IE浏览器中为防止通过__proto__修改公有的属性或者方法,禁止使用__proto__。为了兼容所有浏览器我们可以使用Fn.prototype.属性名进行修改。
链式写法:执行完数组的方法可以接着执行另外的一个方法。
可枚举属性和不可枚举属性:prototypeIsEnumerable
for...in... 循环在遍历的时候会默认的把私有的属性和在他内置类原型上定义的共有属性和方法遍历到,但是我们一般情况下只遍历私有的属性即可:
Object.prototype.fn = function(){console.log("ok");}
var obj = {name:"ywx354980",age:25};
for(var key in obj){
// 用以下的方式判断是否可枚举,或者是不是私有的属性
if(obj.protopertyIsEnumerable(key)){console.log(key)} 或者 if(obj.hasOwnPrototype(key){console.log(key)})
}