Animal.call(this, name);到底是什么?(理解JS中的繼承)


前言

在學習繼承相關的知識點時,遇到了一個問題。
下面這段代碼中的Animal.call(this, name);是什么意思?為什么它就表示繼承到了父類Animal的屬性呢?

function Animal(name) {
    this.name = name;
}
Animal.prototype.say = function() {
    return this.name;
}
function Cat(name, color) {
    Animal.call(this, name);
    this.color = color;
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
var cat = new Cat('小黃', 'yellow');

后來回顧了new操作符的知識點,這才豁然開朗。

一、了解new操作符

通過構造模式來創建對象的關鍵一步就是new操作符,它會根據構造函數創建實例對象。
另外,很重要的是,實例化的過程中,this會指向實例對象,這樣就可以將對應的屬性和方法掛載到實例對象上。
我們打印一下this,發現它確實是實例對象,而非默認的window。

function Foo(name) {
    console.log(this);
    this.name = name;
}
var foo = new Foo('mm');

二、了解call方法

call方法是函數特有的,它用來改變this的指向。
這背后的意思就是:把一個函數的方法綁定到相應的對象上,於是這個對象就就可以調用它。
具體啥意思呢?看下代碼:

function add(x, y) {
    console.log('綁定對象是', this);
    return x + y;
}
var res1 = add(1, 2);
console.log(res1);
var obj = {};
var res2 = add.call(obj, 3, 4);
console.log(res2);


雖然obj沒有add方法,但是它通過call方法就可以調用了。觀察發現,this從window改變到了obj身上!

三、call方法與繼承

大家想想,利用this被改變的這個特點,我們只要寫成構造函數的樣子,不就能把一系列的屬性和方法悄無聲息地掛載到obj上了嗎?!
什么意思呢?比如this.name = name; 既然this改變成obj了,那么原來的代碼就變成這樣了 ===> obj.name = name;
看下代碼:

function foo(name) {
    console.log('綁定對象是', this);
    this.name = name;
    this.say = function() {
        console.log(`我叫${this.name},我喜歡編程!`);
    }
}
foo(); // 直接調用foo,this指向window
var obj = {};
console.log('調用call方法前的obj:',obj);
foo.call(obj, 'mm');
console.log('調用call方法后的obj:',obj);


在改變了this指向后,屬性和方法掛載到了對象上,這不就相當於一種繼承嗎?

四、繼承

再回過頭來看下起初的代碼:

function Animal(name) {
    this.name = name;
}
Animal.prototype.say = function() {
    return this.name;
}
function Cat(name, color) {
    Animal.call(this, name);
    this.color = color;
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
var cat = new Cat('小黃', 'yellow');

第一步,將Cat函數的原型指向Animal的原型對象,這樣Cat就繼承了Animal原型上的say方法;

Cat.prototype = Object.create(Animal.prototype);

在改變了Cat的原型對象后,它的constructor屬性也隨之改變,指向了Animal,因此必須要把它轉回來,所以,
第二步,將Cat的原型對象指向本身(Cat);

Cat.prototype.constructor = Cat;

第三步,實例化Cat對象。

var cat = new Cat('小黃', 'yellow');
  • 首先實例化對象,此時的this綁定到了Cat的實例對象;(然后依次執行以后的代碼)
  • Animal.call(this, name);此時實例對象調用Animal方法,傳入參數name,這樣實例對象上就有了name;
  • this.color = color;接下來是Cat自身的屬性color。將color掛載到實例對象上,這樣實例對象上就有了color。
  • 返回實例對象。

第四步,將實例對象賦值給cat。
最后,cat是否繼承了Animal,又是否有自己的屬性color呢?

console.log(cat);
console.log(cat.say());


OK,我們成功了。

五、總結

綜上,理解Animal.call(this, name);的關鍵是:this綁定對象綁定的是那個實例對象,call方法實現了屬性的繼承。
當然,如果是共享的屬性或方法,還是利用原型鏈來繼承比較好~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM