Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript基础心法——this #6

Open
axuebin opened this issue Oct 16, 2017 · 10 comments
Open

JavaScript基础心法——this #6

axuebin opened this issue Oct 16, 2017 · 10 comments

Comments

@axuebin
Copy link
Owner

axuebin commented Oct 16, 2017

看看这个有着深不可测的魔力的this到底是个什么玩意儿 ~


什么是this

在传统面向对象的语言中,比如Java,this关键字用来表示当前对象本身,或当前对象的一个实例,通过this关键字可以获得当前对象的属性和调用方法。

在JavaScript中,this似乎表现地略有不同,这也是让人“讨厌”的地方~

ECMAScript规范中这样写:

this 关键字执行为当前执行环境的 ThisBinding。

MDN上这样写:

In most cases, the value of this is determined by how a function is called.
在绝大多数情况下,函数的调用方式决定了this的值。

可以这样理解,在JavaScript中,this的指向是调用时决定的,而不是创建时决定的,这就会导致this的指向会让人迷惑,简单来说,this具有运行期绑定的特性。

参考资料:this - JavaScript | MDN

来看看不同的情况五花八门的this吧~

调用位置

首先需要理解调用位置,调用位置就是函数在代码中被调用的位置,而不是声明的位置。

通过分析调用栈(到达当前执行位置所调用的所有函数)可以找到调用位置。

function baz(){
  console.log("baz");
  bar();
}
function bar(){
  console.log("bar");
  foo();
}
function foo(){
  console.log("foo");
}
baz();

当我们调用baz()时,它会以此调用baz()bar()foo()

对于foo():调用位置是在bar()中。
对于bar():调用位置是在baz()中。
而对于baz():调用位置是全局作用域中。

可以看出,调用位置应该是当前正在执行的函数的前一个调用中。

全局上下文

在全局执行上下文中this都指代全局对象。

  • this等价于window对象
  • var === this. === winodw.
console.log(window === this); // true
var a = 1;
this.b = 2;
window.c = 3;
console.log(a + b + c); // 6

在浏览器里面this等价于window对象,如果你声明一些全局变量,这些变量都会作为this的属性。

函数上下文

在函数内部,this的值取决于函数被调用的方式。

直接调用

this指向全局变量。

function foo(){
  return this;
}
console.log(foo() === window); // true

call()、apply()

this指向绑定的对象上。

var person = {
  name: "axuebin",
  age: 25
};
function say(job){
  console.log(this.name+":"+this.age+" "+job);
}
say.call(person,"FE"); // axuebin:25
say.apply(person,["FE"]); // axuebin:25

可以看到,定义了一个say函数是用来输出nameagejob,其中本身没有nameage属性,我们将这个函数绑定到person这个对象上,输出了本属于person的属性,说明此时this是指向对象person的。

如果传入一个原始值(字符串、布尔或数字类型)来当做this的绑定对象, 这个原始值会被转换成它的对象形式(new String()),这通常被称为“装箱”。

callapplythis的绑定角度上来说是一样的,唯一不同的是它们的第二个参数。

bind()

this将永久地被绑定到了bind的第一个参数。

bindcallapply有些相似。

var person = {
  name: "axuebin",
  age: 25
};
function say(){
  console.log(this.name+":"+this.age);
}
var f = say.bind(person);
console.log(f());

箭头函数

所有的箭头函数都没有自己的this,都指向外层。

关于箭头函数的争论一直都在,可以看看下面的几个链接:

ES6 箭头函数中的 this?你可能想多了(翻译)

关于箭头函数this的理解几乎完全是错误的 #150

MDN中对于箭头函数这一部分是这样描述的:

An arrow function does not create its own this, the this value of the enclosing execution context is used.
箭头函数会捕获其所在上下文的this值,作为自己的this值。

function Person(name){
  this.name = name;
  this.say = () => {
    var name = "xb";
    return this.name;
  }
}
var person = new Person("axuebin");
console.log(person.say()); // axuebin

箭头函数常用语回调函数中,例如定时器中:

function foo() {  
  setTimeout(()=>{
    console.log(this.a);
  },100)
}
var obj = {
  a: 2
}
foo.call(obj);

附上MDN关于箭头函数this的解释:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions#不绑定_this

作为对象的一个方法

this指向调用函数的对象。

var person = {
  name: "axuebin",
  getName: function(){
    return this.name;
  }
}
console.log(person.getName()); // axuebin

这里有一个需要注意的地方。。。

var name = "xb";
var person = {
  name: "axuebin",
  getName: function(){
    return this.name;
  }
}
var getName = person.getName;
console.log(getName()); // xb

发现this又指向全局变量了,这是为什么呢?

还是那句话,this的指向得看函数调用时。

作为一个构造函数

this被绑定到正在构造的新对象。

通过构造函数创建一个对象其实执行这样几个步骤:

  1. 创建新对象
  2. 将this指向这个对象
  3. 给对象赋值(属性、方法)
  4. 返回this

