深刻学习js之——this#6

深刻学习js系列是本身阶段性成长的见证,但愿经过文章的形式更加严谨、客观地梳理js的相关知识,也但愿可以帮助更多的前端开发的朋友解决问题,期待咱们的共同进步。javascript

若是以为本系列不错,欢迎点赞、评论、转发,您的支持就是我坚持的最大动力。html


开篇

this关键字是JavaScript中最复杂的机制之一,它是一个很特别的关键字,被自动定义在全部函数的做用域 中。前端

跟别的语言截然不同的是:js的this老是指向一个对象,而具体指向哪一个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时候的环境。java

为何要使用this

若是学习this的代价很大,可是对于咱们平时工做并不大,咱们干吗要付出这么大的代价学习呢?的确,在介绍怎么作以前咱们须要先明白为何。chrome

除去不经常使用的with和eval的状况,具体到实际应用中,this的指向大体能够分为如下四种:数组

  • 1.做为对象的方法调用。
  • 2.做为普通函数调用。
  • 3.构造器调用。
  • 4.Function.prototype.call 或者 Function.prototype.apply 下面分为这四种状况分别进行调用:

1.做为对象的方法调用:

当函数做为对象的方法被调用的时候,this指向该对象:浏览器

var obj = {
  a: 1,
  getA: function () {
    console.log(this === obj); // true 
    console.log(this.a); // 1;
  }
}
obj.getA();
复制代码

2.做为普通函数被调用:

当函数不做为对象的属性被调用时候,也就是咱们所说的普通函数方式,此时的this老是指向全局的对象。在浏览器的js里面,这个全局对象是window对象。微信

// 建立全局的name对象 挂载在window上面
window.name = "globalName"; 
var getName = function () {
  return this.name;
}
console.log(getName()); // 输出的是 globalName

复制代码

或者闭包

window.name = "globalName";
var myObject = {
  name: "louis",
  getName: function () {
    return this.name;
  }
}
var getName = myObject.getName;
console.log(getName()); // "globalName"

复制代码

有时候咱们会遇到一些困扰,好比在事件节点的div函数内部,有一个局部的callback方法,callback被做为普通的函数被调用时,callback内部的this指向了window,但咱们每每想让它的指向div节点.app

<div id = "div1">我是一个div</div>
复制代码
window.id = "window";
document.getElementById('div1').onclick = function () {
  alert(this.id); // 输出:'div1'
  var callback = function () {
    alert(this.id); // 输出:'window'
  }
  callback();
};
复制代码

此时有一种简单的解决方案,能够用一个变量保存div节点的引用:

document.getElementById('div1').onclick = function () {
  var that = this;    // 保存div的引用
  var callback = function () {
    alert(that.id);    // 输出:'div1'
  }
  callback();
}

复制代码

在ECMAScript 2015 中的严格模式下,这种状况下的this指向已经被规定为不会指向全局对象,而是undefined:

function func() {
 "use strict"
  alert(this); // undefined
}
func();

复制代码

3.构造器的调用:

js中没有类,可是能够从构造器中建立对象,同时也提供了new运算符,使得构造器看起来像是一个类,

除了宿主提供的一些内置函数,大部分js函数均可以当成构造器使用,构造器的外表看起来和普通的函数没有什么区别,他们的区别在于调用方式,当使用new运算符调用函数的时候,该函数老是返回一个对象,一般状况下,构造器里面的this就是指向返回的这个对象。

var MyClass = function () {
  this.name = "louis";
}

var obj = new MyClass();
console.log(obj.name);
复制代码

可是new调用构造器时候,还要注意一个问题,若是构造器显式的返回了一个object对象那么这次运算结果最终会返回这个对象,而不是咱们以前期待的this:

var MyClass = function () {
  this.name = 'sven';
  return {    // 显式地返回一个对象
    name: 'anne'
  }
};

var obj = new MyClass();
alert(obj.name);     // 输出:anne”
复制代码

若是构造器不显式的返回任何数据,或是返回一个非对象类型的数据,就不会形成上述问题。

var MyClass = function () {
  this.name = 'sven'
  return 'anne';    // 返回string类型
};

var obj = new MyClass();
alert(obj.name);     // 输出:sven”

复制代码

4.Function.prototype.call 或者 Function.prototype.apply调用

跟普通函数调用相比,用 Function.prototype.call 或者 Function.prototype.apply能够动态的改变传入函数的this:

var obj1 = {
  name: "louis",
  getName: function () {
    return this.name;
  }
}

var obj2 = {
  name: "kerry";
}

console.log(obj1.getName());     // 输出: louis
console.log(obj1.getName.call(obj2));    // 输出: kerry

复制代码

call 和 apply 方法可以很好的体现 js的函数式语言特性 在js中几乎每一次编写函数式语言风格的代码都离不开call和apply

5.丢失的this

下面看一个常常遇到的问题:

var obj = {
  myName: "louis",
  getName: function () {
    return this.name;
  }
}

console.log(obj.getName()); // louis;
var getName2 = obj.getName;
console.log(getName2()) // undefined
复制代码

当调用obj.getName时,getName方法是做为obj对象的属性被调用的,根据上文提到的规律,此时的this指向obj对象,因此obj.getName()输出'louis'。

当用另一个变量getName2来引用obj.getName,而且调用getName2时, 此时是普通函数调用方式,this是指向全局window的,window上面并无挂载任何属性因此程序的执行结果是undefined。

再看另外一个例子,document.getElementById这个方法名实在有点过长,咱们大概尝试过用一个短的函数来代替它,如同prototype.js等一些框架所作过的事情:

var getId = function (id) {
  return document.getElementById(id);
};

getId('div1');

复制代码

咱们也许思考过为何不能用下面这种更简单的方式

var getId = document.getElementById;
getId( 'div1' );
复制代码

如今不妨花1分钟时间,让这段代码在浏览器中运行一次

<div id="div1">我是一个div</div>

复制代码
var getId = document.getElementById;
getId( 'div1' );

复制代码

在chrome friefox IE10 中执行事后就会发现,这段代码抛出一个异常,这是由于不少引擎的document.getElementById 方法的内部实现中须要用到this,这个this原本被指望指向document,当getElementById方法做为document对象的属性被调用时,方法内部的this确实是指向document的。

可是当getId来引用document,getElementById以后,再调用getId,此时就成了普通的函数调用了,函数内部的this指向了window,而不是原来的document。

咱们能够尝试利用apply把document当作this传递给getId函数,修正 this指向问题。

document.getElementById = (function(func){
  return function(){
    return func.apply(document,arguments);
  }
})(document.getElementById);

var getId = document.getElementById;
var div = getId('div1');

alert(div.id);

复制代码

深刻学习JavaScript系列目录

欢迎添加个人我的微信讨论技术和个体成长。

欢迎关注个人我的微信公众号——指尖的宇宙,更多优质思考干货

相关文章
相关标签/搜索