JavaScript高級程序設計——原型和原型鏈

JS原型

前言

此文章爲加深對JS中重要概念進行理解,不建議沒有任何JS基礎的人看,只爲加深對概念理解經過實際的例子,而不是看書覺得本身讀懂了,可能幾天後就忘了,主要是爲了理解核心概念,以及對重難點解釋。javascript

一切都是對象

「一切都是對象」這句話的重點在於如何去理解「對象」這個概念。前端

概念

JavaScript 中,萬物皆對象!但對象也是有區別的。分爲普通對象函數對象,Object 、Function 是 JS 自帶的函數對象。
固然,也不是全部的都是對象,值類型就不是對象。java

function show(x) {

            console.log(typeof x);    // undefined
            console.log(typeof 10);   // number
            console.log(typeof 'abc'); // string
            console.log(typeof true);  // boolean

            console.log(typeof function () {});  //function

            console.log(typeof [1, 'a', true]);  //object
            console.log(typeof { a: 10, b: 20 });  //object
            console.log(typeof null);  //object
            console.log(typeof new Number(10));  //object
        }
        show();

以上代碼列出了typeof輸出的集中類型標識,其中上面的四種(undefined, number, string, boolean)屬於簡單的值類型,不是對象。剩下的幾種狀況——函數、數組、對象、null、new Number(10)都是對象。他們都是引用類型面試

對象——若干屬性的集合

概念

數組是對象,函數是對象,對象仍是對象。編程

對象裏面的一切都是屬性,只有屬性,沒有方法數組

那麼這樣方法如何表示呢?——方法也是一種屬性。由於它的屬性表示爲鍵值對的形式。
並且,javascript中的對象能夠任意的擴展屬性,沒有class的約束。這個你們應該都知道,就再也不強調了。閉包

先說個最多見的例子:app

var obj = {
            a: 10,
            b: function(x) {
                alert(this.a + x)
            },
            c: {
                name: "yzh",
                age: 21
            }
        }

以上代碼中,obj是一個自定義的對象,其中a、b、c就是它的屬性,並且在c的屬性值仍是一個對象,它又有name、year兩個屬性。編程語言

這個可能比較好理解,那麼函數和數組也能夠這樣定義屬性嗎?——固然不行,可是它能夠用另外一種形式,總之函數/數組之流,只要是對象,它就是屬性的集合。函數

var fn = function () {
            alert(100);
        };
        fn.a = 10;
        fn.b = function () {
            alert(123);
        };
        fn.c = {
            name: "yzh",
            age: 21
        };

上段代碼中,函數就做爲對象被賦值了a、b、c三個屬性——很明顯,這就是屬性的集合。

(引用類型)都是對象,對象是屬性的集合。最須要了解的就是對象的概念。

建立對象

前言

這塊在《JS高級程序設計》也算是大章節下的一塊大內容,我只把一些重要的概念寫出來讓你們理解,具體的深刻要本身去看書中的講解。

函數和對象的關係

對象都是經過函數建立的

function Fn() {
            this.name = 'yzh';
            this.year = 1996;
        }
        var fn1 = new Fn();

有人可能會舉出以下反例

var obj = { a: 10, b: 20 };
var arr = [5, 'x', true];

這種作法屬於使用「快捷方式」,在編程語言中,通常叫作「語法糖」。
其實以上代碼的本質是:

//var obj = { a: 10, b: 20 };
//var arr = [5, 'x', true];

        var obj = new Object();
        obj.a = 10;
        obj.b = 20;

        var arr = new Array();
        arr[0] = 5;
        arr[1] = 'x';
        arr[2] = true;

而其中的 Object 和 Array 都是函數:

console.log(typeof (Object));  // function
console.log(typeof (Array));  // function

總結:對象都是經過函數來建立的

prototype

函數也是一種對象。他也是屬性的集合,你也能夠對函數進行自定義屬性

每建立一個函數,就會同時建立函數的prototype對象。

這個prototype的屬性值是一個對象(屬性的集合,再次強調!),默認的只有一個叫作constructor的屬性,指向這個函數自己。

function Fn() { }
        Fn.prototype.name = '王福朋';
        Fn.prototype.getYear = function () {
            return 1988;
        };

        var fn = new Fn();
        console.log(fn.name);
        console.log(fn.getYear());

Fn是一個函數,fn對象是從Fn函數new出來的,這樣fn對象就能夠調用Fn.prototype中的屬性。