所以this就是指向创建的这个对象上。

function Person(name){
  this.name = name;
  this.age = 25;
  this.say = function(){
    console.log(this.name + ":" + this.age);
  }
}
var person = new Person("axuebin");
console.log(person.name); // axuebin
person.say(); // axuebin:25

作为一个DOM事件处理函数

this指向触发事件的元素,也就是始事件处理程序所绑定到的DOM节点。

var ele = document.getElementById("id");
ele.addEventListener("click",function(e){
  console.log(this);
  console.log(this === e.target); // true
})

HTML标签内联事件处理函数

this指向所在的DOM元素

<button onclick="console.log(this);">Click Me</button>

jQuery的this

在许多情况下JQuery的this都指向DOM元素节点。

$(".btn").on("click",function(){
  console.log(this); 
});

总结

如果要判断一个函数的this绑定,就需要找到这个函数的直接调用位置。然后可以顺序按照下面四条规则来判断this的绑定对象:

  1. new调用:绑定到新创建的对象
  2. callapplybind调用:绑定到指定的对象
  3. 由上下文对象调用:绑定到上下文对象
  4. 默认:全局对象

注意:箭头函数不使用上面的绑定规则,根据外层作用域来决定this,继承外层函数调用的this绑定。

@axuebin axuebin changed the title JavaScript this JavaScript的this关键字 Oct 26, 2017
@axuebin axuebin changed the title JavaScript的this关键字 JavaScript基础心法——this Oct 26, 2017
@wo-xiaoxiao
Copy link

var person = {
name: "axuebin",
age: 25
};
function say(job){
console.log(this.name+":"+this.age+" "+job);
}
say.call(person,"FE"); // axuebin:25
say.apply(person,["FE"]); // axuebin:25
打印是不是有问题?

@Lyddddding
Copy link

var person = {
名称:“ axuebin”,
年龄:25
};
函数say(job){
console.log(this.name +“:” + this.age +“” + job);
}
say.call(person,“ FE”); // axuebin:25
say.apply(person,[“ FE”]); // axuebin:25
打印是不是有问题?

// axuebin:25 FE

@djhsyhhy
Copy link

var person = {
name: "axuebin",
age: 25
};
function say(job){
console.log(this.name+":"+this.age+" "+job);
}
say.call(person,"FE"); // axuebin:25
say.apply(person,["FE"]); // axuebin:25
打印是不是有问题?

少了个FE把

@CatherineYF
Copy link

箭头函数一节里用来说明this指向的示例貌似不能反应箭头函数本身的特性啊:

function Person(name){
  this.name = name;
  this.say = () => {
    var name = "xb";
    return this.name;
  }
}
var person = new Person("axuebin");
console.log(person.say()); // axuebin

假如此处,我把this.say改为普通函数:

function Person(name){
  this.name = name;
  this.say = function() {
    var name = "xb";
    return this.name;
  }
}
var person = new Person("axuebin");
console.log(person.say()); // 一样打印出了axuebin,这总不能说明普通函数也和箭头函数一样,由外层上下文决定this吧

@fyuanz
Copy link

fyuanz commented Oct 23, 2020

箭头函数一节里用来说明this指向的示例貌似不能反应箭头函数本身的特性啊:
@CathyZYF

箭头函数会捕获其所在上下文的this值,作为自己的this值。

我的理解是:
毫无疑问this指向由构造函数Person创建的实例person。
this的指向完全取决于函数调用的位置,函数say在person中调用,所以这里无论是使用普通函数还是箭头函数,this都指向person实例。

@mingyang11
Copy link

var name = "xb";
var person = {
name: "axuebin",
getName: function(){
return this.name;
}
}
var getName = person.getName;
console.log(getName()); // xb
这个后来打印的是undefined啊大佬佬

@Qzhor
Copy link

Qzhor commented Apr 13, 2021

" 作为一个DOM事件处理函数, this指向触发事件的元素,也就是始事件处理程序所绑定到的DOM节点。"
这句话前半段是不对的,
this指向绑定事件的dom元素, this===e.currentTarget才是准确的。

@bj-white
Copy link

你好,看完感觉写的很好,但是发现一个问题
代码一:

function Person(name){
this.name = name;
this.say = () => {
var name = "xb";
return this.name;
}
}
var person = new Person("axuebin");
console.log(person.say()); // axuebin
代码二:

function Person(name){
this.name = name;
this.say = function () {
var name = "xb";
return this.name;
}
}
var person = new Person("axuebin");
console.log(person.say()); // axuebin

代码一和代码二一个用箭头函数,一个没用,为什么执行出来的结果是一样的,能详细解释一下吗?

@s597198390
Copy link

@bj-white 因为两个写法this指向的都是person这个对象

@Susan7239874
Copy link

写的很好,就是箭头函数的例子有点不太好

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests