博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript中this关键字
阅读量:5875 次
发布时间:2019-06-19

本文共 6319 字,大约阅读时间需要 21 分钟。

this一直是js中一个老生常谈的东西,但是我们究竟该如何来理解它呢?

在《JavaScript高级程序设计》中,对this的解释是:

this对象是在运行时基于函数的执行环境绑定的。

我们来逐字解读这句话:

  • this是一个对象
  • this的产生与函数有关
  • this与执行环境绑定

说通俗一点就是,“谁调用的这个函数,this就是谁”。


一、函数直接调用中的this

举个栗子:

var x = 1;function testThis() {    console.log(this.x);}testThis();  //1

js中有一个全局对象window,直接调用函数testThis时,就相当于调用window下的testThis方法,包括直接声明的变量也都是挂载在window对象下的。

var x = 1;function testThis() {    this.innerX = 10;    return 1;}testThis() === window.testThis();  // trueinnerX === window.innerX;  // truex === window.x;  // true

同理,在匿名函数中使用this也是指向的window,因为匿名函数的执行环境具有全局性。

(function () {    console.log(this); //window})();

但是呢,凡事都有例外,js的例外就是严格模式。在严格模式中,禁止this关键字指向全局对象。

(function () {    'use strict';    console.log(this); //undefined})();

二、对象方法调用中的this

再举个栗子:

var person = {    "name": "shenfq",    "showName": function () {        console.log(this.name);    } };person.showName(); // 'shenfq'

此时,showName方法中的this指向的是对象person,因为调用showName的是person对象,所以showName方法中的 this.name 其实就是 person.name。

但是如果我们换个思路,把showName方法赋值给一个全局变量,然后在全局环境下调用。

var name = 'global',    person = {        "name": "shenfq",        "showName": function () {            console.log(this.name);        }     },    showGlobalName = person.showName;showGlobalName(); // 'global'

可以看到,在全局环境中调用showName方法时,this就会指向window。

再换个思路,如果showName方法被其他对象调用呢?

var person = {        "name": "shenfq",        "showName": function () {            console.log(this.name);        }     },    animal = {        "name": "dog",        "showName": person.showName    };animal.showName(); // 'dog'

此时的name又变成了animal对象下的name,再复杂一点,如果调用方法的是对象下的一个属性,而这个属性是另个对象。

function showName () {    console.log(this.name);}var person = {    "name": "shenfq",    "bodyParts": {        "name": "hand",        "showName": showName    },    "showName": showName};person.showName(); // 'shenfq'person.bodyParts.showName(); // 'hand'

虽然调用showName方法的最源头是person对象,但是最终调用的是person下的bodyParts,所以方法写在哪个对象下其实不重要,重要的是这个方法最后被谁调用了,this指向的永远是最终调用它的那个对象。讲来讲去,this也就那么回事,只要知道函数体的执行上下文就能知道this指向哪儿,这个规则在大多数情况下都适用,注意是大多数情况,少部分情况后面会讲。

最后一个思考题,当方法返回一个匿名函数,这个匿名函数里面的this指向哪里?

var name = 'global',    person = {        "name": "shenfq",        "returnShowName": function () {            return function () {                console.log(this.name);            }        }     };person.returnShowName()(); // 'global'

答案一目了然,匿名函数不管写在哪里,只要是被直接调用,它的this都是指向window,因为匿名函数的执行环境具有全局性。


三、new构造函数中的this

还是先举个栗子:

function Person (name) {    this.name = name;}var global = Peson('global'),    xiaoming = new Person('xiaoming');console.log(window.name); // 'global'console.log(xiaoming.name); // 'xiaoming'

首先不使用new操作符,直接调用Person函数,这时的this任然指向window。当使用了new操作符时,这个函数就被称为构造函数。

所谓构造函数,就是用来构造一个对象的函数。构造函数总是与new操作符一起出现的,当没有new操作符时,该函数与普通函数无区别。

对构造函数进行new操作的过程被称为实例化。new操作会返回一个被实例化的对象,而构造函数中的this指向的就是那个被实例化的对象,比如上面例子中的xiaoming。

关于构造函数有几点需要注意:

  1. 实例化对象默认会有constructor属性,指向构造函数;
function Person (name) {    this.name = name;}var xiaoming = new Person('xiaoming');console.log(xiaoming.constructor); // Person
  1. 实例化对象会继承构造函数的原型,可以调用构造函数原型上的所有方法;
function Person (name) {    this.name = name;}Person.prototype = {    showName: function () {        console.log(this.name);    }};var xiaoming = new Person('xiaoming');xiaoming.showName(); // 'xiaoming'
  1. 如果构造函数返回了一个对象,那么实例对象就是返回的对象,所有通过this赋值的属性都将不存在
function Person (name, age) {    this.name = name;    this.age  = age;    return {        name: 'innerName'    };}Person.prototype = {    showName: function () {        console.log(this.name);    }};var xiaoming = new Person('xiaoming', 18);console.log(xiaoming); // {name: 'innerName'}

四、通过call、apply间接调用函数时的this

又一次举个栗子:

var obj = {    "name": "object"}function test () {    console.log(this.name);}test.call(obj);   // 'object'test.apply(obj);  // 'object'

与方法都是挂载在Function原型下的方法,所有的函数都能使用。

这两个函数既有相同之处也有不同之处:

  • 相同的地方就是它们的第一个参数会绑定到函数体的this上,如果不传参数,this默认还是绑定到window上。
  • 不同之处在于,call的后续参数会传递给调用函数作为参数,而apply的第二个参数为一个数组,数组里的元素就是调用函数的参数。

语言很苍白,我只好写段代码:

var person = {    "name": "shenfq"};function changeJob(company, work) {    this.company = company;    this.work    = work;};changeJob.call(person, 'NASA', 'spaceman');console.log(person.work); // 'spaceman'changeJob.apply(person, ['Temple', 'monk']);console.log(person.work); // 'monk'

有一点值得注意,这两个方法会把传入的参数转成对象类型,不管传入的字符串还是数字。

var number = 1, string = 'string';function getThisType () {    console.log(typeof this);}getThisType.call(number); //objectgetThisType.apply(string); //object

五、通过bind改变函数的this指向

最后举个栗子:

var name = 'global',    person = {        "name": "shenfq"    };function test () {    console.log(this.name);}test(); // globalvar newTest = test.bind(person);newTest(); // shenfq

bind方法是ES5中新增的,和call、apply一样都是Function对象原型下的方法-- Function.prototype.bind ,所以每个函数都能直接调用。bind方法会返回一个与调用函数一样的函数,只是返回的函数内的this被永久绑定为bind方法的第一个参数,并且被bind绑定后的函数不能再被重新绑定。

function showName () {    console.log(this.name);}var person = {"name": "shenfq"},    animal = {"name": "dog"};var showPersonName = showName.bind(person),    showAnimalName = showPersonName.bind(animal);showPersonName(); //'shenfq'showAnimalName(); //'shenfq'

可以看到showPersonName方法先是对showName绑定了person对象,然后再对showPersonName重新绑定animal对象并没有生效。

六、箭头函数中的this

真的是最后一个栗子:

var person = {    "name": "shenfq",    "returnArrow": function () {        return () => {            console.log(this.name);        }    }};person.returnArrow()(); // 'shenfq'

箭头函数是ES6中新增的一种语法糖,简单说就是匿名函数的简写,但是与匿名函数不同的是箭头函数中的this表示的是外层执行上下文,也就是说箭头函数的this就是外层函数的this。

var person = {    "name": "shenfq",    "returnArrow": function () {        let that = this;        return () => {            console.log(this == that);        }    }};person.returnArrow()(); // true

补充:

事件处理函数中的this:

var $btn = document.getElementById('btn');function showThis () {    console.log(this);}$btn.addEventListener('click', showThis, false);

点击按钮可以看到控制台打印出了元素节点。

事件结果

其实事件函数中的this默认就是绑定事件的元素,调用事件函数时可以简单理解为

$btn.showThis()

只要单击了按钮就会已这种方式来触发事件函数,所以事件函数中的this表示元素节点,这也与之前定义的“谁调用的这个函数,this就是谁”相吻合。

eval中的this:

eval('console.log(this)'); //windowvar obj = {    name: 'object',    showThis: function () {        eval('console.log(this)');    }}obj.showThis(); // obj

是一个可以动态执行js代码的函数,能将传入其中的字符串当作js代码执行。这个方法一般用得比较少,因为很危险,想想动态执行代码,什么字符串都能执行,但是如果用得好也能带来很大的便利。

eval中的this与箭头函数比较类似,与外层函数的this一致。

当然这只针对现代浏览器,在一些低版本的浏览器上,比如ie7、低版本webkit,eval的this指向会有些不同。

eval也可以在一些特殊情况下用来获取全局对象(window、global),使用 。


先写这么多,有需要再补充 ^ _ ^

参考:


转载地址:http://hskix.baihongyu.com/

你可能感兴趣的文章
TextKit简单示例
查看>>
网格最短路径算法(Dijkstra & Fast Marching)(转)
查看>>
最短路径算法-Dijkstra算法的应用之单词转换(词梯问题)
查看>>
软链接和硬链接详解
查看>>
HTML5 video 视频标签 常用属性
查看>>
深入理解javascript对象系列第一篇——初识对象
查看>>
Redis_master-slave模式
查看>>
qemu安装
查看>>
多媒体开发之rtmp---rtmp client 端的实现
查看>>
3.使用Maven构建Web项目
查看>>
iView实现自定义Modal
查看>>
如何在云帮上配置https
查看>>
JQuery干货篇之插入元素
查看>>
Imperva开源域目录控制器,简化活动目录集成
查看>>
可观察性驱动开发,探索未知之地
查看>>
Webpack构建兼容IE8
查看>>
Deis发布1.4版本,支持Microsoft Azure
查看>>
用Elm语言降低失败的风险
查看>>
荷兰商业银行使用精益领导力推行改进
查看>>
cisco 多生成树MST笔记
查看>>