深刻学习js系列是本身阶段性成长的见证,但愿经过文章的形式更加严谨、客观地梳理js的相关知识,也但愿可以帮助更多的前端开发的朋友解决问题,期待咱们的共同进步。javascript
若是以为本系列不错,欢迎点赞、评论、转发,您的支持就是我坚持的最大动力。html
this关键字是JavaScript中最复杂的机制之一,它是一个很特别的关键字,被自动定义在全部函数的做用域 中。前端
跟别的语言截然不同的是:js的this老是指向一个对象,而具体指向哪一个对象是在运行时基于函数的执行环境动态绑定的,而非函数被声明时候的环境。java
若是学习this的代价很大,可是对于咱们平时工做并不大,咱们干吗要付出这么大的代价学习呢?的确,在介绍怎么作以前咱们须要先明白为何。chrome
除去不经常使用的with和eval的状况,具体到实际应用中,this的指向大体能够分为如下四种:数组
- 1.做为对象的方法调用。
- 2.做为普通函数调用。
- 3.构造器调用。
- 4.Function.prototype.call 或者 Function.prototype.apply 下面分为这四种状况分别进行调用:
当函数做为对象的方法被调用的时候,this指向该对象:浏览器
var obj = {
a: 1,
getA: function () {
console.log(this === obj); // true
console.log(this.a); // 1;
}
}
obj.getA();
复制代码
当函数不做为对象的属性被调用时候,也就是咱们所说的普通函数方式,此时的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();
复制代码
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”
复制代码
跟普通函数调用相比,用 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
下面看一个常常遇到的问题:
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);
复制代码
欢迎添加个人我的微信讨论技术和个体成长。