1. 程式人生 > >Javascript原型介紹

Javascript原型介紹

帶來 () 查找 多個 IT defined 指定 AC 有一點

原型及原型鏈

原型基礎概念

function Person () {
    this.name = 'John';
}
var person = new Person();
Person.prototype.say = function() {
    console.log('Hello,' + this.name);
};
person.say();//Hello,John

上述代碼非常簡單,Person原型對象定義了公共的say方法,雖然此舉在構造實例之後出現,但因為原型方法在調用之前已經聲明,當此實例本身沒有此say方法時候,會在自身原型上查找到此方法。

原型鏈

function Foo() {
    this.value = 42;
}
Foo.prototype = {
    method: function() {}
};

function Bar() {}

// 設置Bar的prototype屬性為Foo的實例對象
Bar.prototype = new Foo();
Bar.prototype.foo = 'Hello World';

// 修正Bar.prototype.constructor為Bar本身
Bar.prototype.constructor = Bar;

var test = new Bar() // 創建Bar的一個新實例

// 原型鏈
test [Bar的實例]
    Bar.prototype [Foo的實例] 
        { foo: 'Hello World' }
        Foo.prototype
            {method: ...};
            Object.prototype
                {toString: ... /* etc. */};

上面的例子中,test 對象從 Bar.prototype 繼承下來;因此,它能訪問 Bar的原型方法,同時Bar.prototypeFoo為Foo的實例對象,能夠訪問Foo的原型方法 method。它也能夠訪問Foo 實例屬性 value。需要註意的是 new Bar() 不會創造出一個新的 Foo 實例,而是重復使用它原型上的那個實例;因此,所有的 Bar 實例都會共享相同的 value 屬性。

屬性查找

當查找一個對象的屬性時,JavaScript 會向上遍歷原型鏈,直到找到給定名稱的屬性為止,到查找到達原型鏈的頂部 - 也就是 Object.prototype - 但是仍然沒有找到指定的屬性,就會返回 undefined,我們來看一個例子:


 function foo() {
     this.add = function (x, y) {
         return x + y;
     }
 }

 foo.prototype.add = function (x, y) {
     return x + y + 10;
 }

 Object.prototype.subtract = function (x, y) {
     return x - y;
 }

 var f = new foo();
 alert(f.add(1, 2)); //結果是3,而不是13
 alert(f.subtract(1, 2)); //結果是-1

通過代碼運行,我們發現subtract是安裝我們所說的向上查找來得到結果的,但是add方式有點小不同,這也是我想強調的,就是屬性在查找的時候是先查找自身的屬性,如果沒有再查找原型,再沒有,再往上走,一直插到Object的原型上,所以在某種層面上說,用 for in語句遍歷屬性的時候,效率也是個問題。

還有一點我們需要註意的是,js中基礎構造器的prototype是不可改寫的, 不可刪除, 不可見的;

Object.getOwnPropertyDescriptor(Number, 'prototype');
// Object {value: Number, writable: false, enumerable: false, configurable: false};

hasOwnProperty函數:

hasOwnProperty是Object.prototype的一個方法,它可是個好東西,他能判斷一個對象是否包含自定義屬性而不是原型鏈上的屬性,

// 修改Object.prototype
Object.prototype.bar = 1; 
var foo = {goo: undefined};

foo.bar; // 1
'bar' in foo; // true

foo.hasOwnProperty('bar'); // false
foo.hasOwnProperty('goo'); // true

使用 hasOwnProperty 可以給出正確和期望的結果,這在遍歷對象的屬性時會很有用。

對象在查找屬性時, 首先從自身查找, 查不到在原型鏈上查找, 層層向上一旦查到就返回, 直到查到 Object.protype 還查不到就返回undefined。
大家可以體會一下下面的結果, 建議動手畫一下js中幾種構造器和函數類型的原型鏈, 徹底理解他們之間的關系。

Function.toString === Object.toString                       // true
Function.prototype.toString === Object.toString             // true
Function.prototype.__proto__ === Object.prototype           // ture
Function.prototype.toString === Object.prototype.toString   // false

當檢查對象上某個屬性是否存在時,hasOwnProperty 比較推薦的方法。同時在使用 for in loop 遍歷對象時,推薦總是使用 hasOwnProperty 方法,這將會避免原型對象擴展帶來的幹擾,我們來看一下例子:

// 修改 Object.prototype
Object.prototype.bar = 1;

var foo = {moo: 2};
for(var i in foo) {
    console.log(i); // 輸出兩個屬性:bar 和 moo
}

我們沒辦法改變 for in 語句的行為,所以想過濾結果可以使用 hasOwnProperty 方法,代碼如下:

// foo 變量是上例中的
for(var i in foo) {
    if (foo.hasOwnProperty(i)) {
        console.log(i);
    }
}

這個版本的代碼是唯一正確的寫法。由於我們使用了 hasOwnProperty,所以這次只輸出 moo。如果不使用 hasOwnProperty,
也可以使用Object.key()來獲取目標的屬性和方法列表,得到的將是一個數組, 裏面的屬性是目標對象上的, 不含其原型上和其自身不可枚舉的屬性, 若要想得到更細致的結果可以使用 Object.getOwnPropertyNames() 配合 hasOwnProperty() 使用。

總結:推薦使用 hasOwnProperty,不要對代碼運行的環境做任何假設,不要假設原生對象是否已經被擴展了。

總結

原型極大地豐富了我們的開發代碼,但是在平時使用的過程中請一定要註意上述提到的一些註意事項。

  1. 對象屬性的查找規則, 原型鏈上屬性之間的屏蔽。
  2. 深入理解 hasOwnProperty()for in 機制、Object.keys()
  3. in 操作符具有遍歷到原型鏈頂端的特性,還能夠從對象和原型鏈上不可枚舉的屬性拿到 true, 以至於我們 for in 遍歷的時候要註意原型鏈上的方法。
  4. Object.getOwnPropertyDescriptor() 可以幫助我們更細致的了解對象上的屬性。
  5. 也涉及到了 Object.defineProperty() 方法, 可用來非常細致的定義對象上的某個屬性, 接受三個參數, 對象(object), 屬性名(string), 屬性描述器(object), 另外 Object.defineProperties() 也是相似的,只是它接受2個參數, 要被定義屬性的對象(object), 屬性描述集合props(object), 該方法可以一次定義多個屬性。

Javascript原型介紹