Javascript原型(一)

重複的對象

  • 在 Javascript 中,重複執行的代碼容易出現重複的對象
  • 傳統的構造方法的定義方式會影響性能,容易形成多個對象有多個對象副本。應該將方法單獨抽取出來,讓全部對象共享該方法,globalSayHello()就是這樣的一個方法。
function globalSayHello() {
    console.log("hello");
}

function Person() {
    var o = {};
    o.sayHello = globalSayHello;
    return o;
}

var p1 = new Person();
var p2 = new Person();
p1.sayHello == p2.sayHello; // true

針對以上狀況,能夠將方法所有放到外面,好比,將sayHello()單獨定義到全局中,代碼以下:javascript

function sayHello() {//TODO}

可是這麼作會有以下安全隱患java

  • 在開發中引入各類框架或庫,自定義的成員越多,出現命名衝突的機率越大
  • 可能在開發中有多個構造函數,每個構造函數應該有多個方法,那麼就會變得不容易維護

初識原型

  • 任意一個對象,都會默認鏈接到它的原型中數組

    • 建立一個函數,會附帶建立一個特殊對象,該對象使用函數.prototype引用,稱其爲函數的原型屬性。強調一下是原型屬性
    • 每個由該函數做爲構造函數建立的對象,都會默認鏈接到該對象上
function Person() {}
var p = new Person();

/*p就是由Person函數做爲構造函數建立出來的對象*/
/*在 Chrome 瀏覽器的 watch 中可使用以下方式查看結構*/
p._proto_ == Person.prototype // true

在原型中查找

  • 在該對象訪問某一個方法或屬性時,若是該對象中沒有,則會去到這個神祕對象中查找(也就是原型中去查找)
function Person(name) {
    this.name = name;
}
/* 在原型中定義了 printName() 方法 */
Person.prototype.printName = function () {
    console.log(this.name);
};
var p = new Person("z4");
p.printName()
/**
* 構造函數 Person 中沒有 printName() 這個方法
* 當 p 對象調用這個方法的時候,會先去構造方法中
* 查找,若是找不到,會去原型中查找。
*/

從調試工具中能夠看到,p 對象中是不存在 printName() 方法的,此方法是在 __proto__ 對象中存在的
clipboard.png瀏覽器

鏈接到原型對象(神祕對象)

  • 每個由構造函數建立出來的對象,都會默認鏈接到該神祕對象上
var f1 = new Foo();
    var f2 = new Foo();
    
    /* 若是 f1 中沒有 sayHello 方法,就會去 Foo.prototype 中查找 */
    f1.sayHello();
    /* 若是 f2 中沒有 sayGood 方法,就會去 Foo.prototype 中查找 */
    f2.sayGood();

信息共享

  • 由構造函數建立出來的衆多對象共享一個對象,就是 構造函數.prototype
  • 只須要將共享的東西,重複佔用多的東西放到 構造函數.prototype 中,那麼全部的對象就能夠共享了
var arr = [];
for(var i = 0; i < 4; i++) {
    arr.push({});
    arr.push(function() {})
}

console.log(arr[0] == arr[2]); //false
console.log(arr[1] == arr[3]); //false


/**
* 分析:
* 數組中每次新 push 的 {} 都是一個新的對象
* 因此 arr[0] == arr[2] 是 false
* 同理,每次 push 的 function(){} 也是一個新的對象,因此也不相等
*/
function createPerson() {
    var o = {};
    o.sayHello = function () {
        console.log("hello");
    };
    return o;
}

var p1 = createPerson();
var p2 = createPerson();
console.log(p1 == p2); //false
console.log(p1.sayHello == p2.sayHello); //false


/**
* 分析:
* 每次執行 createPerson() 方法時
* o對象老是一個新的對象,因此 p1 == p2 是 false
* 同理,sayHello 方法每次也是一個新的對象
*/

如今考慮一個問題,這樣建立出的對象是否很浪費資源,好比 sayHello 對象(方法也是一個對象),每次執行 createPerson() 方法都會建立一個都具備相同的功能,這樣是一種資源的浪費。安全

解決資源浪費

function globalSayHello() {
    console.log("hello");
}
function createPerson() {
    var o = {};
    o.sayHello = globalSayHello;
    return o;
}
var o1 = createPerson();
var o2 = createPerson();
console.log(o1.sayHello == o2.sayHello); //true

/**
* 分析:
* globalSayHello 賦值給 o.sayHello , 每次執行 createPerson() 方法時
* o.sayHello 指向的都是同一個對象,因此結果是 true,這樣就解決了資源浪
* 費的問題,由於不管執行多少次 createPerson() 方法, o.sayHello 指向的
* 是同一個對象。
*/

使用以上方式有個問題就是會定義不少的成員變量,每一個方法都是一個成員變量。這也是在重複對象中提到的安全隱。可使用原型來解決這個問題。框架

使用原型解決問題

function Person(name) {
    this.name = name;
}

/* 在原型中定義了 printName() 方法 */
Person.prototype.printName = function () {
    console.log(this.name);
};

var p = new Person("王二麻");

var p1 = new Person("李四");

console.log(p.printName == p1.printName) //true
/**
* 不管建立多少個對象,printName 都只有一個,都是指向的同一個引用,因此
* 資源浪費的問題解決了,還有將方法定義在原型屬性中,也減小了一個成員變
* 量。若是須要定義多個方法,均可以定義在原型屬性中,也能夠採用下面的方式
* 給原型屬性賦值。
*/
Person.prototype = {
    say: function () {
        return "hello";
    },
    hobby: function () {
        return "javascript";
    }
}

var p2 = new Person("田七");
console.log(p2.say() + " " + p2.hobby()); //hello javascript

原型相關概念

  • 神祕對象針對構造函數稱爲原型屬性函數

    • 神祕對象就是構造函數的原型屬性
    • 簡稱原型
  • 神祕對象與構造函數所建立出來的對象也有必定關係工具

    • 神祕對象針對構造函數建立出來的對象稱爲原型對象
    • 簡稱原型
  • 對象繼承自其原型性能

    • 構造函數建立的對象,繼承自構造函數的原型屬性
    • 構造函數建立的對象,繼承自該對象的原型對象
    • 構造函數建立的對象構造函數的原型屬性表示的對象是兩個不一樣的對象
相關文章
相關標籤/搜索