this是什么
this是一个指针,并且永远指向一个对象,对于非箭头函数来说
,具体指向哪个对象是在运行时基于函数的执行环境动态绑定的。对于箭头函数来说
,具体指向哪个对象是在箭头函数申明时的环境确定的。
this的使用场景--非箭头函数
this
的常用使用场景无非就是下面几种
- 作为对象的方法调用
- 作为普通函数调用
- 作为构造函数调用
- 作为事件的回调函数调用
- Function.prototype.call和Function.prototype.apply调用
1 作为对象的方法调用
例子
<script type = "text/javascript">
// 作为对象的方法调用
var userName = "wenyaoyao"
var obj = {
userName:"longhanghang",
getName:function(){
return this.userName;
}
}
console.log(obj.getName());
//输出 longhanghang
</script>
以上代码运行之后输出 longhanghang
结论
当函数作为对象方法被调用时,this指向该对象。
衍生
如果把对象里面的函数赋值给一个用var
定义的全局变量,然后再在全局下面执行会产生什么样的火花呢?
<script type = "text/javascript">
// 作为对象的方法调用
var userName = "wenyaoyao"
var obj = {
userName:"longhanghang",
getName:function(){
return this.userName;
}
}
var getNameFun = obj.getName;
console.log(getNameFun());
//输出 wenyaoyao
</script>
以上代码运行之后输出 wenyaoyao
刨析:因为此时getNameFun是在全局作用域下面执行的,所以this指向window。
2 作为普通函数调用
例子
<script type="text/javascript">
// 作为普通函数调用
var userName = 'wenyaoyao';
function getName() {
console.log(this.userName);
}
getName();
//输出 wenyaoyao
</script>
以上代码运行之后输出 wenyaoyao
结论
当函数直接被调用时this指向全局对象,游览器里面是window
,nodejs里面是global
。
衍生
1 全局环境下使用var关键字定义的变量会直接挂靠到window上,如果是使用let和const呢?
<script type="text/javascript">
// 作为普通函数调用
let userName = 'wenyaoyao';
function getName() {
console.log(this.userName);
}
getName();
//输出 undefined
</script>
以上代码运行之后输出 undefined
刨析: 可以发现let和const定义的变量并不会挂靠在window上,然后此时this指向的是window,所以打印的时候输出undefined。
3 作为构造函数调用
例子
<script type="text/javascript">
// 作为构造函数调用
function Person() {
this.userName = 'longhanghang';
//return this;
}
let person1 = new Person();
console.log(person1.userName);
//输出 longhanghang
</script>
以上代码运行后输出longhanghang
结论
当使用new运算符调用函数时总是会返回一个对象,this指向这个对象。这也就是大名鼎鼎的构造函数。
衍生
1 函数内部怎么区分一个函数的调用类型呢?
<script type="text/javascript">
// 函数内判断调用类型
let obj = {};
function gudgeType() {
if (this instanceof gudgeType) {
console.log('构造函数调用');
} else {
console.log('其他方式调用');
}
}
gudgeType();
new gudgeType();
// 输出其他方式调用
// 构造函数调用
</script>
以上代码运行后依次输出 输出其他方式调用
,构造函数调用
刨析: 我们可以巧妙的在函数内部使用instanceof
对函数的调用方式进行判断。
4 作为事件的回调函数使用
例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>作为事件的回调函数使用</title>
</head>
<body>
<div id = "test">点击我</div>
</body>
<script type="text/javascript">
//作为事件的回调函数使用
var testDom = document.querySelector("#test");
test.onclick = function(e){
console.log(this)
}
</script>
</html>
// 输出 <div id = "test">点击我</div>
以上代码运行后输出 <div id = "test">点击我</div>
结论
作为事件的回调函数调用时,this指向绑定事件的dom对象。
衍生
如果在事件的回调函数里面咱们再定义一个函数,函数内部的this又会指向哪里呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>作为事件的回调函数使用</title>
</head>
<body>
<div id = "test">点击我</div>
</body>
<script type="text/javascript">
//作为事件的回调函数使用
var testDom = document.querySelector("#test");
test.onclick = function(e){
var text = function(){
console.log(this);
}
text();
}
</script>
</html>
// 输出结果为window
以上代码运行后输出 window
刨析:此时的text是作为普通函数被调用的,所以指向windw。
5. Function.prototype.call和Function.prototype.apply调用
1 Function.prototype.call
<script type="text/javascript">
//Function.prototype.call()
function getName(name, age) {
console.log(this.name, name, age);
}
var obj = { name: 'longhanghang' };
getName.call(obj,'wenyaoyao',11);
//输出为 longhanghang wenyaoyao 11
</script>
1 Function.prototype.apply
<script type="text/javascript">
//Function.prototype.apply()
function getName(name, age) {
console.log(this.name, name, age);
}
var obj = { name: 'longhanghang' };
getName.apply(obj,['wenyaoyao',11]);
//输出为 longhanghang wenyaoyao 11
</script>
以上代码最终输出都为longhanghang wenyaoyao 11
结论
执行一个函数我们通常都是采用的后面加()
的方式,例如有名为 getName
这样的一个函数,我们通常会 使用getName()
进行调用,这样调用的方式this
的指向会遵循我们前面说的四种情况,但是如果我们不这样调用,而使用call
和apply
方法来调用时,那么this
的指向取决于我们传的第一个参数,正如上面的示例,第一个参数我们传递的obj
,那么函数内部的this指向的就是这个obj
,后面的参数是函数执行时需要的参数,apply
方法跟call
方法作用差不多,只是后面的函数参数形式不同。
衍生
如果我们将函数作为对象的方法调用,然后再使用call
或者apply
时,会擦出怎么样的火花呢?
<script type="text/javascript">
//Function.prototype.apply()
var obj1 = {
name: 'longhanghang',
getName: function () {
console.log(this.name);
},
};
var obj2 = { name: 'wenyaoyao' };
obj1.getName.call(obj2);
//输出为 wenyaoyao
</script>
以上代码输出为wenyaoyao
刨析:当我们使用了call
或者apply
执行函数时,优先级会高于作为对象的方法调用
this的使用场景--箭头函数
1 作为对象的方法调用
例子
<script type="text/javascript">
// 作为对象的方法调用
var userName = 'wenyaoyao';
var obj = {
userName: 'longhanghang',
getName: () => {
return this.userName;
},
};
console.log(obj.getName());
//输出 wenyaoyao
</script>
以上代码运行之后输出 wenyaoyao
。
结论
this
指向被申明时的环境,因为getName
被申明时实在全局环境下的,所以这里的this
指向window
。
衍生1
如果把对象里面的函数赋值给一个用var
定义的全局变量,然后再在全局下面执行会产生什么样的火花呢?
<script type="text/javascript">
// 作为对象的方法调用
var userName = 'wenyaoyao';
var obj = {
userName: 'longhanghang',
getName: () => {
return this.userName;
},
};
var getNameFun = obj.getName;
console.log(getNameFun());
//输出 wenyaoyao
</script>
以上代码运行之后输出 wenyaoyao
刨析:当箭头函数被申明
的时候,this
指向的是window
。
衍生2
如果箭头函数里面嵌套一个箭头函数呢?
<script type="text/javascript">
// 作为对象的方法调用
var userName = 'wenyaoyao';
var obj = {
userName: 'longhanghang',
getName: () => {
var getFirstName = () => {
return this.userName
}
return getFirstName;
},
};
var getNameFun = obj.getName();
console.log(getNameFun());
//输出 wenyaoyao
</script>
以上代码输出结果为wenyaoyao
刨析:当getFirstName
申明的时候是在getName
的这个作用域里面,this指向的就是getName
指向的this
,而getName
在申明的时候this
指向的是window
,所以结论就是getName
种的this
也是指向window
。
2 作为普通方法调用
例子
<script type = "text/javascript">
let getName = () => {
console.log(this)
}
getName();
</script>
//输出是window
以上代码输出的结果是window
结论
this
指向被申明时的环境,因为getName
被申明时实在全局环境下的,所以这里的this
指向window
。
3 作为构造函数调用
例子
<script type = "text/javascript">
var getName = () => {
console.log(this)
}
var aa = new getName();
//报错 Uncaught TypeError: getName is not a constructor
</script>
以上代码会报错 Uncaught TypeError: getName is not a constructor
结论
箭头函数不能作为构造函数调用。
4 作为事件的回调函数调用
例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>作为事件的回调函数使用</title>
</head>
<body>
<div id = "test">点击我</div>
</body>
<script type="text/javascript">
//作为事件的回调函数使用
var testDom = document.querySelector("#test");
test.onclick = () => {
console.log(this)
}
</script>
//输出为window
</html>
以上代码输出为window
结论
this
指向被申明时的环境,因为回调函数在申明时的环境是window,所以this指向window
。
5. Function.prototype.call和Function.prototype.apply调用
例子
<script type = "text/javascript">
var getName = () => {
console.log(this)
}
getName.call(Object)
getName.apply(Object)
// 输出为window
</script>
以上代码输出为 window
结论
Function.prototype.call和Function.prototype.apply无法改变箭头函数this
的指向,this
的指向还是在申明时就决定了。因为申明时的环境时window
,所以this
指向window
而并不是指向构造函数Object
。
箭头函数和非箭头函数的区别
- 箭头函数长得帅,非箭头函数长得漂亮
- 箭头函数都是匿名函数,非箭头函数可以是匿名函数也可以是具名函数
- 箭头函数不能使用
new
关键字,非箭头函数可以 - 箭头函数没有
prototype
属性,非箭头函数有 - 箭头函数不绑定
arguments
,非箭头函数要绑定 - 箭头函数无法使用
call
和apply
修改的this
的指向,非箭头函数可以 - 箭头函数的
this
在申明时决定,非箭头函数的this
在执行时确定
自己实现call,apply,bind
前面用了这么多call和apply,那么咱们来点干货,自己手写一下这几个函数
1 call
<script type="text/javascript">
Function.prototype.selfCall = function () {
var args = arguments; //获取到实际参数
//类型判断
if (Object.prototype.toString.call(this) !== '[object Function]') {
throw new Error('必须使用函数调用该方法');
}
var realThis = Array.prototype.shift.call(args) || window; //截取出第一个参数也就是自定义this指向的这个对象
var funName = Symbol('funName'); //定义一个Symbol类型的属性名
realThis[funName] = this; //将方法赋值给对象的funName属性
var res = realThis[funName](...args); // 将此方法作为传递进来的对象的方法调用,此时this已经指向我们传递进来的这个对象
delete realThis[funName]; //删除这个对象上的临时属性
return res;
};
function getName(aa) {
console.log(this);
}
let obj = {};
getName.selfCall(obj, 11);
//输出obj这个对象
</script>
最终输出的是obj
对象和11
2 apply
<script type="text/javascript">
Function.prototype.selfCall = function () {
//类型判断
if (Object.prototype.toString.call(this) !== '[object Function]') {
throw new Error('必须使用函数调用该方法');
}
var realThis = arguments[0] || window; //截取出第一个参数也就是自定义this指向的这个对象
var args = arguments[1]; //取出剩下的参数作为函数运行的参数
var funName = Symbol('funName'); //定义一个Symbol类型的属性名
realThis[funName] = this; //将方法赋值给对象的funName属性
var res = realThis[funName](...args); // 将此方法作为传递进来的对象的方法调用,此时this已经指向我们传递进来的这个对象
delete realThis[funName]; //删除这个对象上的临时属性
return res;
};
function getName(aa, bb) {
console.log(this, aa, bb);
}
let obj = {};
getName.selfCall(obj, [11, 22]);
//输出obj这个对象和11
</script>
bind
bind
和call
以及apply
有一点区别就是函数调用bind
后会返回一个新的this
指向的函数,而使用call
和apply
是改成this
指向并执行
<script type="text/javascript">
Function.prototype.selfCall = function () {
//类型判断
if (Object.prototype.toString.call(this) !== '[object Function]') {
throw new Error('必须使用函数调用该方法');
}
var args = arguments; //获取到参数
var realThis = [].shift.call(args) || window; //截取出第一个参数也就是自定义this指向的这个对象
var funName = Symbol('funName');//定义一个Symbol类型的属性名
realThis[funName] = this;//将方法赋值给对象的funName属性
//返回一个匿名函数,匿名函数中去执行我们要改变this指向的函数
return function () {
var diaoyongArgs = arguments;//获取到内层函数的参数
let allArgs = [...args, ...diaoyongArgs];//合并参数
let ret = realThis[funName](...allArgs);//将此方法作为传递进来的对象的方法调用,此时this已经指向我们传递进来的这个对象
delete realThis[funName];//方法执行完后,删除这个对象上的临时属性
return ret;
};
};
function getName() {
console.log(this, ...arguments);
}
let obj = {};
getName.bind(obj, 11, 22)(33);
//输出 {} 11 22 33
</script>
以上代码最终输出 {} 11 22 33
总结
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!