由於每一個對象都有一個隱藏的屬性——「__proto__」,這個屬性引用了建立這個對象的函數的prototype。

即:fn.__proto__ === Fn.prototype
這裏的"__proto__"成爲「隱式原型」

隱式原型

每一個函數function都有一個prototype,即原型。這裏再加一句話——每一個對象都有一個__proto__,可成爲隱式原型。__proto__用於指向建立它的構造函數的原型對象

對象 person1 有一個 __proto__屬性,建立它的構造函數是 Person,構造函數的原型對象是 Person.prototype ,因此:

person1.__proto__ == Person.prototype

又好比:obj這個對象本質上是被Object函數建立的,所以obj.__proto__=== Object.prototype

在說明「Object.prototype」以前,先說一下自定義函數的prototype。自定義函數的prototype本質上就是和 var obj = {} 是同樣的,都是被Object建立,因此它的__proto__指向的就是Object.prototype。

可是Object.prototype確是一個特例——它的__proto__指向的是null.
至於爲何簡單解釋下:

全部的構造器都來自於 Function.prototype,甚至包括根構造器Object及Function自身。全部構造器都繼承了·Function.prototype·的屬性及方法。如length、call、apply、bind

console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype)   // object
console.log(typeof Number.prototype)   // object
console.log(typeof Boolean.prototype)  // object
console.log(typeof String.prototype)   // object
console.log(typeof Array.prototype)    // object
console.log(typeof RegExp.prototype)   // object
console.log(typeof Error.prototype)    // object
console.log(typeof Date.prototype)     // object
console.log(typeof Object.prototype)   // object

知道了全部構造器(含內置及自定義)的__proto__都是Function.prototype,

Function.prototype的__proto__是誰呢

console.log(Function.prototype.__proto__ === Object.prototype) // true
這說明全部的構造器也都是一個普通 JS 對象,能夠給構造器添加/刪除屬性等。同時它也繼承了Object.prototype上的全部方法:toString、valueOf、hasOwnProperty等。

最後Object.prototype的proto是誰?

Object.prototype.__proto__ === null // true
已經到頂了,爲null。

原型鏈

概念

訪問一個對象的屬性時,先在基本屬性中查找,若是沒有,再沿着__proto__這條鏈向上找

javascript中的繼承是經過原型鏈來體現的.

傳統原型語法

function Foo() {}

        Foo.prototype.a = 100;
        Foo.prototype.b = 200;
        var f1 = new Foo();
        f1.a = 10;
        alert(f1.a);  //10
        alert(f1.b);  //200
function Foo() {}
        var f1 = new Foo();
        f1.a = 10;
        Foo.prototype.a = 100;
        Foo.prototype.b = 200;
        
        alert(f1.a);  //10
        alert(f1.b);  //200

對象字面量方法添加屬性和方法的注意事項

function Foo() {}

        Foo.prototype = {
            a: 100,
            b: 200
        }
        var f1 = new Foo();
        f1.a = 10;
        alert(f1.a); //10
        alert(f1.b); //200
function Foo() {}
        var f1 = new Foo();
        f1.a = 10;
        Foo.prototype = {
            a: 100,  
            b: 200
        }
        
        alert(f1.a);  //10
        alert(f1.b);  //undefined

原型的屬性和方法賦值要在,新建實例對象以前,否則沒法得到原型的值和屬性,alert返回相應的undefined

重寫原型對象問題

接上面的例子講,若是在實例上添加新屬性,這個屬性就會屏蔽原型對象中保存的同名屬性,就是阻止訪問了屬性,而不是修改原型的屬性。

function Foo() {}
        var f1 = new Foo();
        f1.a = 10;
        Foo.prototype = {
            a: 100,  
            b: 200
        }
        
        alert(f1.a);  //10
        alert(f1.b);  //undefined

總結:重寫原型對象切斷了現有原型與任何以前已經存在的對象實例之間的關係,它們的引用的仍然是最初的原型

End

暫時總結到此,有些知識點沒有講到,可能須要你們本身去看書或查閱資料來理解,本人理解也有限,文中如有難以理解的還望大神換個方式來闡述。

未完待續

後續還有兩篇講解《執行上下文與做用域》和《閉包》,最後一篇閉包可能會有一些前端面試題來說,並在文章末作個總結。

相關文章
相關標籤/搜索