只有掌握了JavaScript中的this操做符你才算正式迈入JavaScript这门语言的门槛!咱们一直都在用这个框架,那个框架,但每每忽视掉了js最基础的东西,笔者认为这些基础每每才是走下去,走深,最不可或缺的东西.那咱们就一块儿来学习一下js中神奇的this吧--------笔者查看了大量有关this的文章,有些是按照本身思路写的,有些是直接引用其余做者成熟的思路的文章javascript
学习一个知识首先要理解他的字面含义,经过翻译咱们知道,this的含义是这,这个(指较近的人或事物)的意思。那么咱们结合现实,咱们说的“这”在不一样的环境所指的事物是不同的。那么在JavaScript中this在不一样的环境调用所表达的含义也是很是丰富的。若是你以为JavaScript中的this和其余面向对象语言Java同样,是指存储在实例属性中的值,那你就大错特错了。JavaScript中的this有着在这门语言中不可或缺魔力。java
宿主(环境)解释
JS的运行环境通常由宿主环境和执行期环境共同构成,宿主环境是由外壳程序(如web浏览器就是一个外壳程序)生成,执行期环境是由嵌入到外壳程序中的JS引擎(/JS解释器)生成的,在执行期环境JS能够生成内置静态对象、初始化执行环境等。node
对于JavaScript,宿主环境最多见的是web浏览器,浏览器提供了一个JavaScript运行的环境,这个环境里面,须要提供一些接口,好让JavaScript引擎可以和宿主环境对接。JavaScript引擎才是真正执行JavaScript代码的地方,常见的引擎有V8(目前最快JavaScript引擎、Google生产)、JavaScript coregit
可是环境不是惟一的,也就是JavaScript不只仅可以在浏览器里面跑,也能在其余提供了宿主环境的程序里面跑,最多见的就是nodejs。一样做为一个宿主环境,nodejs也有本身的JavaScript引擎--V8。根据官方的定义: Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applicationsgithub
<script>
console.log(this === window) //true
var a = 3;
console.log(this.a, window.a)//3 3
</script>
复制代码
说明:在浏览器中,window对象同时也是全局对象web
> this === global
true
复制代码
index.js 文件在node环境中执行
console.log(this) //Object {}
console.log(this === global); //false
复制代码
index.js 文件在node环境中执行
var foo = "bar";
console.log(this.foo);//undefined
复制代码
> var foo = "bar";
> this.foo
bar
> global.foo
bar
复制代码
index.js 文件在node环境中执行
foo = "bar";
console.log(this.foo);//undefined
console.log(global.foo);//bar
复制代码
上面的几种种状况可能你们已经绕晕了,总结起来就是:在浏览器里面this是老大,它等价于window对象,若是你声明一些全局变量(无论在任何地方),这些变量都会做为this的属性。在node里面,有两种执行JavaScript代码的方式,一种是直接执行写好的JavaScript文件,另一种是直接在里面执行一行行代码。对于直接运行一行行JavaScript代码的方式,global才是老大,this和它是等价的。在这种状况下,和浏览器比较类似,也就是声明一些全局变量会自动添加给老大global,顺带也会添加给this。可是在node里面直接脚本文件就不同了,你声明的全局变量不会自动添加到this,可是会添加到global对象。因此相同点是,在全局范围内,全局变量终究是属于老大的。编程
说明:在函数内部,this的值取决于函数被调用的方式浏览器
<script>
testa()
function testa(){
testb()
function testb(){
console.log(this === window)//true
}
}
</script>
复制代码
index.js 文件在node环境中执行
foo = "bar";
function testThis () {
this.foo = "foo";
}
console.log(global.foo);//bar
testThis();
console.log(global.foo);//foo
复制代码
说明:由于上面代码不在严格模式下,且this的值不是由该调用设置的,因此this的值默认指向全局对象。闭包
<script type="text/javascript">
foo = "bar";
function testThis() {
"use strict";
this.foo = "foo";
}
console.log(this.foo); //logs "bar"
testThis(); //Uncaught TypeError: Cannot set property 'foo' of undefined
</script>
复制代码
说明:然而,在严格模式下,this将保持他进入执行环境时的值,因此下面的this将会默认为undefined,因此,在严格模式下,若是 this 没有被执行环境(execution context)定义,那它将保持为 undefined。app
<script type="text/javascript">
foo = "bar";
function testThis() {
this.foo = "foo";
}
console.log(this.foo); //logs "bar"
new testThis();
console.log(this.foo); //logs "bar"
console.log(new testThis().foo); //logs "foo"
</script>
复制代码
函数里面的this其实相对比较好理解,若是咱们在一个函数里面使用this,须要注意的就是咱们调用函数的方式,若是是正常的方式调用函数,this指代全局的this,若是咱们加一个new,这个函数就变成了一个构造函数,咱们就建立了一个实例,this指代这个实例,这个和其余面向对象的语言很像。另外,写JavaScript很常作的一件事就是绑定事件处理程序,也就是诸如button.addEventListener(‘click’, fn, false)之类的,若是在fn里面须要使用this,this指代事件处理程序对应的对象,也就是button。
<script type="text/javascript">
// 将一个对象做为call和apply的第一个参数,this会被绑定到这个对象。
var obj = {a: 'Custom'};
// 这个属性是在global对象定义的。
var a = 'Global';
function whatsThis(arg) {
return this.a; // this的值取决于函数的调用方式
}
whatsThis(); // 'Global'
whatsThis.call(obj); // 'Custom'
whatsThis.apply(obj); // 'Custom'
</script>
复制代码
说明:使用 call 和 apply 函数的时候要注意,若是传递给 this 的值不是一个对象,JavaScript 会尝试使用内部 ToObject 操做将其转换为对象。所以,若是传递的值是一个原始值好比 7 或 'foo',那么就会使用相关构造函数将它转换为对象,因此原始值 7 会被转换为对象,像 new Number(7) 这样,而字符串 'foo' 转化成 new String('foo') 这样,例如:下面代码
<script type="text/javascript">
function bar() {
console.log(Object.prototype.toString.call(this));
}
//原始值 7 被隐式转换为对象
bar.call(7); // [object Number]
</script>
复制代码
<script type="text/javascript">
function f(){
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); // azerty
var h = g.bind({a:'yoo'}); // bind只生效一次!
console.log(h()); // azerty
var o = {a:37, f:f, g:g, h:h};
console.log(o.f(), o.g(), o.h()); // 37, azerty, azerty
</script>
复制代码
<script type="text/javascript">
function f(){
return this.a;
}
var g = f.bind({a:"azerty"});
console.log(g()); // azerty
var h = g.bind({a:'yoo'}); // bind只生效一次!
console.log(h()); // azerty
var o = {a:37, f:f, g:g, h:h};
console.log(o.f(), o.g(), o.h()); // 37, azerty, azerty
</script>
复制代码
<script type="text/javascript">
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true
</script>
复制代码
<script type="text/javascript">
function Thing() {
console.log(this.foo);
}
Thing.prototype.foo = "bar";
var thing = new Thing(); //logs "bar"
console.log(thing.foo); //logs "bar"
</script>
复制代码
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
Thing.prototype.setFoo = function (newFoo) {
this.foo = newFoo;
}
var thing1 = new Thing();
var thing2 = new Thing();
thing1.logFoo(); //logs "bar"
thing2.logFoo(); //logs "bar"
thing1.setFoo("foo");
thing1.logFoo(); //logs "foo";
thing2.logFoo(); //logs "bar";
thing2.foo = "foobar";
thing1.logFoo(); //logs "foo";
thing2.logFoo(); //logs "foobar";
</script>
复制代码
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
Thing.prototype.setFoo = function (newFoo) {
this.foo = newFoo;
}
Thing.prototype.deleteFoo = function () {
delete this.foo;
}
var thing = new Thing();
thing.setFoo("foo");
thing.logFoo(); //logs "foo";
thing.deleteFoo();
thing.logFoo(); //logs "bar";
thing.foo = "foobar";
thing.logFoo(); //logs "foobar";
delete thing.foo;
thing.logFoo(); //logs "bar";
</script>
复制代码
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo, Thing.prototype.foo);
}
var thing = new Thing();
thing.foo = "foo";
thing.logFoo(); //logs "foo bar";
</script>
复制代码
此时的this是指,构造函数的原型上的方法至于为何看下面
<script type="text/javascript">
function Thing() {
}
Thing.prototype.things = [];
var thing1 = new Thing();
var thing2 = new Thing();
thing1.things.push("foo");
console.log(thing2.things); //logs ["foo"]
</script>
复制代码
<script type="text/javascript">
function Thing1() {
}
Thing1.prototype.foo = "bar";
function Thing2() {
}
Thing2.prototype = new Thing1();
var thing = new Thing2();
console.log(thing.foo); //logs "bar"
</script>
复制代码
<script type="text/javascript">
function Thing1() {
}
Thing1.prototype.foo = "bar";
function Thing2() {
this.foo = "foo";
}
Thing2.prototype = new Thing1();
function Thing3() {
}
Thing3.prototype = new Thing2();
var thing = new Thing3();
console.log(thing.foo); //logs "foo"
</script>
复制代码
<script type="text/javascript">
function Thing1() {
}
Thing1.prototype.foo = "bar";
Thing1.prototype.logFoo = function () {
console.log(this.foo);
}
function Thing2() {
this.foo = "foo";
}
Thing2.prototype = new Thing1();
var thing = new Thing2();
thing.logFoo(); //logs "foo";
</script>
复制代码
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
var info = "attempting to log this.foo:";
function doIt() {
console.log(info, this.foo);
}
doIt();
}
var thing = new Thing();
thing.logFoo(); //logs "attempting to log this.foo: undefined"
</script>
复制代码
在doIt里面的this是global对象或者在严格模式下面是undefined。这是形成不少不熟悉JavaScript的人深陷 this陷阱的根源。在这种状况下事情变得很是糟糕,就像你把一个实例的方法看成一个值,把这个值看成函数参数传递给另一个函数可是却不把这个实例传递给这个函数同样。在这种状况下,一个方法里面的环境变成了全局范围,或者在严格模式下面的undefined。
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
function doIt(method) {
method();
}
var thing = new Thing();
thing.logFoo(); //logs "bar"
doIt(thing.logFoo); //logs undefined
</script>
复制代码
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
var self = this;
var info = "attempting to log this.foo:";
function doIt() {
console.log(info, self.foo);
}
doIt();
}
var thing = new Thing();
thing.logFoo(); //logs "attempting to log this.foo: bar"
</script>
复制代码
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
var self = this;
function doIt() {
console.log(self.foo);
}
doIt();
}
function doItIndirectly(method) {
method();
}
var thing = new Thing();
thing.logFoo(); //logs "bar"
doItIndirectly(thing.logFoo); //logs undefined
</script>
复制代码
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
function doIt(method) {
method();
}
var thing = new Thing();
doIt(thing.logFoo.bind(thing)); //logs bar
</script>
复制代码
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
function doIt() {
console.log(this.foo);
}
doIt.apply(this);
}
function doItIndirectly(method) {
method();
}
var thing = new Thing();
doItIndirectly(thing.logFoo.bind(thing)); //logs bar
</script>
复制代码
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
function logFoo(aStr) {
console.log(aStr, this.foo);
}
var thing = new Thing();
logFoo.bind(thing)("using bind"); //logs "using bind bar"
logFoo.apply(thing, ["using apply"]); //logs "using apply bar"
logFoo.call(thing, "using call"); //logs "using call bar"
logFoo("using nothing"); //logs "using nothing undefined"
</script>
复制代码
<script type="text/javascript">
function Thing() {
return {};
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
var thing = new Thing();
thing.logFoo(); //Uncaught TypeError: undefined is not a function
</script>
复制代码
怪的是,若是你在构造函数里面返回了一个原始值,上面所述的状况并不会发生而且返回语句被忽略了。最好不要在你将经过new调用的构造函数里面返回任何类型的数据,即使你知道本身正在作什么。若是你想建立一个工厂模式,经过一个函数来建立一个实例,这个时候不要使用new来调用函数。固然这个建议是可选的。
<script type="text/javascript">
function Thing() {
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
var thing = Object.create(Thing.prototype);
thing.logFoo(); //logs "bar"
</script>
复制代码
<script type="text/javascript">
function Thing() {
this.foo = "foo";
}
Thing.prototype.foo = "bar";
Thing.prototype.logFoo = function () {
console.log(this.foo);
}
var thing = Object.create(Thing.prototype);
thing.logFoo(); //logs "bar"
</script>
复制代码
<script type="text/javascript">
function Thing1() {
this.foo = "foo";
}
Thing1.prototype.foo = "bar";
function Thing2() {
this.logFoo(); //logs "bar"
Thing1.apply(this);
this.logFoo(); //logs "foo"
}
Thing2.prototype = Object.create(Thing1.prototype);
Thing2.prototype.logFoo = function () {
console.log(this.foo);
}
var thing = new Thing2();
</script>
复制代码
<script type="text/javascript">
var obj = {
foo: "bar",
logFoo: function () {
console.log(this.foo);
}
};
obj.logFoo(); //logs "bar"
</script>
复制代码
<script type="text/javascript">
var obj = {
foo: "bar"
};
function logFoo() {
console.log(this.foo);
}
logFoo.apply(obj); //logs "bar"
</script>
复制代码
<script type="text/javascript">
var obj = {
foo: "bar",
deeper: {
logFoo: function () {
console.log(this.foo);
}
}
};
obj.deeper.logFoo(); //logs undefined
</script>
复制代码
<script type="text/javascript">
var obj = {
foo: "bar",
deeper: {
logFoo: function () {
console.log(obj.foo);
}
}
};
obj.deeper.logFoo(); //logs "bar"
</script>
复制代码
<script type="text/javascript">
function Listener() {
document.getElementById("foo").addEventListener("click",
this.handleClick);
}
Listener.prototype.handleClick = function (event) {
console.log(this); //logs "<div id="foo"></div>"
}
var listener = new Listener();
document.getElementById("foo").click();
</script>
复制代码
<script type="text/javascript">
function Listener() {
document.getElementById("foo").addEventListener("click",
this.handleClick.bind(this));
}
Listener.prototype.handleClick = function (event) {
console.log(this); //logs Listener {handleClick: function}
}
var listener = new Listener();
document.getElementById("foo").click();
</script>
复制代码
<div id="foo" onclick="console.log(this);"></div>
<script type="text/javascript">
document.getElementById("foo").click(); //logs <div id="foo"...
</script>
复制代码
到此结束~~~~~~~~~~~~~~~~~~~~~~~~~~~~~