JavaScript中的this比较灵活,根据在不同环境下,或者同一个函数在不同方式调用下,this都有可能是不同的。但是有一个总的原则,那就是this指的是,调用函数的那个对象。

下面是我的学习笔记,把它罗列成8种情况。

全局的this(浏览器)

全局作用域的this一般指向全局对象,在浏览器中这对象就是window,在node中这对象就是global。

1
2
3
4
console.log(this.document === document); // true (document === window.document)
console.log(this === window); // true
this.a = 37;  //相当于创建了一个全局变量a
console.log(window.a); // 37

一般函数的this(浏览器)

一般的函数声明或者函数表达式,直接调用函数的话,this依然指向全局对象,在浏览器中这对象就是window,在node中这对象就是global。

1
2
3
4
function f1(){ 
  return this
}
f1() === window; // true, global object

再举一个例子,看完就非常透彻了

1
2
3
4
5
function test(){
 this.x = 1;
  alert(this.x);
}
test(); // 1

为了证明this就是全局对象,对代码做一些改变:

1
2
3
4
5
var x = 1;
function test(){
 alert(this.x);
}
test(); // 1

运行结果还是1。再变一下:

1
2
3
4
5
6
var x = 1;
function test(){
 this.x = 0;
}
test();
alert(x); //0

但是在严格模式下,一般函数调用的时候this指向undefined,这也是node为什么要用严格模式的一个原因。

1
2
3
4
5
function f2(){ 
  "use strict"; // see strict mode 
  return this;
}
f2() === undefined; // true

作为对象方法的函数的this

this作为对象方法来使用是比较常见的。

下面这个例子,我们创建了一个对象字面量o,o里面有个属性f,它的值是一个函数对象,把函数作为对象属性的值这种方式我们常常叫作对象的方法。作为对象的方法调用的时候,这时候this指向对象o

1
2
3
4
5
6
7
8
var o = { 
   prop: 37, 
   f: function() {   
     return this.prop;   
  }
}; 
 
console.log(o.f()); // logs 37

我们不一定要定义成函数字面量这样子的对象,像下面这种情况,我们只定义了一个对象o,如果直接调用independent()函数的话,this会指向window,但是我们通过赋值的方式,临时创建一个属性f,并指向函数对象的时候,我们仍然拿到了37。

1
2
3
4
5
6
7
8
var o = {prop: 37};
 
function independent() { 
   return this.prop;
}
 
o.f = independent; 
console.log(o.f()); // logs 37

所以并不是看函数是怎么创建的,而是只要将函数作为对象的方法去调用,this就会指向这个对象。

对象原型链上的this

下面这个例子中:我们先创建了一个对象o,里面有一个属性f,函数作为对象属性的值,我们通过Object.create(o)创建了一个对象p,p是一个空对象,它的原型会指向o,然后使用p.a = 1; p.b = 4创建对象p上的属性,那么我们调用原型上的方法时,this.a,this.b依然能取到对象p上的a和b。这里需要注意的是p的原型才是o,我们调用p.f(),调用的是原型链o上的属性f,原型链上的this可以拿到当前的对象p。

1
2
3
4
5
var o = {f:function(){ return this.a + this.b; }};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5 

get/set方法与this

get/set方法中的this一般会指向get/set方法所在对象里面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function modulus(){  
   return Math.sqrt(this.re * this.re + this.im * this.im);
}
var o = {
  re: 1,
  im: -1,
  get phase(){     
     return Math.atan2(this.im, this.re);   
  }
};
Object.defineProperty(o, 'modulus', {       //临时动态给o对象创建modules属性
  get: modulus, enumerable:true, configurable:true});
 
console.log(o.phase, o.modulus); // logs -0.78 1.4142

构造函数中的this

用new把MyClass作为构造函数调用的话,this会指向空的对象,并且这个对象的原型会指向MyClass.prototype(可以看这篇文章对原型链的总结),但是调用的时候做了this.a = 37的赋值,所以最后this会作为返回值(没写return语句,或者return的是基本类型的话,会将this作为返回值),第二个例子return语句返回了对象,那么就会将a = 38作为返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
function MyClass(){   
   this.a = 37;
}
var o = new MyClass(); 
console.log(o.a); // 37
 
function C2(){   
   this.a = 37;  
   return {a : 38}; 
}
 
o = new C2(); 
console.log(o.a); // 38

call/apply方法与this

除了不同的调用方式外,函数对象有些方法能修改函数执行的this,比如call/apply。

call和apply基本上没差别,只不过call传参的方式是扁平的,而apply是把一个数组传进去。如下面这个例子

什么时候用call和apply呢?比如我们想调用Object.prototype.toString,但是我们想指定某个this的时候,那我们就可以就用Object.prototype.toString.call(this)这样子的方式来调用些无法直接调用的方法。如下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
function add(c, d){ 
   return this.a + this.b + c + d; 
}
var o = {a:1, b:3};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16     //第一个参数接收的是你想作为this的对象
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
 
function bar() { 
   console.log(Object.prototype.toString.call(this));
}
bar.call(7); // "[object Number]"

bind方法与this

bind方法是es5开始提供的,所以ie9+才支持

1
2
3
4
5
6
7
8
9
function f(){ 
   return this.a; 
}
 
var g = f.bind({a : "test"});   //想把某个对象作为this的时候,就把它传进去,得到一个新对象g
console.log(g()); // test       //重复调用的时候,this已经指向bind参数。这对于我们绑定一次需要重复调用依然实现绑定的话,会比apply和call更加高效(看下面这个例子)
 
