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
网友评论