javascript面向对象和原型————呱呱二号
Posted 呱呱二号
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javascript面向对象和原型————呱呱二号相关的知识,希望对你有一定的参考价值。
面向对象
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();
(4-6未完善,日后再补,邪恶脸)
4、原型式继承:这种继承借助原型并基于已有的对象创建新对象,同事不必因此创建自定义类型。
//临时中转函数
function obj(o){ //o表示将要传递进入的一个对象
function F(){} //F构造是一个临时新建的对象,用来存储传递过来的对象
F.prototype = o; //将o对象实例赋值给F构造的原型对象
return new F(); //最后返回这个得到传递过来的对象的对象实例
}
//其实F.prototype = o;就相当于 Child.prototype = new Parent();
//这是字面量的声明方式,相当于var box = new Box();
var box = {
name: \'lee\',
age: 100,
family: [\'haha\',\'hehe\']
}
//box1就等于new F();
var box1 = obj(box);
alert(box1.name);
box1.family.push(\'xixi\');
alert(box1.family); //[\'haha\',\'hehe\',\'xixi\']
var box2 = obj(box);
alert(box2.family); //[\'haha\',\'hehe\',\'xixi\'] //对象的引用属性,共享了
5、寄生式继承(原型式加工厂模式):
function obj(o){ //o表示将要传递进入的一个对象
function F(){} //F构造是一个临时新建的对象,用来存储传递过来的对象
F.prototype = o; //将o对象实例赋值给F构造的原型对象
return new F(); //最后返回这个得到传递过来的对象的对象实例
}
//寄生函数
function create(o){
var f = obj(o);
f.run = function () {
return this.name;
}
return f;
}
var box = {
name: \'lee\',
age: 100,
family: [\'haha\',\'hehe\']
}
var box1 = create(box);
alert(box1.name);
6、寄生组合继承
//临时中转函数
function obj(o){ //o表示将要传递进入的一个对象
function F(){} //F构造是一个临时新建的对象,用来存储传递过来的对象
F.prototype = o; //将o对象实例赋值给F构造的原型对象
return new F(); //最后返回这个得到传递过来的对象的对象实例
}
//寄生函数
function create(box,desk){
var f = obj(box.prototype);
desk.prototype = f;
return f;
}
function Box(name,age){
this.name = name;
this.age = age;
}
Box.prototype.run = function(){
return this.name + this.age + \'运行中...\'
}
function Desk(name,age){
Box.call(this.name);
this.age = age;
}
//通过寄生组合继承来实现继承
create(Box,Desk); //这句话用来代替Desk.prototype = new Box()
let desk = new Desk(\'lee\',100);
alert(desk.run());
(1、原型链继承,2、借用构造函数继承(对象冒充继承)3、组合继承(结合前两种)4、原型式继承)
以上是关于javascript面向对象和原型————呱呱二号的主要内容,如果未能解决你的问题,请参考以下文章