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();
OK 吧
類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();
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();
結果
可以看到,物件Person的屬性成為了p的原型屬性,也就是說 p 原型繼承自 Person !
我們可以實現一個 Object.create()
Object.create = function(prototype){
function Func(){};
Func.prototype = prototype;
var o = new Func();
return o;
}
在這裡,我們將 Person 作為 建構函式的 原型屬性,就可以構造出 以Person 為原型物件的物件.
測試一下
OK