文章目录
this 的四种绑定规则
this 关键字是函数运行时自动生成的一个内部对象,只能在函数内部使用,总指向调用它的对象
1、默认绑定window
像 这种直接使用而不带任何修饰的函数调用 ,就 默认且只能 应用 默认绑定。
那默认绑定到哪呢,一般是window上,严格模式下 是undefined
。
var a = 1 ;
function foo(){
console.log(this.a); // 1
}
foo(); //1 即在非严格模式下,相等于 window.foo() ,这时this指向调用它的window对象
function demo() { // 严格模式下
'use strict';
alert(this);
}
demo();
2、对象方法
- 函数还可以作为某个对象的方法调用,这时this就指这个上级对象
let name = 'finget'
let obj = {
name: 'FinGet',
getName: function() {
alert(this.name);
}
}
obj.getName(); // FinGet
let fn = obj.getName;
fn(); //finget this -> window
- 如果是链性的关系,比如 xx.yy.obj.foo();, 上下文取函数的直接上级,即紧挨着的那个。
var o = {
a:10,
b:{
fn:function(){
console.log(this.a);
}
}
}
o.b.fn(); //undefined
上述代码中,this的上一级对象为b,b内部并没有a变量的定义,所以输出undefined
- 特殊情况
var o = {
a:10,
b:{
a:12,
fn:function(){
console.log(this.a); //undefined
console.log(this); //window
}
}
}
var j = o.b.fn;
j();
此时this指向的是window,这里的大家需要记住,this永远指向的是最后调用它的对象
,虽然fn是对象b的方法,但是fn赋值给j时候并没有执行,所以最终指向window
3、构造函数
- 通过构建函数new关键字生成一个实例对象,此时this指向这个实例对象,并返回
function Foo(){
this.a = 10;
console.log(this);
}
var obj = new Foo(); // 此时Foo中的this指向了返回的实例对象{a:10}
console.log(obj.a); // 10 new绑定
- new过程遇到return一个对象,此时this指向为返回的对象,而非默认创建的实例对象
function foo(){
this.a = 10;
return new String("捣蛋鬼");
}
var obj = new foo();
console.log(obj.a); // undefined
console.log(obj); // "捣蛋鬼"
- 如果返回一个简单类型的时候,则this指向实例对象
function fn()
{
this.user = 'xxx';
return 1;
}
var a = new fn();
console.log(a.user); //xxx
- 注意:是null虽然也是对象,但是此时new仍然指向实例对象
function fn()
{
this.user = 'xxx';
return null;
}
var a = new fn;
console.log(a.user); //xxx
那么用new创建对象后,js帮我们做了什么工作呢:
- 创建一个新对象。
- 把这个新对象的__proto__属性指向 原构造函数的prototype属性。(即继承原函数的原型)
- 将这个新对象绑定到此函数的this上 。
- 返回新对象,如果这个函数没有返回其他对象。
4、箭头函数
this指向箭头函数定义时所处的上下文环境
var name = 'my name is window';
var obj = {
name: 'my name is obj',
fn: function () {
var timer = null;
clearInterval(timer);
timer = setInterval(() => {
console.log(this.name); //my name is obj
}, 1000)
}
}
5、事件函数
元素绑定事件,事件触发后执行函数中,this指向的是当前元素
window.onload = function() {
let $btn = document.getElementById('btn');
$btn.onclick = function(){
alert(this); // this -> 当前触发元素
}
}
6、定时器、回调函数中的this,指向window
定时器setTimeout或setInterval,
- 在全局中定义并执行,this指向window
- 其回调函数为function 时,this指向window
- 其回调函数为箭头函数时,this指向定时器定义时所在的上下文环境中的this
//定时器
setTimeout(function() {
alert(this); // this -> window ,严格模式 也是指向window
},500)
// function 函数
var name = 'my name is window';
var obj = {
name: 'my name is obj',
fn: function () {
var timer = null;
clearInterval(timer);
timer = setInterval(function () {
console.log(this.name); //my name is window, this指向window
}, 1000)
}
}
// 箭头函数
var a = {
say:function(){
console.log(this) // this指向a
setTimeout(() => {
console.log(this) // this指a
},1000)
}
}
a.say()
//回调函数
var o = {
age : 12,
say : function() {
function callback() {
return this.age;
}
func(callback);
}
};
function func(callback) {
var name = "Xiao Ming";
console.log(name + " is " + callback() + " years old.");
}
o.say(); //Xiao Ming is undefined years old.
/* 解析:函数内部的【this】指向于此函数的调用者(拥有者)。
在上面这个例子中,虽然【callback】函数定义于对象【o】的【say】方法中,但实际上由于【callback】是在【func】函数中进行的普通调用,那么【func】中的【callback】的调用者我们便可以理解为是【window】对象 */
//当使用一个对象的未定义的属性时不会报错,并返回“undefined”,而直接使用一个未定义的变量时便会报错
//优化
console.log(name + " is " + callback.call(o) + " years old.");
console.log(name + " is " + callback.apply(o) + " years old.");
console.log(name + " is " + callback.bind(o)() + " years old.");
var callback = () => this.age;
7、使用 call apply bind修改this指向
call和apply:改变this指向后执行函数
。
call(thisScope, arg1, arg2, arg3…); //多个参数
apply(thisScope, [arg1, arg2, arg3…]); //两个参数bind:改变this指向后,返回函数
。
bind(thisScope, arg1, arg2, arg3…)
function foo(a,b){
console.log(a+b);
console.log(this.name)
}
let obj = {
name:"xxx"
}
foo.call(obj,1,2); // 3 xxx
foo.apply(obj, [3,4] ); //7 xxx
var foofunc = foo.bind(obj);
foofunc(100,200); // 300 xxx
this指向优先级
new绑定优先级 > 显示绑定优先级 > 隐式绑定优先级 > 默认绑定优先级
事件函数中的this指向
js三种绑定事件的方式,以及相应的this指向
1、html事件绑定
2、js事件绑定
3、js事件监听
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>js三种绑定事件的方式,以及相应的this指向</title>
</head>
<body>
<!--html事件绑定-->
<button class="btn" data-value=1 onclick="add(this)" >button 1</button>
<button class="btn" data-value=1 onclick="add1()" >button 2</button>
<hr/>
<!--js事件绑定-->
<h4 id="title">title <span>1234</span></h4>
<hr />
<!--js事件监听-->
<p class="desc"><span>my name is</span> <b>Tony</b>!</p>
</body>
<script>
//html事件绑定
function add(that){
console.log(that); // 绑定事件的dom对象
}
function add1(){
console.log(this); //window
}
//js事件绑定
let titleDom = document.getElementById("title");
titleDom.onclick = function(){
console.log(this); // 绑定事件的dom对象
}
//js事件监听
let descDom = document.getElementsByClassName('desc');
descDom[0].addEventListener('click',function(evt){
console.log(evt);
console.log(evt.target); //触发事件的dom对象
console.log(evt.currentTarget); //绑定事件的dom对象
console.log(this); //绑定事件的dom对象
})
</script>
</html>
箭头函数中this指向 (!!!!!!)
ES5中 this 的指向是可变的,ES6内箭头函数中 this的指向是固定的。
ES6内箭头函数内this 指向的固定化,并不是因为箭头函数内部有绑定 this 的机制,实际原因是箭头函数内部根本没有自己的this,它内部的this 其实就是外层代码块的 this,也就是定义时所在上下文环境中的this对象
。
它这this哪里来的???
function foo() {
this.a = 1
let b = () => console.log(this.a)
b()
}
foo() // 1
以上箭头函数中的this其实是父级作用域中的this,即函数foo的this。箭头函数引用了父级的变量,构成了一个闭包。以上代码等价于
function foo() {
this.a = 1
let self = this
let b = () => console.log(self.a)
b()
}
foo() // 1
我们可以⽤Babel 理解⼀下箭头函数:
// es6
const obj = {
getArrow(){
return () => {
console.log(this === obj)
}
}
}
babel转化后:
// ES5 由babel转译
var obj = {
getArrow:function getArrow(){
var _this = this;
return function () {
console.log(_this === obj)
}
}
}
Tips:
(1) 箭头函数体内的 this 对象,就是**定义时所在环境中的this对象
**,而不是调用时所在的对象。
(2) 箭头函数不可以当构造函数 ,即不可以使用 new 命令,因为它没有 this,否则会抛出一个错误。
(3) 箭头函数没有自己的 this,所以不能使用 call()、apply()、bind() 这些方法去改变 this 指向。
(4) 箭头函数不可以使用arguments 对象,该对象在函数体内不存在。如果要用,可以使用rest参数代替。
(5) 箭头函数没有 prototype(原型对象)
使用箭头函数三点建议:
1、箭头函数适合于无复杂逻辑或者无副作用的纯函数场景下,例如用在map、reduce、filter的回调函数中;
2、不要在最外层定义箭头函数,因为在函数内部操作this会很容易污染全局作用域。最起码在箭头函数外部包一层普通函数,将this控制在可见的范围内;
3、箭头函数最吸引人的地方是简洁。在有多层函数嵌套的情况下,箭头函数的简洁性并没有很大的提升,反而影响了函数的作用范围的识别度,这种情况不建议使用箭头函数
function、箭头函数、类中的this
-
function
: function 的 this 谁调用的 this 指向谁
- 直接调用,指向window;
- 作为对象的属性调用,this 指向该对象 -
箭头函数
: 箭头函数本身没有this,其this继承声明时所在作用域的this -
类中的 this
指向其实例化对象
示例:
let f1 = function(){
console.log(this);
};
let f2 = ()=>{
console.log(this); // this 指向声明时的作用域里的this
};
document.onclick = f1;
document.body.onclick = f2;
class Drag {
constructor(f){
this.f = f;
}
}
f1(); // window
f2(); // window
document.onclick(); // docuemnt
document.body.onclick(); // window
new Drag(f1).f(); // Drag{}
new Drag(f2).f(); //window
参考链接 :https://segmentfault.com/a/1190000011194676#articleHeader8
参考链接; https://cnodejs.org/topic/584a207a3ebad99b336b1ede