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)格模式下,則指定為null和undefined的this值會自動指向全局對象(瀏覽器中就是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指向的就是這個實例對象。
上面代碼中,s1是Student 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.name 和s1.age都能打印出來。
另外,特別需要注意的是, 使用關(guān)鍵字 new用來new一個實例的時候,會自動調(diào)用對象方法。 就是說在本例中,new Student("Andy",18) 的時候,會去調(diào)用 Student這個方法?。?!
在Student方法中call了一下,并且傳入了和Person方法中同樣的參數(shù) name、age是讓Student這個方法new出來的實例中都有了name和age這兩個屬性。
事實上,當(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
說明name和age這兩個屬性都是實例對象上的。
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出來的實例上就不會有父類的屬性和方法。
實際上name和age這兩個屬性,或者說父類上的方法,你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