call方法
MDN关于call方法的解释
call() 方法调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
注意:该方法的作用和
apply()方法类似,只有一个区别,就是call()方法接受的是若干个参数的列表,而apply()方法接受的是一个包含多个参数的数组。
例子:
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, ...)
参数
thisArg
在
fun函数运行时指定的this值。
需要注意的是,指定的
this值并不一定是该函数执行时真正的this值,如果这个函数处于非严格模式下,则指定为null和undefined的this值会自动指向全局对象(浏览器中就是window对象),同时值为原始值(数字,字符串,布尔值)的this会指向该原始值的自动包装对象。
arg1, arg2, ...
指定的参数列表
返回值
返回值是你调用的方法的返回值,若该方法没有返回值,则返回undefined。
描述
可以让
call()中的对象调用当前对象所拥有的function。你可以使用call()来实现继承:写一个方法,然后让另外一个新的对象来继承它(而不是在新对象中再写一次这个方法)。
现在再来看一下上面的那个例子
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
在这个例子中,函数Person里面有this,这个this 指向的是什么要看你是怎么调用的,
如果你直接Person()直接这样调用,那么这个this指向的就是全局对象window
如果说你作为函数Person new出来的一个实例来调用,那么这个this指向的就是这个实例对象。
上面代码中,s1是Student new出来的一个实例,那么这个实例在使用的时候,Student内部的this指向的就是这个实例对象.
那么Student这个函数里面的this指向的就是实例对象s1,Person.call(this,name,age)是一个立即执行的函数,意思是说执行这个Person函数,并且这个函数内部的this指向s1这个实例对象。
最上面的function Person(){}这个函数内部的this指向的就是s1,结合 this.name=name; this.age=age就可以看出s1的name属性就是参数name,age属性就是参数age
所以打印s1.name 和s1.age都能打印出来。
另外,特别需要注意的是, 使用关键字 new用来new一个实例的时候,会自动调用对象方法。 就是说在本例中,new Student("Andy",18) 的时候,会去调用 Student这个方法!!!
在Student方法中call了一下,并且传入了和Person方法中同样的参数 name、age是让Student这个方法new出来的实例中都有了name和age这两个属性。
事实上,当你尝试打印 Person.hasOwnProperty('age') Student.hasOwnProperty('age')结果都会是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属性实际上是一个一个传进去的。 而apply方法是可以以数组的形式一下子全放进去。
上面的例子换成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() 方法是用来创建一个新的对象,该对象的原型指向括号里面的参数
Student.prototype=Object.create(Person.prototype)
// 上面的这条语句是说,创建一个新的对象,该对象的原型指向Person.prototype,
并将它赋值给Student.prototype
// 再设置 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中这种最简单的继承,实质上就是将子类的原型设置为父类的实例。
需要经过
1.定义祖先
2.定义祖先可继承的变量/方法
3.定义继承的类(构造函数),并在类中调用组件的方法 call和apply方法
4.使用 prototyoe定义继承关系
5.重新将constructor指向自己
1. 为什么在子类上需要call一下?
答:如果不call,用子类new出来的实例上就不会有父类的属性和方法。
实际上name和age这两个属性,或者说父类上的方法,你call了之后,等于说是在子类的实例上有了父类的属性和方法,子类本身是没有这些个属性和方法的。 不信你call了之后打印 Student.prototype.hasOwnProperty("age") 一定是 false的!
2. 为什么需要Object.create()?
答:新创建对象的原型对象
3. 为什么需要Student.prototype.constructor=Student?
答:重新将constructor指向自己
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.call(this))。
上面这句话就很好理解了。
es6的继承
// es6的继承
//定义类
class Person {
// constructor方法,就是构造方法
// 如果没有定义构造方法,js会自动为其添加
// constructor方法默认返回实例对象(即this),完全可以指定返回另外一个对象
// 例如: return Object.create({name:"John",age:31})
// new一个类的实例的时候,会立即调用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"] 看到没有,sayHello不可枚举,constructor内部定义的变量与方法可枚举
//构造函数的prototype属性,在 ES6 的“类”上面继续存在。
//事实上,类的所有方法都定义在类的prototype属性上面。
console.log(Person===Person.prototype.constructor) // true
//实现继承
class Student extends Person{
constructor(name,grade){
//调用父类的构造函数,用来新建父类的this对象
//super作为函数调用时,返回的是子类B的实例,super内部的this指向B
//super相当于 A.prototype.constructor.call(this)
//super作为函数只能用在constructor中
super(name)
this.grade=grade
}
toString() {
//super作为对象使用时,指向父类的原型对象。
//在静态方法中指向父类
//定义在父类实例上的方法是没办法用的
return this.name + ' ' +super.sayHello();//调用父类的方法
}
}
//可以使用getPrototypeOf方法来获取父类
console.log(Object.getPrototypeOf(Student)===Person) // true
//这里和es5不一样
//对象有属性__proto__,指向该对象的构造函数的原型对象。
//方法除了有属性__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