面向对象
1、工厂模式
function createObject(name,age){ let obj = new Object(); this.name = name; this.age = age; return obj; } let objA = createObject(‘Tom‘,24); let objB = createObject(‘Jane‘,23); typeof ObjA; //Object typeof ObjB; //Object ObjA instanceof Object; ObjA instanceof Object; //方法缺点:不能区分对象实例,所有对象实例都由Object实例化
2、构造函数模式
function Box(name,age){ this.name = name; this.age = age; this.run = function (){ return this.name + this.age; } } function Desk(name,age){ this.name = name; this.age = age; this.run = function (){ return this.name + this.age; } } let box = new Box(‘Tom‘,24); let desk = new Desk(‘Jane‘,23); box instanceof Box; //true box instanceof Desk; //false desk instanceof Desk; //true // //知识延伸,对象冒充 let o = new Object(); Box.call(o,‘Ha‘,25); o.name;
构造函数方式和原型方式变量存储的方式
3、原型
我们创建的每一个函数都有一个prototype(原型属性),这个属性是一个对象,它的用途是包含可以由特定类型的所有实例共享的属性和方法。逻辑上可以这么理解:prototype通过调用构造函数而创建的那个对象的原型对象。使用原型的好处可以让所有对象实例共享它所包含的属性和方法。也就是说,不必在构造函数中定义对象信息,而是可以直接将这些信息添加到原型中。(我自己的理解,通过构造函数创建的对象会自动创建一个原型对象prototype。)
function Box(){}
Box.prototype.name = ‘Lee‘; //原型属性
Box.prototype.age = 100;
Box.prototype.run = function () { //原型方法
return this.name + this.age + ‘运行中‘;
}
证明:原型对象内的属性和方法都被实例对象共用
let box1 = new Box();
let box2 = new Box();
Object.is(box1.run,box2.run); //true Object.is(),判断两个变量是否相等 (等于box1.run === box2.run);
说明box1和box2中的run方法都指向同一个引用地址。
在原型模式声明中,多了两个属性,这两个属性都是创建对象时自动生成的。__proto__属性是实例指向原型对象的一个指针,它的作用就是指向构造函数的原型属性constructor。通过这两个属性,就可以访问到原型里的属性和方法了。
//判断一个实例对象是否指向了原型对象,只要实例化了,会自动指向的
Box.prototype.isPrototypeOf(box1); //true 接着上面的代码
let obj = new Object(); //
Box.prototype.isPrototypeOf(obj);
如果实例对象中有name属性,原型对象中也有name属性,通过 . 访问name会打印处实例对象中的name属性。
function Box () {}
Box.prototype.name = ‘guaguaerhao‘;
let box1 = new Box();
box1.name = ‘呱呱二号‘;
console.log(box1.name); //呱呱二号
原型模式的执行流程:
1、先查找实例对象中是否存在属性,如果存在,则返回
2、如果实例对象中没有该属性,则在原型对象中查找,如果存在,则返回
判断实例中是否存在属性
box1.hasOwnProperty(‘name‘); //true
(name in box1) //不管是原型中有name属性还是实例中有name属性,都会返回true
判断只有原型中是否存在属性
function hasPrototypeProperty(object,property){
return !object.hasOwnProperty(property) && (property in object);
}
4、字面量原型模式
function Box () {}
Box.prototype = {
constructor: Box, //将原型对象的constructor强制指向回Box
name: ‘guaguaerhao‘,
age: 24,
run: function(){
return this.name + this.age;
}
}
ps:原型中最大的缺点就是它的优点,共享。原型模式创建的对象,省略了构造函数传参,带来的缺点就是初始化的值都是一样的。
5、构造函数加原型模式(构造函数和方法分开,没有一种封装的感觉,感觉很零碎)
function Box(name,age){ //不共享的使用构造函数
this.name = name;
this.age = age;
this.family = [‘爸爸‘,‘妈妈‘,‘哥哥‘,‘我‘];
}
Box.prototype = { //共享的使用原型
constructor: Box,
run: function () {
return this.name + this.age;
}
}
6、动态原型模式
function Box(name,age){
this.name = name;
this.age = age;
//可是,实例化对象的时候,每次都会创建run()方法,浪费内存,因为他在每一个对象中都是一样的功能,没有必要,每次实例化都创建,实际上只需要创建一次。
Box.prototype.run = function () {
return this.name + this.age;
}
}
所以
function Box(name,age){
this.name = name;
this.name = age;
if(typeof this.run !== ‘function‘){ //只会实例化一次
Box.prototype.run = function () {
return this.name + this.age;
}
}
}
7、寄生构造函数(工厂模式+构造函数)
function Box(name,age){
let obj = new Object();
obj.name = name;
obj.age = age;
obj.run = function (){
return this.name + this.age;
}
return obj;
}
let obj = new Box(‘ha‘,24);
obj.run();
7、寄生构造函数(工厂模式+构造函数)
function Box(name,age){
let obj = new Object();
obj.name = name;
obj.age = age;
obj.run = function (){
return this.name + this.age;
}
return obj;
}
let obj = Box(‘ha‘,24);
obj.run();
继承
1、继承是面向对象中比较核心的概念,ECMAScript只支持继承:
function Parent(){
this.name = ‘p‘;
}
function Child(){
this.name = ‘c‘;
}
//通过原型链继承,父类实例化后的对象实例,赋值给子类型的原型属性
//new Parent()会将构造函数里的信息和原型的信息都交给Child
Child.prototype = new Parent();
2、对象冒充模式
为了解决引用共享和超类型无法传参的问题,采用一种叫借用构造函数的技术,或者成为对象冒充
function Parent(name,age){
this.name = name;
this.age = age;
}
//Parent.prototype.family = ‘家庭‘; //child实例无法访问
function Child(name,age){
Parent.call(this,name,age); //对象冒充,给父类传递参数,对象冒充只能继承构造函数中的属性,原型中的无法访问
}
let child = new Child(‘haha‘,24);
child.name;
child.family; //undefined
3、原型链加借用构造函数,组合模式
function Parent(age){
this.age = age;
}
Parent.prototype.run = function () { //解决了方法共享
return this.age;
}
function Child(age){
Parent.call(this,age);
}
Child.prototype = new Parent();