js原型(prototype)和面对对象

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了js原型(prototype)和面对对象相关的知识,希望对你有一定的参考价值。

•在JS中,每当定义一个函数时候,函数中都会包含一些预定义的属性。其中的一个属性就是原型对象 prototype,原型的作用就是给这个类的每一个对象都添加一个统一的方法,在原型中定义的方法和属性都是被所以实例对象所共享.
•__proto__是一个对象拥有的内置属性,是JS内部使用寻找原型链的属性。当实例化一个对象时候,内部会新建一个__proto__属性并把prototype赋值给它。

prototype对象就是让你有能力向对象添加属性和方法。__proto__相当于继承了prototype。

js创建对象的五种方式:

  1. 工厂模式
  2. 构造函数模式
  3. 原型模式
  4. 混合模式(原型+构造函数)
  5. 动态原型模式

首先工厂模式:

  

 1 var fn=function(){
 2     return "啊打";
 3 };
 4 function Parent(){
 5     var  Child = new Object();
 6     Child.name="李小龙";
 7     Child.age="30";
 8     Child.fn=fn;
 9     return Child;
10 };
11     
12 var  x = Parent();//实例化
13 alert(x.name);//调用
14 alert(x.fn());
var x = {name:‘张安‘,age:22,fn:function(){return "啊打"}};他跟这种写法一样


工厂模式就是在定义一个函数,在函数内部创建Object对象,然后创建属性和方法,最后将对象作为返回值返回给调用者。

在定义方法时最好在外部定义,这样可以避免重复创建该方法。

这种方式引用对象时不推荐使用new关键字,因为使用后可能会出现一些问题,所以这里不建议使用。

其次构造函数模式:

 

function person(name,age){
            this.name = name;
            this.age = age;
            this.family = ["爸爸","妈妈","哥哥"];
            this.sayName = function(){
                console.log(this.name);
            };
            this.myFamily = function(){
                var size = this.family.length;
                var str = "家庭成员:";
                for(var i = 0;i < size;i++){
                    str += this.family[i]+",";
                }
                console.log(str);
            };
        }
        var p = new person("张三",22);
        p.sayName();//张三
        p.family.push("弟弟");
        p.myFamily();//家庭成员:爸爸,妈妈,哥哥,弟弟
        var p1 = new person("李四",22);
        p1.sayName();//李四
        p1.myFamily();//家庭成员:爸爸,妈妈,哥哥

构造函数模式在实例化时必须使用关键字new,不然无法作为构造函数调用而是普通的函数调用,所以不能把里面的属性赋给调用者。

同时内部this的属性还会映射到外部作用域,影响到window,不推荐使用。了解就可以

如果把方法定义到外部.

var fn = function(value){
    return value;
};
var fnList = function(value){
    var size = value.length;
    var str = "";
    for(var i = 0;i < size;i++){
        str += value[i] + ",";
    }
    return str;
};
function Parent(name,age){
    this.name = name;
    this.age = age;
    this.list = ["爸爸","妈妈","哥哥"];
    this.sayName = fn(this.name);
    this.fnList = fnList(this.list);
}
var x = new Parent("张三",22);
console.log(x);
alert(x.sayName);//张三
alert(x.fnList);//爸爸,妈妈,哥哥
var y = new Parent("张三",24);
y.name = "李四";
y.list.push("弟弟");
alert(y.sayName);//张三
alert(y.fnList);//爸爸,妈妈,哥哥

这样的写法在Y重新赋值后无法内部没有受影响,不明白为什么。

原型模式:

function createObject(){}
        createObject.prototype.name="张三";
        createObject.prototype.age="22";
        createObject.prototype.family=["爸爸","妈妈","儿子"];
        createObject.prototype.sayName=function(){
            alert(this.name);
        };
        createObject.prototype.fnFamily=function(){
            var size = this.family.length;
            var str = "家庭成员:";
            for(var i = 0;i < size;i++){
                str += this.family[i] + ",";
            }
            alert(str);
        };

        var x = new createObject();
        console.log(x);
        x.name="李四";
        x.family.push("闺女");
        x.sayName(); //李四
        x.fnFamily();//家庭成员:爸爸,妈妈,儿子,闺女
        var y = new createObject();
        console.log(y);
        y.sayName(); //张三
        y.fnFamily();//家庭成员:爸爸,妈妈,儿子,闺女

原型模式,就是先创建一个函数,然后用函数的prototype属性给函数添加属性和方法。prototype特点是共享。

在实例化时,new createObject()生成一个createObject.__proto__,接着x和y的__proto__继承了createObject.__proto__,所以x,y都拥有了相同的属性。

然而x在自己的作用域把那么改成李四,所以输出的是自己的name属性而不是父类的name属性,但是family用的是push是改变的父类的值。而y没有在自己的作用域改变那么值,所以调用的是父类的name值所以是张三。

这里如果变成x.fanily = [,,,],那么就是在x自己的作用域重新赋值family,这样y的fanily就不会受影响。

混合模式(原型模式+构造函数模式)

function Person(name, age){
            this.name = name;
            this.age = age;
            this.family = ["爸爸","妈妈","儿子"];
        }
        Person.prototype.sayName = function(){
            alert(this.name);
        };
        Person.prototype.fnFamily = function(){
            var size = this.family.length;
            var str = "家庭成员:";
            for(var i = 0;i < size;i++){
                str += this.family[i]+",";
            }
           alert(str);
        };
        var a = new Person("张三",22);
        a.sayName();//张三
        a.family=["闺女"];
        a.fnFamily();//闺女
        var b = new Person("李四",24);
        b.sayName();//李四
        b.fnFamily();//爸爸,妈妈,儿子

这种模式就是现在比较常用的,并且这种模式跟原型模式是结果一样的。这里我们看a的属性,a.family = ["闺女"],跟上面说的一样,这个是在自己的作用域重新赋值,没有影响到父类,所有b输出的还是爸爸,妈妈,儿子。

 最后动态原型模式:

function Person(name,age){
            this.name = name;
            this.age = age;
            this.family = ["爸爸","妈妈","儿子"];
            if(typeof this.fnFamily != "function"){
                Person.prototype.fnFamily = function(){
                    var size = this.family.length;
                    var str = "家庭成员:";
                    for(var i = 0;i < size;i++){
                        str += this.family[i]+",";
                    }
                   alert(str);
                }
            }
            if(typeof this.sayName != "function"){
                Person.prototype.sayName = function(){
                    alert(this.name);
                }
            }
        }
        var x = new Person();
        x.name= "小明";
        x.family.push("闺女");
        x.sayName();//小明
        x.fnFamily();//家庭成员:爸爸,妈妈,儿子,闺女
        var y = new Person();
        y.sayName();//undefined
        y.fnFamily();//家庭成员:爸爸,妈妈,儿子

这种模式将所有的信息都封装在了构造函数里,在实例化时,通过给里面赋值来实现动态操控属性的值。

中间的if判断是为了避免重复创建函数。

总结js原型链:

function xxx(){}...

var a = new xxx();

a.__proto__继承xxx.__proto__继承object.__proto__继承null

 

以上是关于js原型(prototype)和面对对象的主要内容,如果未能解决你的问题,请参考以下文章

JS原型和继承

Js中继承模式

ProtoType(原型)-对象创建型模式

JS原型+原型链+设计模式

设计模式 - Prototype 原型模式

C#设计模式——原型模式(Prototype Pattern)