Js的call和apply以及ES5、ES6的繼承


call方法


MDN關(guān)于call方法的解釋

call() 方法調(diào)用一個函數(shù), 其具有一個指定的this值和分別地提供的參數(shù)(參數(shù)的列表)。

注意:該方法的作用和 apply() 方法類似,只有一個區(qū)別,就是call()方法接受的是若干個參數(shù)的列表,而apply()方法接受的是一個包含多個參數(shù)的數(shù)組。


例子:

function Person(name,age){
    this.name=name
    this.age=age
}


function Student(name,age){
    Person.call(this,name,age)
    this.grade="高三"
}


var s1=new Student("Andy",18)

console.log(s1.name)  // Andy
console.log(s1.age)   // 18

console.log(Person.prototype===Student.prototype)    //  false
console.log(Person.prototype.constructor===Student.prototype.constructor)    //  false



語法

fun.call(thisArg, arg1, arg2, ...)

參數(shù)

??thisArg

fun函數(shù)運行時指定的this值。

需要注意的是,指定的this值并不一定是該函數(shù)執(zhí)行時真正的this值,如果這個函數(shù)處于非嚴(yán)格模式下,則指定為nullundefinedthis值會自動指向全局對象(瀏覽器中就是window對象),同時值為原始值(數(shù)字,字符串,布爾值)的this會指向該原始值的自動包裝對象。

arg1, arg2, ...

指定的參數(shù)列表

返回值

返回值是你調(diào)用的方法的返回值,若該方法沒有返回值,則返回undefined。


描述

可以讓call()中的對象調(diào)用當(dāng)前對象所擁有的function。你可以使用call()來實現(xiàn)繼承:寫一個方法,然后讓另外一個新的對象來繼承它(而不是在新對象中再寫一次這個方法)。


現(xiàn)在再來看一下上面的那個例子


function Person(name,age){
    this.name=name
    this.age=age
}



function Student(name,age){
    Person.call(this,name,age);
    this.grade="高三";
}


var s1=new Student("Andy",18)

console.log(s1.name)  // Andy
console.log(s1.age)   // 18

在這個例子中,函數(shù)Person里面有this,這個this 指向的是什么要看你是怎么調(diào)用的,

如果你直接Person()直接這樣調(diào)用,那么這個this指向的就是全局對象window

如果說你作為函數(shù)Person new出來的一個實例來調(diào)用,那么這個this指向的就是這個實例對象。

上面代碼中,s1Student new出來的一個實例,那么這個實例在使用的時候,Student內(nèi)部的this指向的就是這個實例對象.

那么Student這個函數(shù)里面的this指向的就是實例對象s1,Person.call(this,name,age)是一個立即執(zhí)行的函數(shù),意思是說執(zhí)行這個Person函數(shù),并且這個函數(shù)內(nèi)部的this指向s1這個實例對象。

最上面的function Person(){}這個函數(shù)內(nèi)部的this指向的就是s1,結(jié)合 this.name=name; this.age=age就可以看出s1的name屬性就是參數(shù)name,age屬性就是參數(shù)age

所以打印s1.names1.age都能打印出來。

另外,特別需要注意的是, 使用關(guān)鍵字 new用來new一個實例的時候,會自動調(diào)用對象方法。 就是說在本例中,new Student("Andy",18) 的時候,會去調(diào)用 Student這個方法?。?!


Student方法中call了一下,并且傳入了和Person方法中同樣的參數(shù) name、age是讓Student這個方法new出來的實例中都有了nameage這兩個屬性。

事實上,當(dāng)你嘗試打印 Person.hasOwnProperty('age') Student.hasOwnProperty('age')結(jié)果都會是false

console.log(Person.hasOwnProperty('age'))   // false

console.log(Student.hasOwnProperty('age'))  // false

console.log(s1.hasOwnProperty('age'))      // true

var p1=new Person('小明',5)
console.log(p1.hasOwnProperty('age'))      // true

說明nameage這兩個屬性都是實例對象上的。



Apply方法

apply方法的作用和call方法一樣。 只是傳參的方式不一樣,上面的call方法可以看到name,age屬性實際上是一個一個傳進(jìn)去的。 而apply方法是可以以數(shù)組的形式一下子全放進(jìn)去。

上面的例子換成apply就是下面的這種寫法


function Person(name,age){
    this.name=name
    this.age=age
}



function Student(name,age){
    Person.apply(this,[name,age]);
    this.grade="高三";
}


var s1=new Student("Andy",18)

console.log(s1.name)  // Andy
console.log(s1.age)   // 18



es5的繼承

// es5的繼承

function Person(name,age){
  this.name=name
  this.age=age
}



function Student(name,age,grade){
  Person.call(this,name,age);
  this.grade=grade;
}

// Object.create() 方法是用來創(chuàng)建一個新的對象,該對象的原型指向括號里面的參數(shù)
Student.prototype=Object.create(Person.prototype)
// 上面的這條語句是說,創(chuàng)建一個新的對象,該對象的原型指向Person.prototype,
并將它賦值給Student.prototype

// 再設(shè)置 constructor屬性等于Student
Student.prototype.constructor=Student


//  以上就是es5的繼承


//   需要注意的是,子類的原型上是沒有父類的屬性和方法的

