今天一个朋友转给我一道题,让我帮忙解释解释。前端
当我看到题目的时候,第一眼以为贼简单,可是看提问越到后面越懵逼了,在琢磨着能不能猜对了…………web
var age = 10;
var person={
age:20,
getAge(){
var age = 30;
return this.age;
},
};
alert(age,age*2);
person.getAge();
var b = person.getAge;
b();
(person.getAge)();
(1,person.getAge)();
(1,person.getAge.bind(person))();
(person.getAge,person.getAge)();
(person.getAge=person.getAge)();
person.getAge.call();
person.getAge.call(person);
function getAge2() {
this.age = 40;
console.log(person.getAge());
};
getAge2();
console.log(age);
function getAge3(){
this.age = 50;
this.getAge4 = ()=>{
console.log(person.getAge.call(this));
}
}
new getAge3().getAge4();
console.log(age);
function getAge4(){
this.age = 60;
this.getAge5 = ()=>{
console.log(person.getAge.call(this));
}
}
new getAge4().getAge5();
console.log(age);
var age2 = 10;
var person2={
age2:20,
getAge2:()=>{
var age2 = 30;
return this.age2;
},
};
console.log(person2.getAge2.call());
console.log(person2.getAge2.call(person2));
复制代码
果不其然,我答错了好多……微信
这道题目,题简单的不能再简单了,就是对象,函数,变量,可是问的很深,没有扎实的知识,很难确切的回答上这些问题。app
要回答这些问题,关键仍是要深刻了解 this 和 逗号表达式。编辑器
首先咱们简单回顾下这两个很是重要的知识。最后再看看文末的综合题目。函数
this 在 js 中很是重要。在笔试的时候,若是有考察基础知识,那么出现 this 的几率那可不是通常的高。学习
一句话:谁调用就指向谁。ui
var person={
age:20,
getAge(){
var age = 30;
return this.age;
},
};
person.getAge(); // 20
复制代码
这个的 getAge 方法是 person 调用的,因此 this 指向 person,person.age 输出为 20;this
一句话:调用者指向谁,则指向谁。spa
var age = 10;
var person={
age:20,
getAge:()=>{
var age = 30;
return this.age;
},
};
person.getAge(); // 10
复制代码
这个的 getAge 方法是 person 调用的,则 getAge 和 person 的指向一致,person 是 window 调用的(参照上述普通函数),因此 person 指向 window,所以 getAge 也指向 window,输出 10。
一句话:你说指向谁就指向谁。
改变 this 指向,有 call,apply,bind 这几种方法。
var age = 10;
var person={
age:20,
getAge:function(){
var age = 30;
return this.age;
},
};
person.getAge.call(person);
复制代码
这里在执行 getAge 方法的时候,传入了 person,那么 getAge 的 this 指向 person,因此输出 20。
(http://www.ecma-international.org/ecma-262/#sec-abstract-operations)
逗号表达式 能够用于分割任何一个表达式,能够用于分割函数参数等。
function test(){
let a=1;
return ++a,a++,a++;
}
console.log(test());
复制代码
这里逗号用于分割表达式,等价于:
function test(){
let a=1;
++a;
a++;
return a++;
}
console.log(test());
复制代码
天然,答案不用多说,是 3 ,由于 return 后面的是 a++,若是是 ++a ,那结果是 4(这里不太明白的,自行学习 “++运算符”)。
接下来咱们来一一解答上面的问题,我再贴一下题目。
var age = 10;
var person={
age:20,
getAge(){
var age = 30;
return this.age;
},
};
复制代码
这里逗号分隔的是参数列表(Argument Lists),由于 alert 只接受一个参数,即第一个参数,后面的都忽略。所以弹窗是 10。
这个输出结果是 10,主要是要区别 person.getAge(),差异在于当前是先赋值给一个变量 b,而后执行 b()。
等价于:
var b = person.getAge;
window.b();
复制代码
回到最前面的知识点,[谁调用,指向谁],这里赋值变量以后,调用 b 方法的是 window,因此 this 指向 window,答案是 10。
这里括号只是起到一个分割的做用,并无实际意义,等价于 person.getAge()。因此答案是 20;
这里和上一个题目的差异是引入了逗号表达式。咱们知道逗号表达式返回的是最后一个值,即 person.getAge,注意这里是表达式返回值。
等价于:
var a = (1,person.getAge);
a();
复制代码
或者:
var a = (false||person.getAge);
a();
复制代码
显然,这里 a 调用方是 window,因此答案是 10。注意这里是非严格模式下。
后面题目若是牵涉到模式区别的时候一般都是指非严格模式,非严格模式与严格模式下的重要区别是当 this 为 null 或者 undefined 的时候,是否会改成指向 window。 非严格模式下会改成指向 window,严格模式就仍然为保持 null 或者 undefined。
"use strict";
var age = 10;
var person={
age:20,
getAge(){
var age = 30;
return this.age;
},
};
console.log(person.getAge());// 输出仍然是 20,this 指针指向 person
var a = (1,person.getAge);
a();// Cannot read property 'age' of undefined , this 是 undefined
复制代码
这道题是 逗号表达式 和 bind 知识点,参照上面的分析,等价于:
var a =(1,person.getAge.bind(person));
a();
复制代码
这里与上一道的区别是,返回的是一个 bind 以后的函数,a 方法已经强制指向 person 了,因此等价于:
person.getAge.bind(person)()
复制代码
答案为 20,this 指向 person。
这个其实就是一个逗号表达式,返回最后一个项的值,这里连续设置两个 person.getAge,只是一个陷阱,前面的 person.getAge 对结果没影响。咬定青山山不放松,坚决排除陷阱。
等价于:
(1,person.getAge)();
复制代码
答案与上面一致,为 10。
这里有点不同,意思为:对象 person,给它设置了一个属性 getAge(若是有 getAge 属性,则从新赋值),将这个属性 getAge 用 person.getAge 赋值。
括号里面是一个赋值表达式,表达式的返回值,就是这个从新被赋值了 person.getAge 的 person 对象下面的 getAge 属性。
等价于:
var person.getAge = person.getAge // 赋值
var a = person.getAge; // 表达式返回值
a();
复制代码
上面已经解释,因此输出为 10。
这里使用 call 函数改变 this 指针。在不传做用域参数的时候,在严格模式下 this 是 undefined,这里是非严格模式。
call,apply,bind 均可以改变 this 指针,以下:
// 10 call 为空,this 为 undefined,从新指向为 window
person.getAge.call();
// 20 this 指针指向 person
person.getAge.call(person);
复制代码
function getAge2() {
this.age = 40;
console.log(person.getAge());// 20
};
getAge2();
console.log(age);// 40
复制代码
里面的 person.getAge 仍然是谁调用执行谁,person.getAge 方法的 this 是指向 person,输出是 20。 可是里面有一个 this.age = 40 的赋值。
那么这个 this 是啥?固然也是谁调用指向谁,这里的 getAge2 是 window 调用,因此这里的 this 是 window。
那么这至关于将外层的 age 已经修改成 40 了,因此 console.log(age) ,已经变成 40 了。
function getAge3(){
this.age = 50;
this.getAge4 = ()=>{
console.log(person.getAge.call(this));// 50
}
}
new getAge3().getAge4();
console.log(age); // 40
复制代码
在 getAge3 里面,定义了一个公有方法 getAge4,可是这里是一个箭头函数,里面使用 call 修改函数 person.getAge 的 this 指针指向当前的 this,那么当前 this 指向哪里呢?
仍然是看谁调用了 getAge4,这里是 new getAge3() 这个实例调用了 getAge4,因此 call 传进去的 this 是指向 new getAge3(),这里 this.age=50 被赋值为 50 了,因此输出为 50。
可是全局的 age 并无被修改,与 题1 不同,这里的 this 指向了实例对象,并非指向 window,因此全局的 age 仍然为 40。
当读到这里的时候,你或许已经豁然开朗,以为很理解了,那么这一题你会作吗?
var age2 = 10;
var person2={
age2:20,
getAge2:()=>{
var age2 = 30;
return this.age2;
},
};
console.log(person2.getAge2.call()); // 10
console.log(person2.getAge2.call(person2)); // 10
复制代码
这里的核心差异是 getAge2 函数是使用了箭头函数。按上面理解的,箭头函数的 this 指向 person2 ?
错了!不是如此。
其实是箭头函数没有本身的 this,既然没有,那 call 怎么能改变它自身的 this 指针呢?
参照文档:(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)
因此上面不论是 call(),仍是 call(person2),都没法修改 this 指针,因此二者都输出全局的 10。
咱们再来看一道题,箭头函数的 this 。
var age = 10;
var person={
age:20,
child:{
age:40,
getAge:function(){
return this.age;
},
},
child2:{
age:40,
getAge:()=>{
return this.age;
},
},
child3:function(){
this.getAge = ()=>{
return this.age;
}
}
};
console.log(person.child.getAge());// 40
console.log(person.child2.getAge()); // 10
console.log((new person.child3()).getAge()); // undfined
复制代码
根据咱们上面的知识,能够知道 person.child.getAge() 的 this 就是谁调用指向谁,这里是 child 调用,因此指向 child ,输出 40 。
咱们知道,箭头函数是没有本身的 this,那么它的 this 是啥呢?
就是当前箭头函数逐级向上查找,找到函数做用域的 this,则为当前箭头函数的 this。
因此,这里 person.child2.getAge 函数的父级调用方是 child2,可是 child2 是对象,也没有本身的 this 和 做用域,因此继续向上查找 person,而后发现 person 也是对象,再继续向上查找,找到 window 这个大 Boss 了,因此 this 就指向 window ,输出为 10。
(new person.child3()).getAge() 的 this ,同理向上一级查找,发现 new person.child3() 是个函数实例,因此 this 指向 child3 的这个实例,然而 child3 实例没有 age 属性,因此输出 undefined。
这些题,确实很难!若是有不理解的,多看几篇解答,相信你必定会豁然开朗!或者关注公众号,加做者微信,一对一解答。
欢迎关注个人微信公众号,一块儿作靠谱前端!