JavaScript中this的全面解析及常见实例


Posted in Javascript onMay 14, 2019

前言

this 关键字在 Javascript 中非常常见,但是很多开发者很难说清它到底指向什么。大部分人会从字面意思上去理解 this,认为 this 指向函数自身,实际上this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调
用时的各种条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

总结: 函数被调用时发生 this 绑定,this 指向什么完全取决于函数在哪里被调用。

一、this 的绑定规则

this 一共有 4 中绑定规则,接下来一一介绍每种规则的解释和规则直接的优先级

  • 默认绑定(严格/非严格模式)
  • 隐式绑定
  • 显式绑定
  • new 绑定

1.1 默认绑定(严格/非严格模式)

  • 独立函数调用: 独立函数调用时 this 使用默认绑定规则,默认绑定规则下 this 指向 window(全局对象)。
  • 严格模式下: this 无法使用默认绑定,this 会绑定到 undefined。

独立函数调用

function foo() {
 console.log(this.a);
}
var a = 2;
foo(); // 2

严格模式下:

function foo() {
 "use strict";
 console.log(this); //undefined
 console.log(this.a); //Uncaught TypeError: Cannot read property 'a' of undefined
}
var a = 2;
foo();

注意下边两种情况

var age = "18";
var obj = {
 name: "heyushuo",
 age: 25,
 fn: function() {
 function sayName() {
  console.log(this); //window
  console.log(this.age); //undefined
 }
 sayName();
 }
};
obj.fn();

函数 sayName 虽然是在 obj.fn 内部定义的,但是它仍然是一个独立函数调用,this 仍然指向 window。

var a = "global";
var obj = {
 a: 2,
 foo: function() {
 console.log(this.a); //global
 }
};
var bar = obj.foo; // 函数别名!
bar();

虽然 bar 是 obj.foo 的一个引用,但是实际上,它引用的是函数本身,因此此时的
bar() 其实是一个不带任何修饰的独立函数调用,因此应用了默认绑定。

1.2 隐式绑定

当函数引用有上下文对象时(例如:obj.foo 这个时候使用 obj 上下文来引用函数 foo),隐式绑定规则会把函数中的 this 绑定到这个上下文对象。

var obj = {
 name: "heyushuo,
 foo: function() {
 console.log(this.name); //heyushuo
 }
};

obj.foo();

对象属性引用链中只有上一层或者说最后一层在调用中起作用。

var obj = {
 name: "heyushuo",
 obj1: {
 name: "kebi",
 foo: function() {
  console.log(this.name); // kebi
 }
 }
};

obj.obj1.foo();

隐式丢失

被隐式绑定的函数会丢失绑定对象,而应用默认绑定,把 this 绑定到全局对象或者 undefined(严格模式) 上。

第一种

var a = "global";
var obj = {
 a: 2,
 foo: function() {
 console.log(this.a); //global
 }
};
var bar = obj.foo; // 函数别名!
bar();

虽然 bar 是 obj.foo 的一个引用,但是实际上,它引用的是函数本身,因此此时的bar() 其实是一个不带任何修饰的独立函数调用,因此应用了默认绑定。

第二种传入回调函数时:

var a = "global";
var obj = {
 a: 2,
 foo: function() {
 console.log(this.a); //global
 }
};
var bar = obj.foo; // 函数别名!
function doFoo(fn) {
 fn(); // <-- 调用位置!
}
doFoo(bar); //global

//和下边这种一样
setTimeout(obj.foo, 300);

1.3 显示绑定

通过 call() 或者 apply()方法。第一个参数是一个对象,在调用函数时将这个对象绑定到 this 上,称之为显示绑定。

function foo() {
 console.log(this.a);
}
var obj = {
 a: 2
};
foo.call(obj); // 2

显示绑定引申出来一个硬绑定,代码如下

function foo(something) { 
 console.log( this.a, something ); 
 return this.a + something;
}
// 简单的辅助绑定函数
function bind(fn, obj) { 
 return function() {
 return fn.apply( obj, arguments ); //内部已经强制绑定了传入函数this的指向
 };
}
var obj = { 
 a:2
};
var bar = bind( foo, obj ); 
var b = bar( 3 ); // 2 3
console.log( b ); // 5

bar函数无论如何调用,它总会手动在 obj 上调用 fn,强制把 fn 的 this 绑定到了 obj。这样也解决前面提到的丢失绑定问题
由于硬绑定是一种非常常用的模式,所以在 ES5 中提供了内置的方法 Function.prototype.bind

function foo(something) { 
 console.log( this.a, something ); 
 return this.a + something;
}
var obj = { 
 a:2
};
var bar = foo.bind( obj );
var b = bar( 3 ); // 2 3 
console.log( b ); // 5

1.4 new绑定

使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

  • 创建(或者说构造)一个全新的对象。
  • 这个新对象会被执行 [[ 原型 ]] 连接。
  • 这个新对象会绑定到函数调用的 this。
  • 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。

例如:

