JS-this关键字
1、定义
this是Javascript语言的一个关键字。
它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。
随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用函数的那个对象。
function test(){ this.x = 1; }
2、用法
2.1 this 和构造器
this 本身就是类定义时构造器里需要用到的,和构造器在一起再自然不过。
function Tab(nav, content) { this.nav = nav this.content = content } Tab.prototype.getNav = function() { return this.nav; }; Tab.prototype.setNav = function(nav) { this.nav = nav; }; Tab.prototype.add = function() { };
2.2 this 和对象
这时this就指这个上级对象。
var tab = { nav: '', content: '', getNav: function() { return this.nav; }, setNav: function(n) { this.nav = n; } }
2.3 this 和函数
首先,this 和独立的函数放在一起是没有意义的,前面也提到过 this 应该是和面向对象相关的。纯粹的函数只是一个低级别的抽象,封装和复用。如下:
function showMsg() { alert(this.message) } showMsg() // undefined
定义 showMsg,然后以函数方式调用,this.message 是 undefined。因此坚决杜绝在 纯函数内使用 this,但有时候会这么写,调用方式使用 call/apply:
function showMsg() { alert(this.message) } var m1 = { message: '输入的电话号码不正确' } var m2 = { message: '输入的身份证号不正确' } showMsg.call(m1) // '输入的电话号码不正确' showMsg.call(m2) // '输入的身份证号不正确'
用这种方式可以节省一些代码量,比如当两个 类/对象 有一共相似的方法时,不必写两份,只要定义一个,然后将其绑定在各自的原型和对象上。这时候其实你还是在使用对象或类(方式1/2),只是间接使用罢了。
2.4 全局环境的 this
前面提到this指的是,调用函数的那个对象。全局环境中有不同的宿主对象,浏览器环境中是 window, node 环境中是 global。这里重点说下浏览器环境中的 this。
浏览器环境中非函数内 this 指向 window。
浏览器中比较坑人,非函数内直接使用 var 声明的变量默认为全局变量,且默认挂在 window 上作为属性。
var andy = '刘德华' alert(andy === window.andy) // true alert(andy === this.andy) // true alert(window.andy === this.andy) // true
例如笔试题:
var x = 10; function func() { alert(this.x) } var obj = { x: 20, fn: function() { alert(this.x) } } var fn = obj.fn func() // 10 fn() // 10
没错,最终输出的都是全局的 10。永远记住这一点:判断 this 指向谁,看执行时而非定义时,只要函数(function)没有绑定在对象上调用,它的 this 就是 window。
2.5 this 和 DOM/事件
W3C 把 DOM 实现成了各种节点,节点嵌套一起形成 DOM tree。节点有不同类型,如文本节点,元素节点等10多种。元素节点又分成了很多,对写HTML的人来说便是很熟悉的标签(Tag),如 div,ul,label 等。 看 W3C 的 API 文档,会发现它完全是按照面向对象方式实现的各种 API,有 interface,extends 等。
前面说过 this 是指向当前类的实例对象,对于这些 tag 类来说,不看其源码也知它们的很多方法内部用到的 this 是指向自己的。 有了这个结论,写HTML和JS时, this 就清晰了很多。
示例A:
<!-- this 指向 div --> <div onclick="alert(this)"></div>
示例B:
<div id="nav"></div> <script> nav.onclick = function() { alert(this) // 指向div#nav } </script>
示例C:
$('#nav').on('click', function() { alert(this) // 指向 nav })
以上三个示例可以看到,在给元素节点添加事件的时候,其响应函数(handler)执行时的 this 都指向 Element 节点自身。jQuery 也保持了和标准一致,但却让人迷惑,按 “this 指向调用时所在函数所绑定的对象” 这个定义,jQuery 事件 handler 里的 this,应该指向 jQuery 对象,而非 DOM 节点。因此你会发现在用 jQuery 时,经常需要把事件 handler 里的 element 在用 $ 包裹下变成 jQuery 对象后再去操作。
$('#nav').on('click', function() { var _this = $(this) // this指向#nav节点,而非jQuery对象,所以需要再次转为 jQuery 对象 _this .attr('data-x', x) _this .attr('data-x', x) })
有人可能有如下的疑问:
<div id="nav" onclick="getId()">ddd</div> <script> function getId() { alert(this.id) } </script>
点击 div 后,为什么 id 是 undefined,不说是指向的 当前元素 div 吗?
判断 this 指向谁,看执行时而非定义时,只要函数(function)没有绑定在对象上调用,它的 this 就是 window。
这里函数 getId 调用时没有绑定在任何对象上,可以理解成这种结构:
div.onclick = function() { getId() }
getId 所处匿名函数里的 this 是 div,但 getId 自身内的 this 则不是了。
2.6 this 可以被 call/apply 改变
call/apply 是函数调用的另外两种方式,两者的第一个参数都可以改变函数的上下文 this。call/apply 是 JS 里动态语言特性的表征。
动态语言:
程序在运行时可以改变其结构,新的函数可以被引进,已有的函数可以被删除,即程序在运行时可以发生结构上的变化。
通常有以下几点特征表示它为动态语言:
动态的数据类型
动态的函数执行
动态的方法重写
var m1 = { message: 'This is A' } var m2 = { message: 'This is B' } function showMsg() { alert(this.message) } showMsg() // undefined showMsg.call(m1) // 'This is A' showMsg.call(m2) // 'This is B'
2.7 ES6 箭头函数(arrow function) 和 this
判断 this 指向谁,看执行时而非定义时,只要函数(function)没有绑定在对象上调用,它的 this 就是 window。
前面一直用这句话来判断 this 的指向,在箭头函数里前面半句就失效了。箭头函数的特征就是,定义在哪,this 就指向那。即箭头函数定义在一个对象里,那箭头函数里的 this 就指向该对象。如下:
var book = { author: 'John Resig', init: function() { document.onclick = ev => { alert(this.author) ; // 这里的 this 不是 document 了 } } }; book.init()
对象 book 里有一个属性 author, 有一个 init 方法, 给 document 添加了一个点击事件,如果是传统的函数,我们知道 this 指向应该是 document,但箭头函数会指向当前对象 book。
箭头函数让 JS 回归自然和简单,函数定义在哪它 this 就指向哪,定义在对象里它指向该对象,定义在类的原型上,指向该类的实例,这样更容易理解。