JavaScript 面向对象的原型与实例分析

发布时间:2020-02-28编辑:脚本学堂
本文介绍下,javascript中面向对象的原型与实例,有需要的朋友参考下。

一、函数(构造函数)、原型、实例对象之间的关系

A、函数与原型的关系

1、只要创建了一个函数,那么它就会根据一组特定的规则为该函数创建一个prototype属性(原型对象)
如:
 

复制代码 代码示例:
function fun(){    
}
console.log(fun.prototype); //fun {},说明prototype是一个对象
 

注意:原型中定义的属性、方法是所有调用构造函数实例化对象所共享的

2、prototype属性都会自动获得一个constructor(构造函数)属性,constructor属性包含了prototype属性所在函数(fun)的指针(很重要),说明在构造函数(fun)中可以访问原型中定义的属性和方法,
如果构造函数中定义了原型中的同名属性方法时,那么实例就会调用重新定义的属性与方法了;当一个函数使用new操作符实例化一个对象时,它用来标识实例化对象的类型(并没有太大的实际意义)
如:
 

复制代码 代码示例:
function fun(name){
    console.log(fun.prototype.name == this.name);//true(yjh)
    this.name = "yjh1";
    console.log(this.name);//yjh1
    console.log(fun.prototype.name == this.name);//false(yjh,yjh1)
}
fun.prototype = {
    constructor: fun,
    name: "yjh"
}
var fun1 = new fun();
console.log(fun.prototype.constructor == fun); //true
console.log(fun1.constructor == fun); //true

B、实例化对象与原型的关系

1、当函数使用new操作符实例化一个对象时,那么对象就包含了一个内在的指向原型的__proto__属性,它只存在实例对象与原型对象之间
如:
 

复制代码 代码示例:
function fun(name,age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        alert(this.name);  
    }
}
fun.prototype = {
    constructor: fun,
    age: 22,
    sayName: function(){
        alert(this.age);
    }  
}
var fun1 = new fun("yjh","23");
console.log(fun1.__proto__) //fun { age="22", sayAge=function()}
console.log(fun1.__proto__ == fun.prototype); //true
 

C、实例对象与函数(构造函数)的关系

1、当函数使用new操作符实例化一个对象时,实例对象通过内部属性__proto__指向原型,共享原型(prototype)中定义的属性和方法,
由于原型(prototype)中的constructor属性指向构造函数,因此实例对象也拥有构造函数中定义的属性和方法
如:
 

复制代码 代码示例:
function fun(name,age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        alert(this.name);  
    }
}
var fun1 = new fun("yjh","23");
fun1.sayName(); //yjh

D、函数(构造函数)与实例对象、原型三者之间的关系

1、在调用实例化对象属性,方法时,那么首先会搜索实例对象自身定义的属性和方法,如果没有的话,会继续搜索原型
如:
 

复制代码 代码示例:
function fun(name,age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        alert(this.name);  
    }
}
fun.prototype.age = "22";
fun.prototype.sayAge = function(){
    alert(this.age);   
}
var fun1 = new fun("yjh","23");
fun1.age = 24;
fun1.sayAge(); //24 ,调用了原型中的方法;
 

首先搜索sayAge方法,实例对象中没有,搜索原型,在原型中找到sayName方法,继续搜索age属性,发现在fun1实例对象中已有定义age值,因此直接使用,不再继续搜索了;如果没有直接在fun1实例对象定义age属性的话,那么结果就是23了,原因是因为构造函数重新定义了实例对象的age属性

二、面向对象模式:
对函数的初步理解:
a、javascript中的任何一个函数都是一个Function类型的一个实例,也是一个Object类型的实例,定义了一个function函数,那么它就是一个实例对象,实例对象的内部属性__proto__指向了Object构造函数的原型prototype属性,因此实例继承了Object对象的默认属性和方法;
b、普通函数默认返回undefined,构造函数返回一个实例对象。

1、创建对象,使用一个特定接口new Object()
缺点:使用同一个接口创建很多对象,会产生大量重复的代码

2、使用工厂模式,用函数来封装,以特定接口创建对象
如:
 

复制代码 代码示例:
 function createObj(name,age){
    var o = new Object();
    o.name = name;
    o.age = age;
    return o;
}
var o1 = createObj("yjh",23)
 

优点:解决了使用一个接口创建多个相似对象产生大量重复代码的问题
缺点:没有解决对象识别的问题,即o1是怎么样的一个对象类型

3、构造函数模式,JavaScript没有类概念
如:
 

复制代码 代码示例:
   function CreateObj(name,age){
    this.name = name;
    this.age = age;
    this.sayName = function(){
        alert("hi" + this.name);   
    }
}
var obj1 = new CreateObj("yjh1",23);
var obj2 = new CreateObj("yjh2",23);
 

优点:解决了实例对象类型识别的问题,obj1,obj2对象为CreateObj类型。