function foo() { 
 this.name = "heyushuo";
 this.age = 25
} 
foo.prototype.sayName = function(){
 console.log(this.name+this.age);
}
var bar = new foo();

console.log(bar); //{name: "heyushuo", age: 25}
//这个新对象会绑定到函数调用的 this。所以此时的this就是bar对象
console.log( bar.age ); // 25

如下图是 new foo() 这个对象

JavaScript中this的全面解析及常见实例

二、四种绑定关系的优先级

判断this,可以按照下面的顺序来进行判断:

1、函数是否在 new 中调用(new 绑定)?如果是的话 this 绑定的是新创建的对象。

var bar = new foo()

2、函数是否通过 call、apply(显式绑定)或者硬绑定调用?如果是的话,this 绑定的是指定的对象。

var bar = foo.call(obj2)

3、函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this 绑定的是那个上下文对象。

var bar = obj1.foo()

4、如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到 undefined,否则绑定到全局对象。

var bar = foo()

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
javascript奇异的arguments分析
Oct 20 Javascript
js清空表单数据的两种方式(遍历+reset)
Jul 18 Javascript
Javascript基础教程之argument 详解
Jan 18 Javascript
JS实现不规则TAB选项卡效果代码
Sep 16 Javascript
如何利用JS通过身份证号获取当事人的生日、年龄、性别
Jan 22 Javascript
MUI实现上拉加载和下拉刷新效果
Jun 30 Javascript
vue几个常用跨域处理方式介绍
Feb 07 Javascript
JS获取子节点、父节点和兄弟节点的方法实例总结
Jul 06 Javascript
微信小程序生成分享海报方法(附带二维码生成)
Mar 29 Javascript
vue指令之表单控件绑定v-model v-model与v-bind结合使用
Apr 17 Javascript
利用原生JS实现欢乐水果机小游戏
Apr 23 Javascript
.netcore+vue 实现压缩文件下载功能
Sep 24 Javascript
jquery 验证用户名是否重复代码实例
May 14 #jQuery
记录vue项目中遇到的一点小问题
May 14 #Javascript
javascript中如何判断类型汇总
May 14 #Javascript
详解如何探测小程序返回到webview页面
May 14 #Javascript
JQuery获取元素尺寸、位置及页面滚动事件应用示例
May 14 #jQuery
javascript实现遮罩层动态效果实例
May 14 #Javascript
JQuery animate动画应用示例
May 14 #jQuery
You might like
咖啡因含量是由谁决定的?低因咖啡怎么来?低因咖啡适合什么人喝
2021/03/06 新手入门
PHPCMS的使用小结
2010/09/20 PHP
php实现分页工具类分享
2014/01/09 PHP
微信公众号支付之坑:调用支付jsapi缺少参数 timeStamp等错误解决方法
2016/01/12 PHP
php实现和c#一致的DES加密解密实例
2017/07/24 PHP
PHP生成随机数的方法总结
2018/03/01 PHP
Laravel框架实现利用监听器进行sql语句记录功能
2018/06/06 PHP
利用google提供的API(JavaScript接口)获取网站访问者IP地理位置的代码详解
2010/07/24 Javascript
扩展jQuery对象时如何扩展成员变量具体怎么实现
2014/04/25 Javascript
jQuery设置指定网页元素宽度和高度的方法
2015/03/25 Javascript
分享12个实用的jQuery代码片段
2016/03/09 Javascript
JavaScript制作简单分页插件
2016/09/11 Javascript
简单理解vue中track-by属性
2016/10/26 Javascript
如何学JavaScript?前辈的经验之谈
2016/12/28 Javascript
Javascript 实现全屏滚动实例代码
2016/12/31 Javascript
Nodejs进阶之服务端字符编解码和乱码处理
2017/09/04 NodeJs
JS分页的实现(同步与异步)
2017/09/16 Javascript
利用JS测试目标网站的打开响应速度
2017/12/01 Javascript
关于微信小程序map组件z-index的层级问题分析
2019/07/09 Javascript
微信小程序的授权实现过程解析
2019/08/02 Javascript
react组件基本用法示例小结
2020/04/27 Javascript
openlayers 3实现车辆轨迹回放
2020/09/24 Javascript
简单介绍Python中的RSS处理
2015/04/13 Python
浅谈Python爬取网页的编码处理
2016/11/04 Python
python opencv3实现人脸识别(windows)
2018/05/25 Python
python利用百度AI实现文字识别功能
2018/11/27 Python
对Python3中dict.keys()转换成list类型的方法详解
2019/02/03 Python
Python变量作用域LEGB用法解析
2020/02/04 Python
Django高并发负载均衡实现原理详解
2020/04/04 Python
CSS 3.0文字悬停跳动特效代码
2020/10/26 HTML / CSS
墨西哥网上超市:Superama
2018/07/10 全球购物
网络技术支持面试题
2013/04/22 面试题
北京大学自荐信范文
2014/01/28 职场文书
《美丽的田园》教学反思
2014/03/01 职场文书
听证通知书
2015/04/24 职场文书
旅行社计调工作总结
2015/08/12 职场文书