JS面向对象的理解
- 1.理解对象
- 1.1.new 操作符 + Object 创建对象
- 1.2.字面式创建对象
- 2.创建对象
- 2.1.工厂模式
- 2.2.构造函数模式
- 2.3.原型模式
- 2.3.1.原型模式
- 2.3.2.理解原型对象
- 2.3.3.原型与in操作符
- 2.3.4.更简单的原型语法
- 2.3.5.原型的动态性
- 2.3.6.原型对象的原型
- 2.3.7.原型对象的问题
- 2.4.组合使用构造函数模式和原型模式
- 2.5.动态原型模式
- 2.6.寄生构造函数模式
- 2.7.稳妥构造函数模式
- 3.继承
- 3.1.原型链
- 3.1.1.原型链
- 3.1.2.别忘记默认的原型
- 3.1.3.确定原型和实例的关系
- 3.1.4.谨慎的定义方法
- 3.1.5.原型链的问题
- 3.2.借用构造函数
- 3.3.组合继承
- 3.4.原型式继承
- 3.5.寄生式继承
- 3.6.寄生组合式继承
- 4.学习地址
1.理解对象
1.1.new 操作符 + Object 创建对象
var person = new Object();
person.name = 'lvxin';
person.age = 25,
person.job = 'UI development';
person.sayName = function() {console.log(this); // Object--> {name: 'lvxin', age: 25, job: 'UI development', sayName:function}console.log(this == person); //trueconsole.log(this.name); //lvxin
}
console.log(person.sayName());
1.2.字面式创建对象
var person = {name: 'lvxin',age: 25,job: 'UI Development',sayName: function() {console.log(this); // Object--> {name: 'lvxin', age: 25, job: 'UI development', sayName:function}console.log(this == person); // trueconsole.log(this.name); //lvxin }
}
console.log(person.sayName());
可扩展this指向问题。
apply和call的用法和区别
以上两种方法在使用同一接口创建多个对象时,会产生大量重复代码,为了解决此问题,工厂模式被开发。
2.创建对象
2.1.工厂模式
- 无法确定对象的类型,每次都是调用Object
- 创建多个对象之间没有关联
function createPerson(name,age,job) {var o = new Object();o.name = name;o.age = age;o.job = job;o.sayName = function() {console.log(this.name);}return o;
}
var person1 = createPerson('lvxin',18,'web');
var person2 = createPerson('xuyuqiu',20,'web');console.log(typeof person1);//Object 只能知道是Object类型,不知道是其他具体得Person
console.log(typeof person2);//Object
console.log(person1 === person2);//false
console.log(person1.sayName === person2.sayName)//false
console.log(person1 instanceof Object); //true
工厂模式解决了重复实例化多个对象的问题,但没有解决对象识别的问题(但是工厂模式却无从识别对象的类型,因为全部都是Object,不像Date、Array等,本例中,得到的都是o对象,对象的类型都是Object,因此出现了构造函数模式)。
2.2.构造函数模式
对比工厂模式有以下不同之处:
- 没有显式地创建对象
- 直接将属性和方法赋给了 this 对象
- 没有 return 语句
以此方法调用构造函数步骤 {
1. 创建一个新对象
2. 将构造函数的作用域赋给新对象(将this指向这个新对象)
3. 执行构造函数代码(为这个新对象添加属性)
4. 返回新对象
}
function Person(name,age,job) {this.name = name;this.age = age;this.job = job;this.sayName = function() {console.log(this.name);}
}
var person1 = new Person('lvxin',18,'web');
var person2 = new Person('xuyuqiu',20,'web');console.log(person1.constructor == Person);//true
console.log(person2.constructor == Person);//true
// 创建所有的对象都是Object实例,因为对象均继承Object
console.log(person1 instanceof Person);//true
console.log(person1 instanceof Object);//true
console.log(person2 instanceof Person);//true
console.log(person2 instanceof Object);//true
问题: 多个实例重复创建方法,无法共享,多个实例都有sayName方法,但均不是同一个Function的实例。
- 每一个实例都要重新创建一遍,person1和person2都有sayName的方法,但是是两个Function实例。
- sayName方法定义到外部来,就是全局的,那么person1和person2就是同一个方法了。
解决方法:
function Person(name,age,job) {this.name = name;this.age = age;this.job = job;//this.sayName = function() {// console.log(this.name);//};//this.sayName = new Function("console.log(this.name)");this.sayName = sayName;
}
function sayName() {console.log(this.name);
}
var person1 = new Person('lvxin',18,'web');
var person2 = new Person('xuyuqiu',20,'web');
console.log(person1.sayName == person2.sayName);//true
2.3.原型模式
2.3.1.原型模式
- 每个函数(构造函数)都有一个prototype(原型)属性
- prototype(原型)属性是一个指针,指向一个对象
- prototype(原型)属性的这个对象包括所有特定类型(自己定义的构造函数)的所有实例(new自己定定义的函数)的属性和方法。
//空的构造函数
function Person () {}
//所有Person的实例共享这些属性和方法
Person.prototype.age = 18;
Person.prototype.name = "lvxin";
Person.prototype.jos = "web";
Person.prototype.sayName = function() {console.log(this.name);
}
var person1 = new Person();
var person2 = new Person();
console.log(person1.sayName());//lvxin
console.log(person2.sayName());//lvxin
console.log(person1.sayName() == person2.sayName());
2.3.2.理解原型对象
- 所有原型都会自动获得一个constructor(构造函数属性),Person.prototype.constructor.
- Person(构造函数)的prototype属性指向Person Prototype(Person的原型)
- Person Prototype有构造函数(constructor)指向Person。
- person1有prototype属性指向Person Prototype,但没有自己的构造函数。
- isPrototypeOf(),返回true/false,判断实例是否拥有某个原型
- Object.getPrototypeOf(),返回定义原型的字符名称(Preson.prototpe),得到实例的原型名称
function Person() {}Person.prototype.name = 'lvxin';Person.prototype.age = 18;Person.prototype.job = 'web';Person.prototype.sayName = function() {console.log(this.name);}
var person1 = new Person();
var person2 = new Person();
console.log(Person.prototype.isPrototypeOf(person1));//true
console.log(Person.prototype.isPrototypeOf(person2));//trueconsole.log(Object.getPrototypeOf(person1) == Person.prototype);//true
console.log(Object.getPrototypeOf(person1).name);
- 寻找属性和方法的过程:从对象实例本身开始,再看原型对象,再看object对象
- 不能用对象实例重写原型的值,实例和原型属性同名,实例会屏蔽原型的值。
- 可以用 delete删除实例的属性
- hasOwnPrototype(),返回true/false,判断是否有自己的原型属性,即实例有自己的属性。
person1.name = "xuyuqiu";
console.log(person1.name);//xuyuqiu 实例值
console.log(person2.name);//lvxin 原型值
delete person1.name;
console.log(person1.name);//lvxin 原型值person1.name = "duanyujie";
console.log(person1.hasOwnPrototype(name));//true;
console.log(person2.hasOwnPrototype(name));//false;
delete person1.name;
console.log(person1.hasOwnPrototype(name));//false;
2.3.3.原型与in操作符
/**
* 1.in用法:判断属性是否存在* 2.in 无论实例自己的属性还是实例用原型的属性,都返回true* 3.通过 in返回true和hasOwnPrototype()返回false,判断是原型上的属性* 4.hasPrototypeProperty(实例,属性),返回true/false,判断是否是原型上的属性。*/function Person() {}Person.prototype.name = 'lvxin';Person.prototype.age = 18;Person.prototype.job = 'web';Person.prototype.sayName = function() {console.log(this.name);}
var person1 = new Person();
var person2 = new Person();person1.name = "xuyuqiu";
console.log('name' in person1);//true;
console.log('name' in person2);//true;console.log(person2.hasOwnProperty(name));//false 判断person2.name 来自原型//console.log(hasPrototypeProperty(person1,'name'));//false;
//console.log(hasPrototypeProperty(person2,'name'));//true;/*** 1.Object.keys(对象),返回数组,找到对象的可以枚举的属性名* 2.Object.getOwnPropertyNames(对象),返回数组,找到对象的可以枚举或者不可枚举的属性名*/
var keysArr = Object.keys(Person.prototype);//[name,age,job,sayName];
console.log(keysArr);person1.name = "duanyujie";
person1.age = 20;
//实例没有的自生属性
var person1KeysArr = Object.keys(person1);//[name,age]
console.log(person1KeysArr);//constructor 不可枚举
var keys = Object.getOwnPropertyNames(Person.prototype);//[constructor,name,age,job,sayName];
console.log(keys);
2.3.4.更简单的原型语法
/*** `1.创建一个函数同时会创建一个prototype对象* 2.赋值整个对象是重构一个对象,一个一个赋值是修改本来的值*/
function Person() {}
Person.prototype = {name:'lvxin',age:18,job:"web",sayName:function() {console.log(this.name);}
}
var person1 = new Person();
console.log(person1 instanceof Person);//true;
console.log(person1 instanceof Object);//true;
console.log(person1.constructor instanceof Person);//false;
console.log(person1.constructor instanceof Object);//true;console.log(Object.getOwnPropertyDescriptor(Person.prototype,"constructor"));//undefined/*** 1.修改实例的constructor指向有两种方法* 1-1:直接写个属性constructor:Person* 1-2:Object.defineProperty(对象,属性名,修改的列值)*/
function Person2() {}
Person2.prototype = {constructor:Person2,name:'lvxin',age:18,job:"web",sayName:function() {console.log(this.name);}
}
var person2 = new Person2();
console.log(person2 instanceof Person2);//true;
console.log(person2 instanceof Object);//true;
console.log(person2.constructor instanceof Person2);//false
console.log(person2.constructor instanceof Object);//true;console.log(Object.getOwnPropertyDescriptor(Person2.prototype,"constructor"));//enumerable:true
Object.defineProperty(Person2.prototype,"constructor",{enumerable:false,value:Person2
});
console.log(person2.constructor instanceof Person2);//false;
console.log(Object.getOwnPropertyDescriptor(Person2.prototype,"constructor"));//enumerable:false
console.log(person2.constructor instanceof Person2);//f alse;
2.3.5.原型的动态性
/*** 1.动态性:先实例化,之后还可以加属性和方法,仍然可以访问到* 2.把原型修改成另一个对象,切断了现有的原型和之前已经存在的实例对象。* 3.创建一个构造函数,即有一个构造属性*/
function Person() {}
var person = new Person();
Person.prototype.sayHi = function() {console.log("say Hi");
}
person.sayHi();//say Hi
console.log(person.sayHi());//undefined 无返回值 function Person2() {}
Person2.prototype = {name:'lvxin',age:18,job:"web",sayName:function() {console.log(this.name);}
}
var person2 = new Person();
person2.sayName();//error person2实例和新的原型Person2.prototype没有关系,person2实例和原来的原型有关
2.3.6.原型对象的原型
/*** 1.所有原生引用类型(object,Array,String)为原型对象的原型* */
console.log(typeof Array.prototype.sort);//function
console.log(typeof String.prototype.substring);//function//判断字符是否存在
String.prototype.startsWith = function (text) {console.log(this.indexOf(text));//0return this.indexOf(text) == 0;
};var msg = "Hello world!";
console.log(msg.startsWith('Hello'));//true
2.3.7.原型对象的问题
/*** 1.实例取得相同的属性值* 2.实例1修改了值,实例2值也会变*/function Person () {}
Person.prototype = {constructor:Person,name:'lvxin',age:18,job:'web',friends:['xuyuqiu','duanyujie'],sayName:function() {console.log(this.name);}
};
var person1 = new Person();
var person2 = new Person();person1.friends.push('lizhiying');//
console.log(person1.friends);//['xuyuqiu','duanyujie','lizhiying']
console.log(person2.friends);//['xuyuqiu','duanyujie','lizhiying']console.log(person1.friends === person2.friends);//true
2.4.组合使用构造函数模式和原型模式
function Person (name,age,job) {this.name = name;this.age = age;this.job = job;this.friends = ['xuyuqiu','duanyujie'];
}
Person.prototype = {constructor:Person,sayName:function() {console.log(this.name);}
}
//指向新的原型
var person1 = new Person('lvxin',18,'web');
var person2 = new Person('zhengling',19,'test');person1.friends.push('lizhiying');//
console.log(person1.friends);//['xuyuqiu','duanyujie','lizhiying']
console.log(person2.friends);//['xuyuqiu','duanyujie']console.log(person1.friends === person2.friends);//false
console.log(person1.sayName === person2.sayName);//true
2.5.动态原型模式
/*** 判断是否初始化构造函数*/
function Person(name,age,job) {this.name = name;this.age = age;this.job = job;//初次调用构造函数时if(typeof this.sayName != 'function') {//注意,不能使用字变量方式重写Person.prototype.sayName = function() {console.log(this.name);}}
}
var friend = new Person('xuyuqiu',18,'web');
friend.sayName();
2.6.寄生构造函数模式
/*** 1.概念:创建一个函数,封装对象代码,返回新建的代码* 2.不建议用这个方法*/
function Person (name,age,job) {var o = new Object();//var value = new Array();o.name = name;o.age = age;o.job = job;o.sayName = function() {console.log(this.name);}return o;
}
var friend = new Person('xuyuqiu',19,'web');//xuyuqiu
friend.sayName();//xuyuqiu
2.7.稳妥构造函数模式
/*** 1.没有公共的属性,方法也不引用this对象,返回对象* 2特点:* 方法不用this引用* 实例不用new来创造* 3.安全性高,外部只可以通过调用内部函数,来访问属性的值*/
function Person(name,age,job) {//没有属性var o = new Object();o.sayName = function() {//不用thisconsole.log(name);}return o;
}
//不用new
var friend = Person('lvxin',18,'web');friend.sayName();
3.继承
3.1.原型链
3.1.1.原型链
/*** 1.定义:每个构造函数都有个prototype属性,指向原型对象,* 原型对象constructor属性,指向构造函数,实例指向原型对象,具有所有原型对象的方法和属性。* 2.原型链:(原型和实例之间的一个链条),第二个构造函数的原型对象为第一个构造函数的实例。*/
//定义父的构造函数(超类型函数)
function SuperType() {this.prototype = true;
}
SuperType.prototype.getSuperValue = function() {//console.log(this.prototype);return this.prototype;
};
//定义子类的构造函数(子类型函数)
function SubType() {this.subprototype = false;}//继承
SubType.prototype = new SuperType();SubType.prototype.getSubValue = function() {//console.log(this.subprototype);return this.subprototype;
};var instance = new SubType();
console.log(instance.prototype);//flase;
console.log(instance.getSuperValue());//true;
console.log(instance.getSubValue());//flase;
console.log(instance.toString());
3.1.2.别忘记默认的原型
//定义父的构造函数(超类型函数)
function SuperType() {this.prototype = true;
}
SuperType.prototype.getSuperValue = function() {//console.log(this.prototype);return this.prototype;
};
//定义子类的构造函数(子类型函数)
function SubType() {this.subprototype = false;}//继承
SubType.prototype = new SuperType();SubType.prototype.getSubValue = function() {//console.log(this.subprototype);return this.subprototype;
};var instance = new SubType();
console.log(instance.prototype);//flase;
console.log(instance.getSuperValue());//true;
console.log(instance.getSubValue());//flase;
/*** 所有函数的默认原型对象是Object,所以实例都会* toString();* hasOwnPrototype();* isPrototypeOf();* toLocalString();*/
console.log(instance.toString());//输出 是一个对象,SubType和SuperTpye都没有toString()方法
3.1.3.确定原型和实例的关系
//定义父的构造函数(超类型函数)
function SuperType() {this.prototype = true;
}
SuperType.prototype.getSuperValue = function() {//console.log(this.prototype);return this.prototype;
};
//定义子类的构造函数(子类型函数)
function SubType() {this.subprototype = false;}//继承
SubType.prototype = new SuperType();SubType.prototype.getSubValue = function() {//console.log(this.subprototype);return this.subprototype;
};var instance = new SubType();console.log(instance instanceof Object);//true;
console.log(instance instanceof SuperType);//true;
console.log(instance instanceof SubType);//true;
/*** 原型.isPrototypeOf(实例):判断实例是否是原型上的*/
console.log(Object.prototype.isPrototypeOf(instance));//true
console.log(SuperType.prototype.isPrototypeOf(instance));//true
console.log(SubType.prototype.isPrototypeOf(instance));//true
3.1.4.谨慎的定义方法
/*** 1.先继承父的属性和方法,再去写子类的属性和方法,可以重写父类的方法和属性。* 2.通过原型链继承,不能用字变量的方式创建原型。*/
function SuperType() {this.prototype = true;
};
SuperType.prototype.getSuperValue = function() {return this.prototype;
};
function SubType() {this.subprototype = false;
}
//继承
SubType.prototype = new SuperType();
//重写父的方法
SubType.prototype.getSuperValue = function() {return false;
}
//增加新方法
SubType.prototype.getSubValue = function() {return this.subprototype;
}
var instance = new SubType();
console.log(instance.getSuperValue());//false;先找子的同名函数/*** 1.先继承父的属性和方法,再去写子类的属性和方法,可以重写父类的方法和属性。* 2.通过原型链继承,不能用字变量的方式创建原型。*/function SuperType1() {this.prototype = true;
};
SuperType1.prototype.getSuperValue1 = function() {return this.prototype;
};
function SubType1() {this.subprototype = false;
}
//继承
SubType1.prototype = new SuperType1();
//用字变量的方式
SubType1.prototype = {getSubValue:function() {return this.subprototype;},
};
var instance = new SubType1();
console.log(instance.getSuperValue1());//is no has function 字变量会使继承无效,子中没有父的方法。
3.1.5.原型链的问题
/*** 1.所有方法和属性都全部共享,一改全改值*/
function SuperType() {this.color = ['blue','yellow','red'];
}
function SubType() {}
SubType.prototype = new SuperType();
var instance1 = new SubType();
var instance2 = new SubType();
console.log(instance1.color);//['blue','yellow','red','white']
console.log(instance2.color);//['blue','yellow','red','white']
instance1.color.push('white');
console.log(instance1.color);//['blue','yellow','red','white']
console.log(instance2.color);//['blue','yellow','red','white']
3.2.借用构造函数
/*** 1.在子类构造函数中调用父类的构造函数* 2.可以传递参数给父类* 3.子类新的方法写在调用父类的之后。*/
function SuperType() {this.colors = ['red','bule','yello'];
}
function SubType() {//继承SuperType.call(this);this.age = 29;
}
var instance1 = new SubType();
var instance2 = new SubType();
instance1.colors.push('white');
console.log(instance1.colors);//['blue','yellow','red','white']
console.log(instance1.age);//29
console.log(instance2.colors);//['blue','yellow','red']
console.log(instance2.age);//29function SuperType1(name) {this.colors = ['red','bule','yello'];this.name = name;
}
function SubType1() {//继承SuperType1.call(this,'lvxin');this.age = 29;
}
var instance11 = new SubType1();
var instance21 = new SubType1();console.log(instance11.name);//lvxin
console.log(instance21.name);//lxinconsole.log(instance11.age);//29
console.log(instance21.age);//29console.log(instance11.colors);//['red','bule','yello','white']
instance11.colors.push('white');
console.log(instance21.colors);//['red','bule','yello']
3.3.组合继承
/*** 1.原型链和借用构造来实现对实例属性的继承*/
function SuperType(name) {this.colors = ['red','blue','green'];this.name = name;
}
SuperType.prototype.sayName = function() {return this.name;
};
function SubType(name,age) {SuperType.call(this,name);this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {return this.age;
}var instance1 = new SubType('lvxin',18);
instance1.colors.push('white'); //['red','blue','green','white']
console.log(instance1.colors);
console.log(instance1.sayName());//lvxin
console.log(instance1.sayAge());//18
var instance2 = new SubType('xuyuqiu',19);
console.log(instance2.colors);//['red','blue','green']
console.log(instance2.sayName());//xuyuqiu
console.log(instance2.sayAge());//19
3.4.原型式继承
/*** 1.值都共享* 2.Object.create(对象)方法也可以用来创建一个实例。*/console.log("-------------------6.3.4原型式继承-----------------------");function object(o) {function F() {}F.prototype = o;return new F();}var person = {name:'lvxin',friends:['duanyujie','xuyuqiu']};//var anotherPerson = new object(person);var anotherPerson = Object.create(person);anotherPerson.name = 'lizhiying';anotherPerson.friends.push('liufan');//var yetAnotherPerson = new object(person);var yetAnotherPerson = Object.create(person);yetAnotherPerson.name = 'xufengfeng';yetAnotherPerson.friends.push('zhengling');console.log(person.friends);//['duanyujie','xuyuqiu','liufan','zhenglin']console.log(person.name);//xufengfeng
3.5.寄生式继承
function object(o) {function F() {}F.prototype = o;return new F();}function createAnother(original) {var clone = object(original);clone.sayHi = function() {return 'hi';}return clone;}var person = {name:'lvxin',friends:['duanyujie','xuyuqiu']};var anotherPerson = createAnother(person);console.log(anotherPerson.sayHi());//hi
3.6.寄生组合式继承
function object(o) {function F() {}F.prototype = o;return new F();
}function inheritPrototype(subType,superType) {var prototype = object(subType.prototype);prototype.constructor = subType;subType.prototype = prototype;
}function SuperType(name) {this.colors = ['red','blue','green'];this.name = name;
}SuperType.prototype.sayName = function() {return this.name;
};function SubType(name,age) {SuperType.call(this,name);this.age = age;
}inheritPrototype(SubType,SuperType);SubType.prototype.sayAge = function() {return this.age;
}
4.学习地址
【JS】创建对象的6种方式总结
GitHub代码