缺点:构造函数定义的属性和方法不是所有实例所共享的,各实例调用的方法是两个不同Function类型的实例(obj1.sayName != obj2.sayName)

4、原型模式
如:
 

复制代码 代码示例:
function CreateObj(){
}
CreateObj.prototype = {
    constructor: CreateObj,
    name: "yjh",
    age: 23,
    colors: ["a","b"],
    sayName: function(){
        alert(this.name);  
    }
}
var obj1 = new CreateObj();
var obj2 = new CreateObj();
alert(obj1.sayName == obj2.sayName);//true
obj1.colors.push("c");
alert(obj2.colors);//a,b,c
 

说明:调用obj1,obj2实例的属性和方法,首先会搜索实例自身定义的属性和方法,如果没有,由于实例的__proto__内部属性指向原型,
因此会继续搜索原型中定义的属性和方法

优点:原型中定义的属性和方法是所有实例对象所共享的,解决了多个函数实现同一功能的问题
缺点:如果在原型中定义的属性包含的是引用类型的值,那么通过修改一个实例属性值会影响到另一个实例属性值,这正是由于原型的共享本质所导致的

5、组合模式(构造函数模式与原型模式)
如:
 

复制代码 代码示例:
   function CreateObj(name,age){
    console.log(this.name);//yjhyjh
    this.name = name;
    this.age = age;
    this.colors = ["a","b"];
}
CreateObj.prototype = {
    constructor: CreateObj,
    name: "yjhyjh",
    sayName: function(){
        return this.name;
    }
}
var obj1 = new CreateObj("yjh1",23);
var obj2 = new CreateObj("yjh2",23);
alert(obj1.sayName == obj2.sayName);//true
alert(obj1.sayName());//yjh1
alert(obj2.sayName());//yjh2
obj1.colors.push("c");
alert(obj2.colors);//a,b
 

说明:把所有实例不需要共享的属性定义在构造函数中,把需要共享的属性,方法定义在原型中,互补构造函数模式和原型模式的优缺点,
原型是所有实例化对象的原型对象,实例与原型之间是通过实例内部属性__proto__连接到原型,所有实例共享原型中的属性和方法,如果构造函数中重新定义了原型中的同名属性、方法,
那么实例对象将会调用构造函数中定义的属性与方法。

6、继承(实现继承,原型链)
就是把一个构造函数的原型作为另一个构造函数的实例化对象,那么这个实例化原型对象就会继承另一个构造函数的原型属性和方法,这就是所谓的原型链
如:
 

复制代码 代码示例:
function Fun1(){
    this.name = ["yjh1","yjh2"];
}
Fun1.prototype = {
    constructor: Fun1,
    sayName: function(){
        alert(this.name)   
    }
}
function Fun2(){}
Fun2.prototype = new Fun1();
var fun2 = new Fun2();
fun2.sayName();//yjh1,yjh2
fun2.name.push("yjh3"); //fun2.name = ["yjh1","yjh2","yjh3"];
var fun3 = new Fun2();
alert(fun3.name);//yjh1,yjh2,yjh3
 

缺点:来自包含引用类型值的原型,原型中定义的属性,方法是所有实例所共享的,Fun2.prototype原型对象是Fun1类型的一个实例,
因此原型对象中有包含引用类型值的name属性,当实例化多个Fun2类型对象时,所有的实例对象都共享这个原型name属性,通过修改实例中的name属性值,
会直接修改原型中定义的name属性值

7、组合继承(继承与借用构造函数)
如:
 

复制代码 代码示例:
 function Fun1(){
    this.name = ["yjh1","yjh2"];
}
Fun1.prototype = {
    constructor: Fun1,
    sayName: function(){
        alert(this.name)   
    }
}
function Fun2(){
    Fun1.call(this);   
}
Fun2.prototype = new Fun1();
var fun2 = new Fun2();
fun2.sayName();//yjh1,yjh2
fun2.name.push("yjh3"); //fun2.name = ["yjh1","yjh2","yjh3"];
var fun3 = new Fun2();
alert(fun2.name);//yjh1,yjh2,yjh3
alert(fun3.name);//yjh1,yjh2
 

说明:
由于构造函数中定义的属性,方法不是所有实例共享的,而且会覆盖原型中定义的属性与方法,所以它不会由于实例化原型对象(Fun2.prototype)中包含了引用类型值的属性而存在的问题。

您可能感兴趣的文章:

JavaScript面向对象编程(入门参考)
理解JavaScript中的面向对象
深入解析 Javascript 面向对象编程
JavaScript面向对象的一个例子
JavaScript 面向对象(OOP)的语法参考
javascript面向对象之this用法举例
javascript面向对象编程之this详解
javascipt面向对象扩展的例子
javascipt面向对象之成员函数实例
javasrcipt 面向对象编程的例子