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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM