为了更好的看懂这篇文章,建议先要理解js对象的原型和原型链相关的知识,若不了解,可以看上一篇文章《js对象的构造函数与原型》
this的理解
- 在全局中使用this,this为window对象
- 谁调用了对象中属性,this就是谁
- this.xxx调用属性的时候,会沿着原型链查找xxx属性
案例如下:
var width = 200,name = 'window'
Object.prototype.color = 'red'
Object.prototype.findName = function(){
console.log(this.name)
}
Object.prototype.findWidth = function(){
console.log(this.width)
}
Person.prototype.height = 180
function Person(){
this.name = 'Person'
this.sayName = function(){
console.log(this.name)
}
this.sayHeight = function(){
console.log(this.height)
}
this.sayColor = function(){
console.log(this.color)
}
this.sayWidth = function(){
console.log(this.width)
}
this.getName = function(){
this.findName()
}
this.getWidth = function(){
this.findWidth()
}
}
var per = new Person()
per.sayName() //Person
per.sayHeight() //180
per.sayColor() //red
per.getName() //Person
per.sayWidth() //undeinfed
per.getWidth() //undeinfed
this.findWidth() //200
this.findName() //window
console.log(this.width) //200
控制台打印结果:
案例分析
per.sayName() //Person
分析:per为构造函数Person构造的对象,自身就带有name属性,所以per.sayName()方法中,console.log(this.name),this代表的就是per自身,this.name就是等于是per.name,即为“Person”.
per.sayHeight() //180
分析:per.sayHeight()中,console.log(this.height),this指的也是per,但是per对象自身没有height属性,但此时,会根据per对象的原型链开始寻找是否存在height属性,因为构造函数Person.prototype.height = 180,因此这里this.height会取Person原型中的height,即为180.
per.sayColor() //red
分析:per.sayColor()同理,打印的是this.color, this指得也是per,但是它自身没有color属性,他的原型也没有color属性,此时,会继续沿着原型链寻找,是否有color属性,最终在构造Object的原型中寻找到color属性,因此per对象也会继承该属性,因此this.color为 “red”.
per.getName() //Person
分析:在per.getName()中调用this.findName(),显然per对象是没有findName方法的,依然更具原型链寻找,在Object原型中找到findName()方法,所以per也能调用findName()方法,根据"谁调用,this就是谁的原则",此时this为per,因此findName()中的console.log(this.name),打印的即为per.name,所以输出结果为 “Peroson”.
per.sayWidth() //undeinfed
分析:per.sayWidth(),打印的是this.width,但是在per对象和他的原型链中,都没有找到width属性,因此打印的结果为undefined.
per.getWidth() //undeinfed
分析:根据原型链查找原则,per.getWidth()实际调用的是Object原型中的findWith()方法,this指的是per对象,但是在per对象以及他的原型链都没有找到width属性,因此findwith()中的console.log(this.width)的打印结果为undefined.
this.findWidth() //200
分析:根据“在全局中,this就是window”的原则,this.findWidth()即等于window.findWidth().
但是window对象没有findWidth()方法,同样会沿着原型链查找findWidth方法,最后在最终的构造函数Object对象原型中找到findWidth方法,所以window对象也能调用findWidth()方法。findWidth()方法中打印的是this.width,已知this即为window,所以等于window.width 即等于变量width,所以最终输出的结果为200.
this.findName() //window
分析:与上述分析同理,this.findName 中的 this.name等于 window.name 即为name变量,所以输出 “window”
console.log(this.width) //200
分析:this.width 等于 window.width 等于 变量width,所以输出200.
call和apply方法
- call和apply作用都是改变this
- call和appy方法区别在于他们的传参的形式不同:
call(对象,参数1,参数2,…参数n)
apply(对象,[参数1,参数2,…参数n])
先来个案例:
var width = 200
Object.prototype.color = 'red'
Object.prototype.findWidth = function(){
console.log(this.width)
}
function Person(){
this.sayWidth = function(){
console.log(this.width)
}
this.getWidth = function(){
this.findWidth()
}
this.callWidth = function(){
this.findWidth.call(window)
}
this.applyWidth = function(){
this.findWidth.apply(window)
}
}
var per = new Person()
per.sayWidth() //undeinfed
per.getWidth() //undeinfed
per.callWidth() //200
per.applyWidth() //200
控制台打印结果:
分析: per.sayWidth() ,per.getWidth() 这两个函数输出结果为什么undefined,由上述过程分析可知,因为per对象和其原型链中均无width属性,所以输出undefined.
per.callWidth(),per.applyWidth() 方法中分别调用了call和apply方法,传入的参数是window,此时等于this变成了window对象,所以在findWidth()方法中,的this.width等于是widonw.width即为200.
call和apply中传递参数
使用案例:
Object.prototype.findWidth = function(width,height,color,age){
this.width = width
this.height = height
this.color = color
this.age = age
}
function Person(){
this.callWidth = function(width,height){
this.findWidth.call(window,width,height)
}
this.applyWidth = function(color,age){
this.findWidth.apply(window,['','',color,age])
}
}
var pOne = new Person()
var pTwo = new Person()
pOne.callWidth(200,180)
console.log(width) //200
console.log(height) //180
pTwo.applyWidth("red",18)
console.log(color) //red
console.log(age) //18
分析:通过call和apply的方法把this变成window,然后按照规则传入参数,即实现了相当于在全局定义变量的功能。