物件-原型-繼承

Zwe1發表於2018-04-02

前言

  • 本想單獨來寫一篇關於原型和原型鏈的文章,但思來想去很難將它單獨從javascript中摘取出來獨成一篇文章。講到原型,就必然提到js物件,講到原型,其目的不就是為了繼承嘛!既然如此就把他們放在一起來講吧。

  • javascript是一種OOP語言,其中所有資料型別都是物件,因此需要通過一種機制,將物件聯絡起來。原型便是這門語言中實現繼承,實現資源共享的一種方式。

javascript一切皆物件。

建立一個物件的方式

物件字面量表示法

var obj = {
    'name': 'an object'
};
複製程式碼

使用建構函式 (ES6中class的實現方法)

什麼是建構函式?
建構函式是Javascript中用於生成物件的一種函式型別模板。

function People (name) {
    this.name = name || 'default name';
};
People.prototype.hello = function () {
	console.log('hello ' + this.name);
};

var female = new People('female');  
female.hello();
// hello female
複製程式碼

new關鍵詞呼叫一個建構函式,會返回一個物件。如果不使用new操作符,直接賦值,它將如普通函式一般執行,返回undefined。

Object.create()

var People = {
    'name': 'myname',
    'hello': function () {
        console.log('hello ' + this.name);
    }
};
var male = Object.create(People);
male.hello();
//hello myname
複製程式碼

在Javascript中物件是一種引用資源,一個物件將被存放在記憶體堆中,我們對它的取值賦值操作其實是在操作它的堆地址。

原型和原型鏈

  • 每個物件都有一個私有屬性叫做“原型”,其型別為物件。console.dir(window),便可以看到在其屬性中有一項名為__proto__,便是window物件的原型。

  • 在Javascript一切皆物件,所以一個物件的原型也有__proto__,即物件的原型也有原型屬性。

  • 當訪問一個物件屬性時,會先搜尋物件的本地屬性,不存在時會沿著其原型鏈向上查詢,直至Object的原型。

  • 原型是其所有引用者物件的公共資料倉儲。可以通過原型來實現物件繼承。

var fooProto = {};
var Foo = function (y) {
    this.y = y;
};
Foo.prototype = fooProto;
var b = new Foo(20);
var c = new Foo(30);

console.log(b.__proto__ === Foo.prototype);  //true
console.log(b.__proto__ === fooProto);  //true
console.log(c.__proto__ === Foo.prototype);  //true
console.log(c.__proto__ === fooProto);  //true
console.log(Foo.__proto__ === Function.prototype);  //true
console.log(Foo.prototype.__proto__ === Object.prototype);  //true
複製程式碼

原型鏈

__proto__和prototype

  • 幾乎所有函式都有一個名為prototype的屬性。prototype指向所有以該函式為建構函式的物件例項的原型,在原型中有所有例項的共享資源。

  • 物件具有屬性__proto__,被稱為隱式原型,一個物件的__proto__指向該物件的建構函式的原型,如此所有例項物件便能夠訪問在建構函式原型中定義的屬性和方法。

var Alien = function () {};
var Zippy = new Alien();
console.log(Zippy.__proto__ === Alien.prototype); //true
複製程式碼

原型鏈

當不為一個建構函式指定原型時,其預設原型中會有兩個屬性,constructor和__proto__。 constructor指向物件的建構函式。物件原型的constructor指向物件本身。

_proto_和prototype

繼承

本節內容援引阮一峰個人博文

繼承是面嚮物件語言的特色之一。(封裝,多型,繼承)
通俗的講,繼承就是把別人的東西拿來用,省時省力省記憶體。

建構函式繫結

function Animal(){
    this.species = "動物";
}

function Cat(name,color){
    Animal.apply(this, arguments);
    this.name = name;
    this.color = color;
}

var cat1 = new Cat("大毛","黃色");
alert(cat1.species); // 動物
複製程式碼

使用apply方法指定Animal函式中this的上下文環境。

prototype模式

Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黃色");
alert(cat1.species); // 動物
複製程式碼

將Cat的prototype物件指向Animal的例項,便可以繼承Animal。
之前我們提到物件的constructor指向其建構函式。當我們將Cat的prototype指向Animal的一個例項時,其constructor便指向了Animal例項的建構函式,即Animal方法。所以我們需要修正Cat原型prototype的constructor指向Cat本身。

直接繼承prototype

由於Animal物件中,不變的屬性都可以直接寫入Animal.prototype。所以我們也可以讓Cat()跳過Animal(),直接繼承Animal.prototype。

function Animal(){ };
Animal.prototype.species = "動物";
Cat.prototype = Animal.prototype;
Cat.prototype.constructor = Cat;
var cat1 = new Cat("大毛","黃色");
alert(cat1.species); // 動物
複製程式碼
  • 與前一種方法相比,這樣做的優點是效率比較高(不用執行和建立Animal的例項了),比較省記憶體。缺點是Cat.prototype和Animal.prototype現在指向了同一個物件,那麼任何對Cat.prototype的修改,都會反映到Animal.prototype。

利用空物件作為中介

function extend(Child, Parent) {
    var F = function(){};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
    Child.uber = Parent.prototype;
};

extend(Cat,Animal);
var cat1 = new Cat("大毛","黃色");
alert(cat1.species); // 動物
複製程式碼

拷貝繼承

純粹採用"拷貝"方法實現繼承。

function extend2(Child, Parent) {
    var p = Parent.prototype;
    var c = Child.prototype;
    for (var i in p) {
        c[i] = p[i];
    }
    c.uber = p;
};

extend2(Cat, Animal);
var cat1 = new Cat("大毛","黃色");
alert(cat1.species); // 動物
複製程式碼

相關文章