咱们几乎天天都在和this打交道,先不去管他的概念,看一个小栗子,预热一下javascript
⚠️约定:
由于使用let和const声明的变量没有挂载到全局变量下,因此在全局下声明的变量咱们使用var前端
var heroName = '黄蓉'
function hero() {
const heroName = "黄药师"
console.log(this)//window
console.log(this.heroName);//=>黄蓉
}
hero()
复制代码
咱们执行hero(),经过打印咱们发现java
this指向的是window全局,因此this.heroName,应当在全局下查找heroName,因此输出=>黄蓉
当全局下没有heroName呢?this指向的是全局,全局没有就是没有,那就是undefined数组
function hero() {
const heroName = "黄药师"
console.log(this)//window
console.log(this.heroName);//=>undefined
}
hero()
复制代码
咱们继续看一个栗子app
var heroName = '黄蓉'
function hero() {
const heroName = "黄药师"
console.log(this)//oHero
console.log(this.heroName);//=>欧阳锋
}
const oHero={
heroName:'欧阳锋',
hero
}
oHero.hero()
复制代码
提示:在声明oHero中的hero中,使用了ES6标准,当key和value同名是,能够像上面这样简写
上面的栗子中,调用hero()的环境变了 ,根据输出咱们发现,this的指向变了
this的指向是oHero对象,因此this.heroName=>欧阳锋,一样的当oHero下没有heroName,毫无悬念,就会输出=>undefined
若是前面两个小栗子搞懂了,咱们就继续
一个栗子函数
var heroName = '黄蓉'
function hero() {
console.log(this.heroName);
}
const oHero1 = {
heroName:'郭靖',
hero
};
hero()//=>黄蓉
oHero1.hero()//=>郭靖
复制代码
如今咱们尝试着给this下一个定义:学习
this指的是函数运行时所在的环境,若是一个函数内部有this,this就会指向一个对象,指向哪一个对象呢?取决于这个函数的执行环境。 补充(若是在全局下调用这个函数,则this指向全局,若是在某个对象下调用this,则this指向这个对象)ui
再看一个栗子this
var heroName = '黄蓉'
function hero1() {
const heroName = "黄药师"
this.hero2()
}
function hero2() {
console.log(this.heroName);
}
hero1()
复制代码
先不关心输出 咱们在hero2中的console.log前加一个debugger,分别看下两个this的指向 spa
能够发现。两个this均指向window 在脑中跑一遍这段代码,但愿你还清醒 咱们一块儿捋一下: 全局下执行hero1() 在hero1中,this指向的是全局=>window, 全局下存在hero2(), 在hero2中,this指向哪里?取决因而哪里调用的hero2(),是在hero1()中调用的hero2(), 因此hero2中的this指向hero1?输出=>黄药师? 可是咱们经过debugger得出,hero2中的this指向的是window,因此输出必然是=>黄蓉 为何呢? 咱们再看一个栗子
function hero(){
console.log(this.heroName);
}
var HERO1 = {
heroName : '黄蓉',
hero
}
var HERO2 = {
heroName : '郭靖',
HERO1
}
var HERO3 = {
heroName : '郭靖',
HERO2
}
HERO3.HERO2.HERO1.hero()//=>黄蓉
复制代码
我相信不会有人写出这样的代码,除了我! 这依然是个调用链 咱们在输出前打一个断点
做为一个函数调用this指向window,做为一个对象的方法调用,this指向当前调用的对象
回顾以前的栗子
var heroName = '黄蓉'
function hero() {
console.log(this.heroName);
}
const oHero1 = {
heroName:'郭靖',
hero
};
hero()//=>黄蓉
oHero1.hero()//=>郭靖
复制代码
hero(),是做为一个函数进行调用的,因此this指向window oHero1.hero(), 是做为一个函数的方法调用的,因此this指向oHero1对象
继续看一个栗子
function hero1(){
this.hero2()
}
function hero2(){
console.log(this.heroName);//Uncaught TypeError: this.hero2 is not a function
}
var HERO1 = {
heroName : '黄蓉',
hero1
}
var HERO2 = {
heroName : '郭靖',
HERO1
}
HERO2.HERO1.hero1()
复制代码
报错了
HERO1对象下调用的hero1(),因此hero1()中this指向的是HERO1,可是HERO1中不存在方法hero2(),因此报错 稍做修改
function hero1(){
HERO3.hero2()
}
function hero2(){
console.log(this.heroName);//=>郭靖
}
var HERO1 = {
heroName : '黄蓉',
hero1
}
var HERO2 = {
heroName : '郭靖',
HERO1
}
var HERO3 = {
heroName : '郭靖',
hero2
}
HERO2.HERO1.hero1()
复制代码
咱们彷佛总结出来一点小窍门,咱们从最后的输出反推执行顺序 是谁调用的hero2(),是HERO3,因此hero2,中的this指向HERO3,因此输出是=>郭靖
上面说了前两种状况,其实(1)是(2)的一种特殊状况, 即看成为一个函数调用的时候,就是做为window对象的一个方法进行调用
一个小栗子进行简单回顾
var heroName = "郭靖";
function hero1() {
console.log(this.heroName);
}
var HERO1 = {
heroName: "黄蓉",
hero1:hero1
};
HERO1.hero1();//=>黄蓉
hero1();//=>郭靖
复制代码
都是执行hero1()方法,一样都是输出this.heroName,却获得了不一样的结果
this的指向是在函数执行的时候定义的,而不是在函数建立时定义的
基于上面的栗子,咱们再看一个栗子
var heroObj = {
heroName: "郭靖",
heroFoo: {
heroName: "黄蓉",
hero: function() {
console.log(this.heroName);
}
}
};
heroObj.heroFoo.hero();//=>黄蓉
var h = heroObj.heroFoo.hero;
h();//=>undefined
复制代码
为了直观表达咱们作一下改动
//部分代码省略
window.heroObj.heroFoo.hero();//=>黄蓉
var h = heroObj.heroFoo.hero;
window.h();//=>undefined
复制代码
输出的结果是相同的,我要表达的内容也很就很直观明显了, 这些对象和方法,最终都会挂载到window对象下,咱们看这一句
window.heroObj.heroFoo.hero();//=>黄蓉
复制代码
hero()中的this是指向调用它的对象,那是哪一个对象调用的hero()呢?
这是个问题
这是个问题?
这不是个问题
this指向的是最后调用它的对象
上面的栗子
window.heroObj.heroFoo.hero();//=>黄蓉
复制代码
最后调用hero()的是heroFoo,因此hero()中this指向了heroFoo对象
var h = heroObj.heroFoo.hero;
window.h();//=>undefined
复制代码
最后调用h()的是window对象,因此hero()中this指向了window对象
综上
this的指向是在函数执行的时候定义的,而不是在函数建立时定义的,this指向的是最后调用它的对象
下面讨论剩下的两种状况
以前在介绍面向对象的时候,谈一谈javascript面向对象,讨论过,使用构造函数来建立对象
function Hero(name, nickname, skill) {
this.name = name;
this.nickname = nickname;
this.doSth = function() {
return skill
};
}
const hero = new Hero("黄药师", "东邪", "碧海潮生曲");
console.log(hero);
复制代码
使用自定义构造器构造一个对象须要四步
const hero = new Hero("黄药师", "东邪", "碧海潮生曲");
复制代码
hero.__proto__=Hero.prototype;
复制代码
说this指向hero,Hero内全部针对this的操做,都会发生在hero上面
console.log(hero.name);//=>黄药师
console.log(hero.nickname);//=>东邪
console.log(hero.doSth());//=>碧海潮生曲
复制代码
构造函数是不须要return的, return在普通函数中也不是必须存在的,咱们知道,在普通函数中,若是没有手动return ,会默认return undefined 可是若是在构造函数中使用了return,会存在一些坑 咱们一块儿来填坑 咱们把上面的栗子进行简化
function Hero(name) {
this.heroName = name;
return 123
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>郭靖
复制代码
function Hero(name) {
this.heroName = name;
return null
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>郭靖
复制代码
function Hero(name) {
this.heroName = name;
return undefined
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>郭靖
复制代码
再也不进行一一列举,上面return的都是基本数据类型 继续看 数组类型
function Hero(name) {
this.heroName = name;
return []
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>undefined
复制代码
object类型
function Hero(name) {
this.heroName = name;
return {
heroName: "黄蓉"
};
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>黄蓉
复制代码
funciton类型
function Hero(name) {
this.heroName = name;
return function(){}
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>undefined
复制代码
funciton升级
function Hero(name) {
this.heroName = name;
return (function() {
return {heroName:'黄蓉'}
})()
}
const hero = new Hero("郭靖");
console.log(hero.heroName);//=>黄蓉
复制代码
总结一下 构造函数会改版this的指向,指向经过new实例化出来的对象 构造函数中不须要return,当存在return时,如下几点须要注意
以前在讲面向对象的时候,对call进行过讨论,javascript 面向对象之一篇文章搞定call()方法 它们两个也是用来改变this的指向的,
为何要改变this的指向呢?
看一个栗子
const hero1 = {
name: "欧阳锋",
doSth: function (skill) {
console.log(`${this.name}学习${skill}`);
}
};
const hero2 = {
name: "洪七公"
};
hero1.doSth('九阴真经')//=>欧阳锋学习九阴真经
复制代码
执行了hero1对象下面的doSth方法,并传入参数“九阴真经”,最后输出=>欧阳锋学习九阴真经 结合上面的介绍讲解,咱们知道,doSth中的this是指向的hero1,这个没问题吧
好了,如今hero2下面有一个“洪七公”,“洪七公”也想调用hero1下面的doSth方法,怎么办呢?
const hero1 = {
name: "欧阳锋",
doSth: function (skill) {
console.log(`${this.name}学习${skill}`);
}
};
const hero2 = {
name: "洪七公"
};
hero1.doSth.call(hero2, "降龙十八掌");//=>洪七公学习降龙十八掌
复制代码
doSth是由hero1调用的,默认状况下doSth中的this指向的是hero1,可是使用了call,因此,this的指向变了,指向了call方法中的第一个参数hero2,
apply呢?apply和call非常相似
const hero1 = {
name: "欧阳锋",
doSth: function(skill, favourite) {
console.log(`${this.name}学习${skill}`);
console.log(`${this.name}喜欢${favourite}`);
}
};
const hero2 = {
name: "洪七公"
};
hero1.doSth.apply(hero2, ["降龙十八掌", "吃鸡"]);
hero1.doSth.call(hero2, "降龙十八掌", "吃鸡");
复制代码
这两种写法等效,只是传入的参数格式略有不一样
hero1.doSth.apply(hero2, ["降龙十八掌", "吃鸡"]);
hero1.doSth.call(hero2, "降龙十八掌", "吃鸡");
复制代码
bind也能够改变this的指向,在用法上和call和apply略有不一样,bind的使用更加灵活
const hero1 = {
name: "欧阳锋",
doSth: function(skill, favourite) {
console.log(`${this.name}学习${skill}`);
console.log(`${this.name}喜欢${favourite}`);
}
};
const hero2 = {
name: "洪七公"
};
const foo = hero1.doSth.bind(hero2, "降龙十八掌", "吃鸡");
foo();
复制代码
以前说的call和apply都是当即执行,而bind不会当即执行,须要手动执行,因此bind的使用更加灵活 不止于此 上面的foo方法在调用的时候能够额外的传入参数
const hero1 = {
name: "欧阳锋",
doSth: function(skill, favourite, q, n, b) {
console.log(`${this.name}学习${skill}`);//=>洪七公学习降龙十八掌
console.log(`${this.name}喜欢${favourite}`);//=>洪七公喜欢吃鸡
console.log(q, n, b);//=>1 2 3
}
};
const hero2 = {
name: "洪七公"
};
const foo = hero1.doSth.bind(hero2, "降龙十八掌", "吃鸡");
foo(1, 2, 3);
复制代码
我是陌上寒,咱们一块儿学前端
END