天天看點

JavaScript封裝的幾種方式

JS 對象封裝的常用方式

JS是一門面向對象語言,其對象是用prototype屬性來模拟的。來看看如何封裝JS對象.

正常封裝

function Person (name,age,sex){
    this.name = name;
    this.age = age;
    this.sex = sex;
}

Pserson.prototype = {
    constructor:Person,
    sayHello:function(){
        console.log('hello');
    }
}      

 這種方式是比較常見的方式,比較直覺,但是Person() 的職責是構造對象,如果把初始化的事情也放在裡面完成,代碼就會顯得繁瑣,如果放在一個方法裡初始化會不會好點呢?

更新版 (常見)

function Person (info){
    this._init_(info);
}

Pserson.prototype = {
    constructor : Person,
    _init_ : function(info) {
        this.name = info.name;
        this.age = info.age;
        this.sex = info.sex;
    }
    sayHello:function(){
        console.log('hello');
    }
}      

 可是,說到這裡就發現,name,age,sex 并沒有在Person裡面申明,哪來的呢???

new 的執行原理

new 的執行過程可以用下面一個函數來代替

var myNew = function(constructor, args) {
        var o = {};
        o.__proto__ = constructor.prototype;
        var res = constructor.apply(o, args);
        var type = typeof res;
        if (['string', 'number', 'boolean', 'null', 'undefined'].indexOf(type) !== -1) {
            return o;
        }
        return res;
    }      

解釋:

首先通過 var o = {} 構造一個空對象.

然後将 構造函數的原型屬性prototype指派給o 的原型對象__proto__ 。這樣,在執行 this.init(info); 這句話的時候,對象 o 就可以在其原型對象中查找_init_ 方法。(原型鍊)。

之後這句話 就是精髓了。

var res = constructor.apply(o,args);      

 以o為上下文調用函數,同時将參數作為數組傳遞。那麼,

this._init_(info);      

 這句話就會被 o 執行,函數

_init_ : function(info) {
        this.name = info.name;
        this.age = info.age;
        this.sex = info.sex;
    }      

以 o 為上下文調用,o也将擁有自己的 name,age,sex 屬性。

如果在構造函數中,return 複合類型,包括對象,函數,和正規表達式,那麼就會直接傳回這個對象,否則,傳回 o

var type = typeof res;
    if(['string','number','boolean','null','undefined'].indexOf(type) !== -1){
        return o;
    }
    return res;      

 測試一下

function Person(name) {
        this.name = name;
    }
    Person.prototype.sayHello = function() {
        console.log(this.name);
    }
    var o1 = myNew(Person, ['pawn']);
    console.log(o1);
    o1.sayHello();      
JavaScript封裝的幾種方式

call方法:

文法:call([thisObj[,arg1[, arg2[,   [,.argN]]]]])

定義:調用一個對象的一個方法,以另一個對象替換目前對象。

說明:

call 方法可以用來代替另一個對象調用一個方法。call 方法可将一個函數的對象上下文從初始的上下文改變為由 thisObj 指定的新對象。

如果沒有提供 thisObj 參數,那麼 Global 對象被用作 thisObj。

apply方法:

文法:apply([thisObj[,argArray]])

定義:應用某一對象的一個方法,用另一個對象替換目前對象。

說明:

如果 argArray 不是一個有效的數組或者不是 arguments 對象,那麼将導緻一個 TypeError。

如果沒有提供 argArray 和 thisObj 任何一個參數,那麼 Global 對象将被用作 thisObj, 并且無法被傳遞任何參數。

類jQuery 封裝

 這種方式是我從 jQuery 那裡學來的。

jQuery 對象具有很強的內建性,可以作為函數調用,也可以做為對象調用,當作為函數調用的時候,她可以無需 new 而傳回它的一個執行個體,很友善。

代碼

var Person = function(info){
    return new Person.prototype.init(info);
}

Person.prototype = {
    constructor: Person,
    init:function(){
        this.name = info.name.
    }
}
Person.prototype.init.prototype = Person.prototype;      

 這種封裝方式非常巧妙。

将對象的構造操作放在函數的裡面,而自己充當一個工廠。

不斷調用 prototype 并不是一個直覺的做法,于是

var Person = function(info){
    return new Person.fn.init(info);
}

Person.fn = Person.prototype = {
    constructor: Person,
    init:function(){
        this.name = info.name;
        this.sayHello = function(){
            this.makeArray();
        }
    }
    makeArray:function(){
        console.log(this.name);
    }
}
// 這句話的作用 
// 雖然把makeArray 等常用方法挂載到 Person.prorotype 下面,但還是會被 init 這個執行個體使用.
Person.fn.init.prototype = Person.fn;      

 最後用 閉包 封裝起來

var Person = (function(window) {
        var Person = function(name) {
            return new Person.fn.init(name);
        }

        Person.fn = Person.prototype = {
            constructor: Person,
            init: function(name) {
                this.name = name;
                this.sayHello = function() {
                    this.makeArray();
                }
            },
            makeArray: function() {
                console.log(this.name);
            }
        }

        Person.fn.init.prototype = Person.fn;

        return Person;
    })();      

 測試一下

var p = Person('pawn');
    console.log(p);
    p.sayHello();      
JavaScript封裝的幾種方式

object.create()

js也提供了一種構造對象的方式,object.create(); 可以傳遞一個對象Person,構造一個p,并且使p 繼承Person.

var Person = {
        name: 'pawn',
        sayHello: function() {
            console.log(this.name);
        }
    }
    var p = Object.create(Person);
    console.log(p);
    p.sayHello();      
JavaScript封裝的幾種方式

可以看到,對象Person的屬性成為了p的原型屬性,也就是說 p 原型繼承自 Person !

我們可以實作一個 Object.create()

Object.create = function(prototype){
   function Func(){};
   Func.prototype = prototype;
   var o = new Func();
   return o;
}      

 在這裡,我們将 Person 作為 構造函數的 原型屬性,就可以構造出 以Person 為原型對象的對象.

JavaScript封裝的幾種方式