只有掌握了JavaScript中的this操作符你才算正式迈入JavaScript这门语言的门槛!我们一直都在用这个框架,那个框架,但往往忽视掉了js最基础的东西,笔者认为这些基础往往才是走下去,走深,最不可或缺的东西.那我们就一起来学习一下js中神奇的this吧--------笔者查看了大量有关this的文章,有些是按照自己思路写的,有些是直接引用其他作者成熟的思路的文章
学习一个知识首先要理解他的字面含义,通过翻译我们知道,this的含义是这,这个(指较近的人或事物)的意思。那么我们结合现实,我们说的“这”在不同的环境所指的事物是不一样的。那么在JavaScript中this在不同的环境调用所表达的含义也是非常丰富的。如果你觉得JavaScript中的this和其他面向对象语言Java一样,是指存储在实例属性中的值,那你就大错特错了。JavaScript中的this有着在这门语言中不可或缺魔力。
宿主(环境)解释
JS的运行环境一般由宿主环境和执行期环境共同构成,宿主环境是由外壳程序(如web浏览器就是一个外壳程序)生成,执行期环境是由嵌入到外壳程序中的JS引擎(/JS解释器)生成的,在执行期环境JS可以生成内置静态对象、初始化执行环境等。
对于JavaScript,宿主环境最常见的是web浏览器,浏览器提供了一个JavaScript运行的环境,这个环境里面,需要提供一些接口,好让JavaScript引擎能够和宿主环境对接。JavaScript引擎才是真正执行JavaScript代码的地方,常见的引擎有V8(目前最快JavaScript引擎、Google生产)、JavaScript core
但是环境不是唯一的,也就是JavaScript不仅仅能够在浏览器里面跑,也能在其他提供了宿主环境的程序里面跑,最常见的就是nodejs。同样作为一个宿主环境,nodejs也有自己的JavaScript引擎--V8。根据官方的定义:
Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications
<script>
console.log(this === window) //true
var a = 3;
console.log(this.a, window.a)//3 3
</script>
说明:在浏览器中,window对象同时也是全局对象
> 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。
<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>
到此结束~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
本文由 前端小孟 作者:前端小孟 发表,转载请注明来源!