console.log(Student.prototype.hasOwnProperty("age")) 
//  false 子類的原型上是沒有父類的屬性和方法的

// 子類的實例上才有這些屬性和方法
var s1=new Student('張三',30)
console.log(s1.hasOwnProperty("age"))  //  true

ES5中這種最簡單的繼承,實質(zhì)上就是將子類的原型設(shè)置為父類的實例。

需要經(jīng)過
????1.定義祖先
????2.定義祖先可繼承的變量/方法
????3.定義繼承的類(構(gòu)造函數(shù)),并在類中調(diào)用組件的方法 call和apply方法
????4.使用 prototyoe定義繼承關(guān)系
????5.重新將constructor指向自己



1. 為什么在子類上需要call一下?

? ?:如果不call,用子類new出來的實例上就不會有父類的屬性和方法。

實際上nameage這兩個屬性,或者說父類上的方法,你call了之后,等于說是在子類的實例上有了父類的屬性和方法,子類本身是沒有這些個屬性和方法的。 不信你call了之后打印 Student.prototype.hasOwnProperty("age") 一定是 false的!

2. 為什么需要Object.create()?

? ?:新創(chuàng)建對象的原型對象

3. 為什么需要Student.prototype.constructor=Student?

? ?:重新將constructor指向自己

ES5 的繼承,實質(zhì)是先創(chuàng)造子類的實例對象this,然后再將父類的方法添加到this上面(Parent.call(this))。

上面這句話就很好理解了。


es6的繼承

//  es6的繼承

//定義類

class Person {
    // constructor方法,就是構(gòu)造方法
    // 如果沒有定義構(gòu)造方法,js會自動為其添加
    //  constructor方法默認(rèn)返回實例對象(即this),完全可以指定返回另外一個對象 
   // 例如: return Object.create({name:"John",age:31})
    //  new一個類的實例的時候,會立即調(diào)用constructor方法
    constructor(name){
        this.name=name
        this.showName=function(){
            console.log(this.name)
        }
    }

    //  下面的這個sayHello方法它是不可枚舉的,這一點與es5不同
    //  需要注意的是,下面的這個sayHello方法不是定義在類Person上的,也不是定義在類的實例對象上的,
    // 而是定義在類Person的原型上的
    //  Person.hasOwnProperty('sayHello')  // false
    //  p1.hasOwnProperty('sayHello')  // false
    //  Person.prototype.hasOwnProperty('sayHello')  // true
    //  p1.__proto__hasOwnProperty('sayHello')  // true
    sayHello(){
            console.log('hello')
    }
}

var p1=new Person("xiao8")

console.log(Object.keys(p1))    
//  ["name", "showName"]  看到?jīng)]有,sayHello不可枚舉,constructor內(nèi)部定義的變量與方法可枚舉


//構(gòu)造函數(shù)的prototype屬性,在 ES6 的“類”上面繼續(xù)存在。   
//事實上,類的所有方法都定義在類的prototype屬性上面。
console.log(Person===Person.prototype.constructor)   //  true





//實現(xiàn)繼承

class Student extends Person{
    constructor(name,grade){
        //調(diào)用父類的構(gòu)造函數(shù),用來新建父類的this對象
        //super作為函數(shù)調(diào)用時,返回的是子類B的實例,super內(nèi)部的this指向B
        //super相當(dāng)于 A.prototype.constructor.call(this)
        //super作為函數(shù)只能用在constructor中
        super(name)
        this.grade=grade
    }

    toString() {
        //super作為對象使用時,指向父類的原型對象。
        //在靜態(tài)方法中指向父類
        //定義在父類實例上的方法是沒辦法用的
        return this.name + ' ' +super.sayHello();//調(diào)用父類的方法
    }
}


//可以使用getPrototypeOf方法來獲取父類
console.log(Object.getPrototypeOf(Student)===Person)   // true

//這里和es5不一樣
//對象有屬性__proto__,指向該對象的構(gòu)造函數(shù)的原型對象。
//方法除了有屬性__proto__,還有屬性prototype,prototype指向該方法的原型對象。

var p1 = new Person("Andy");
var s2 = new Student("小華",'三年級');

console.log(p1.__proto__ === p1.__proto__)// true
console.log(s2.__proto__.__proto__ === p1.__proto__) // true

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,625評論 18 399
  • Scala與Java的關(guān)系 Scala與Java的關(guān)系是非常緊密的!! 因為Scala是基于Java虛擬機(jī),也就是...
    燈火gg閱讀 3,606評論 1 24
  • 大家好,我是IT修真院萌新分院第3期的學(xué)員張曉琳,一枚正直、純潔、善良的前端程序員今天給大家分享一下,修真院官網(wǎng)j...
    Demon_0481閱讀 640評論 0 2
  • 這周主題下的內(nèi)容狠狠的撞進(jìn)了我的內(nèi)心,讓我更加清楚的看到了自己。 原來剖析自己,看見真實的自己,了解自己,雖然痛苦...
    遇見橙子閱讀 138評論 0 0
  • 1.肩膀的三角肌是把肩膀變寬的肌肉之一,分為前束后束和中束。介紹一下聯(lián)系三角肌前束的動作,其動作要領(lǐng)是:雙腳分開與...
    青木川_閱讀 1,084評論 0 0

友情鏈接更多精彩內(nèi)容