var o = {a : 37, f : f, g : g}; 
console.log(o.f(), o.g()); // 37, test   //o.f()通过对象的属性调用,this指向对象o;比较特殊的是即使我们把新绑定的方法作为对象的属性调用,o.g()依然会按之前的绑定去走,所以答案是test不是g

总结

做项目的时候才发现这些基础概念有多么的重要,如果不把它们逐个落实了,真的是一不小心就会掉进坑里。后续我还会对原型链,作用域,继承,链式调用,正则等知识进行总结,欢迎关注

【转】深入浅出JavaScript之this的更多相关文章

  1. 深入浅出 JavaScript 对象 v0.5

    JavaScript 没有类的概念,因此它的对象与基于类的语言中的对象有所不同.笔者主要参考<JS 高级程序设计>.<JS 权威指南>和<JS 精粹> 本文由浅入深 ...

  2. 深入浅出 JavaScript 关键词 -- this

    深入浅出 JavaScript 关键词 -- this 要说 JavaScript 这门语言最容易让人困惑的知识点,this 关键词肯定算一个.JavaScript 语言面世多年,一直在进化完善,现在 ...

  3. 深入浅出Javascript的正则表达式

    深入浅出的javascript的正则表达式学习教程 阅读目录 了解正则表达式的方法 了解正则中的普通字符 了解正则中的方括号[]的含义 理解javascript中的元字符 RegExp特殊字符中的需要 ...

  4. 深入浅出JavaScript之原型链&继承

    Javascript语言的继承机制,它没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instanc ...

  5. 深入浅出JavaScript之闭包(Closure)

    闭包(closure)是掌握Javascript从人门到深入一个非常重要的门槛,它是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现.下面写下我的学习笔记~ 闭包-无处不 ...

  6. 深入浅出JavaScript之this

    JavaScript中的this比较灵活,根据在不同环境下,或者同一个函数在不同方式调用下,this都有可能是不同的.但是有一个总的原则,那就是this指的是,调用函数的那个对象. 下面是我的学习笔记 ...

  7. 【转】深入浅出JavaScript之闭包(Closure)

    闭包(closure)是掌握Javascript从人门到深入一个非常重要的门槛,它是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现.下面写下我的学习笔记~ 闭包-无处不 ...

  8. 深入浅出 JavaScript 中的 this

    在 Java 等面向对象的语言中,this 关键字的含义是明确且具体的,即指代当前对象.一般在编译期确定下来,或称为编译期绑定.而在 JavaScript 中,this 是动态绑定,或称为运行期绑定的 ...

  9. 【转】深入浅出 JavaScript 中的 this

    Java 等面向对象的语言中,this 关键字的含义是明确且具体的,即指代当前对象.一般在编译期确定下来,或称为编译期绑定.而在 JavaScript 中,this 是动态绑定,或称为运行期绑定的,这 ...

随机推荐

  1. Maven实战:pom.xml与settings.xml

    pom.xml与settings.xml pom.xml与setting.xml,可以说是Maven中最重要的两个配置文件,决定了Maven的核心功能,虽然之前的文章零零碎碎有提到过pom.xml和s ...

  2. 《图解TCP/IP》读书笔记

    一.国际惯例:书托 这是一本图文并茂的网络管理技术书籍,旨在让广大读者理解TCP/IP的基本知识.掌握TCP/IP的基本技能. 书中讲解了网络基础知识.TCP/IP基础知识.数据链路.IP协议.IP协 ...

  3. 关于我 — About Me

    个人简介 姓名:周旭龙 关注:.NET开发技术.Web前端技术 邮箱:edisonchou@hotmail.com GitHub: https://github.com/edisonchou 主要经历 ...

  4. MVVM大比拼之vue.js源码精析

    VUE 源码分析 简介 Vue 是 MVVM 框架中的新贵,如果我没记错的话作者应该毕业不久,现在在google.vue 如作者自己所说,在api设计上受到了很多来自knockout.angularj ...

  5. C++模板编程:如何使非通用的模板函数实现声明和定义分离

    我们在编写C++类库时,为了隐藏实现,往往只能忍痛舍弃模版的强大特性.但如果我们只需要有限的几个类型的模版实现,并且不允许用户传入其他类型时,我们就可以将实例化的代码放在cpp文件中实现了.然而,当我 ...

  6. 如何开发一个简单的HTML5 Canvas 小游戏

    原文:How to make a simple HTML5 Canvas game 想要快速上手HTML5 Canvas小游戏开发?下面通过一个例子来进行手把手教学.(如果你怀疑我的资历, A Wiz ...

  7. CSharpGL(7)对VAO和VBO的封装

    CSharpGL(7)对VAO和VBO的封装 2016-08-13 由于CSharpGL一直在更新,现在这个教程已经不适用最新的代码了.CSharpGL源码中包含10多个独立的Demo,更适合入门参考 ...

  8. Java基础知识(贰)

    一.面向对象 Java中的面向对象与C#的面向对象,本质都是一样.所以对于学过C#的同学理解Java中面向对象的概念就比较轻松. 对象 定义: 万物皆对象,客观存在的事物都称为对象. 1.面向对象 类 ...

  9. Atitit 图像处理的心得与疑惑 attilax总结

    Atitit 图像处理的心得与疑惑 attilax总结 1.1. 使用类库好不好??还是自己实现算法1 1.2. 但是,如果遇到类库体积太大,后者没有合适的算法,那就只能自己开发算法了1 1.3. 如 ...

  10. WCF学习之旅—实现支持REST客户端应用(二十四)

    WCF学习之旅—实现REST服务(二十二) WCF学习之旅—实现支持REST服务端应用(二十三) 在上二篇文章中简单介绍了一下RestFul与WCF支持RestFul所提供的方法,及创建一个支持RES ...