python用于网站开发,作风建设活动网站,深圳市罗湖区住房和建设局网站,做app需要什么条件在Javascript中#xff0c;this 关键字是一个非常重要的概念#xff0c;this这个关键字可以说是很常见也用的很多#xff0c;说它简单也很简单#xff0c;说它难也很难。我们经常会用到this#xff0c;也经常会因为this头疼#xff0c;是一个经常被误解和误用的概念…在Javascript中this 关键字是一个非常重要的概念this这个关键字可以说是很常见也用的很多说它简单也很简单说它难也很难。我们经常会用到this也经常会因为this头疼是一个经常被误解和误用的概念为什么呢因为有时候我们不知道this到底指的是什么怎么用
在 JavaScript 中this 的值在函数被调用时确定而不是在函数被创建时确定。这使得 this 在 JavaScript 中的行为与其他一些语言中的类似关键字如 Python 的 self 或 Java 的 this有所不同。本文将从全局作用域或函数外部、普通函数调用、对象的方法、构造函数、事件处理函数、箭头函数几个方面来剖析JavaScript中的this。
一、抛砖引玉
先看一段代码
var name 前端技术营;
var obj {name: 张三,foo: function() {console.log(this.name);}
};
var foo obj.foo;
obj.foo(); // 张三
foo(); // 前端技术营可以看到上面代码中obj.foo() 和 foo() 都指向同一个函数但是执行结果却不一样产生这种差异的原因就在于函数体内部使用了 this 关键字。
在《JavaScript高级程序设计》一书中是这样说的this对象是在运行时基于函数的执行环境绑定的在全局函数中this等于window而当函数被作为某个对象的方法调用时this等于那个对象。不过匿名函数的执行环境具有全局性因此其this对象通常纸箱window。
所以上面的问题对obj.foo() 来说 foo 运行在 obj 环境中所以 this 指向 obj 对于 foo() 来说 foo 运行在全局环境下所以在非严格模式下 this 指向 window 所以导致了两者运行的结果不同。看到这有的人可能就有疑问了函数的运行环境是如何判定的为什么 obj.foo() 就是在 obj 环境为何 var foo obj.foo; foo() 就在全局环境执行了插个眼继续往下看就明白这个问题了
二、为什么需要this
先看下面代码
function foo() {console.log(this.name)
}var bar {name: 张三,foo: foo
}
var baz {name: 李四,foo: foo
}
bar.foo(); // 张三
baz.foo(); // 李四Javascript 引擎在处理上面代码时会在堆内存中生成两个对象然后把这两个对象在内存中的地址分别赋值给变量bar和baz。在读取 this.name 时需要先从变量bar和baz拿到地址然后再分别从对应地址中拿到对象再返回它的 name 属性。
对象的属性是一个函数当引擎遇到对象属性是函数的情况会将函数单独保存在堆中然后再将函数的地址赋值给对象属性而 Javascript 是允许在函数体内引用当前环境的其他变量。那么问题来了函数可以在不同的运行环境执行所以我们就需要一种机制能够在函数内获得当前运行环境foo只定义了一次却可以被不同的对象引用实现了代码共享由此诞生了 this它的设计目的就是指向函数运行时所在的环境。
那么如何正确的在代码中判定this所指向的环境呢
三、全局作用域中
在全局作用域代码中this 是不变的this始终是全局对象本身即window。
var a 张三;
this.b 李四;
window.c 王五;console.log(this.a); // 张三
console.log(b); // 李四
console.log(this.c); // 王五console.log(this window); // true运行以上代码发现this window为true也就是说在全局作用域中this就是全局对象window所以上述 a ,b ,c 都相当于在全局对象上添加相应的属性。
var a 1;
var b function () {return function1;
}
console.log(window.a); //1
console.log(window.b); //ƒ (){ return function1; }
console.log(window.a a); //true
console.log(window.b b); //true在全局对象上定义的变量可以直接访问。
window.aa 2;
this.bb function () {return function2;
}
console.log(aa); //2
console.log(bb); //ƒ (){ return function2; }四、函数中的this
在函数中使用this才是令我们最容易困惑的这里我们主要是对函数代码中的this进行分析。
这里再次强调一下this的指向在函数创建的时候是决定不了的而是在进入当前执行上下文时确定的也就是在函数执行时并且是执行前确定的。但是同一个函数作用域中的this指向可能完全不同但是不管怎样函数在运行时的this的指向是不变的而且不能被赋值。
函数中this的指向丰富的多它可以是全局对象、当前对象、或者是任意对象当然这取决于函数的调用方式。在JavaScript中函数的调用方式有一下几种方式作为函数调用、作为对象属性调用、作为构造函数调用、使用apply或call调用。下面我们将按照这几种调用方式一一讨论this的含义。
4.1 作为函数调用
看如下代码
function foo() {var name 张三;console.log(this.name); // undefinedconsole.log(this); // Window
}foo();按照我们上面说的this最终指向的是调用它的对象这里的函数foo实际是被Window对象所点出来的下面的代码就可以证明。
function foo() {var name 张三;console.log(this.name); // undefinedconsole.log(this); // Window
}window.foo();和上面代码一样其实alert也是window的一个属性也是window点出来的。
function foo() {function bar() {this.name 张三;console.log(this window); // true}bar()
}
foo();
console.log(name); // 张三上述代码中在函数内部的函数独立调用此时this还是被绑定到了window。
在严格模式下不能将全局对象 window 作为默认绑定此时 this 会绑定到 undefined 但是在严格模式下调用函数则不会影响默认绑定。
function foo() {use strict;console.log(thiswindow); // falseconsole.log(thisundefined); // true
}
foo();use strict
function foo() {var name 张三;console.log(this.name);
};foo();// Uncaught TypeError: Cannot read property ‘name’ of undefined at foo 加了use strict之后和上面一样的代码运行就会报错在严格模式下不能将全局对象 window 作为默认绑定。
var name 张三;function foo() {console.log(this.name); // 张三console.log(this window); // true
};(() {use strictfoo();
})();看上面代码在foo() 前加了use strict运行并没有报错依然打印出了结果可见在严格模式下调用函数则不会影响默认绑定。
小结当函数作为独立函数被调用时内部this被默认绑定为(指向)全局对象window但是在严格模式下会有区别在严格模式下this被绑定为undefined。
4.2 作为对象属性调用
先看一段代码
var obj {name: 张三,foo: function() {console.log(this.name); //张三}
}
obj.foo();根据this最终指向调用它的对象可知这里的this指向的是对象obj因为调用这个foo是通过obj.foo()执行的那自然指向就是对象o。
是不是感觉自己懂了别急再看看下边的代码。
var obj {name: 张三,foo: function() {console.log(this.name); // 张三}
}
window.obj.foo();先解释一下window.obj.foo()window是js中的全局对象我们创建的变量实际上是给window添加属性所以这里可以用window点obj对象。
再看这段代码和上面的那段代码几乎是一样的但是这里的this为什么不是指向window如果按照上面的理论最终this指向的是调用它的对象那这个理论还成不成立呢我们先接着再看下面一段代码。
var obj {name: 张三,bar: {name: 李四,foo: function() {console.log(this.name); // 李四}}
}
obj.bar.foo();这里同样也是对象obj点出来的但是同样this并没有执行它那你肯定会说我一开始说的那些不就都是错误的吗其实也不是只是一开始说的不准确接下来补充一句话我相信你就可以彻底的理解this的指向的问题。
1、如果一个函数中有this但是它没有被上一级的对象所调用那么this指向的就是window这里需要说明的是在js的严格版中this指向的不是window。
2、如果一个函数中有this这个函数有被上一级的对象所调用那么this指向的就是上一级的对象。
3、如果一个函数中有this这个函数中包含多个对象尽管这个函数是被最外层的对象所调用this指向的也只是它上一级的对象。
var obj {name: 张三,bar: {// name: 李四,foo: function() {console.log(this.name); // undefined}}
}
obj.bar.foo();这段代码尽管对象bar中没有属性name这个this指向的也是对象bar因为this只会指向它的上一级对象不管这个对象中有没有this要的属性。
var obj {name: 张三,bar: {age: 18,foo: function() {console.log(this.age); // undefinedconsole.log(this); // window}}
}var fn obj.bar.foo;
fn();解释obj.bar.foo方法声明部分只需要理解为在堆内存中开辟了一块空间并由obj.bar.foo持有这块内存空间的引用由于函数尚未执行因此还没有确定this。将obj.foo赋值给bar也就是将函数的引用拷贝一份给了barbar独立调用因此this指向window。this永远指向的是最后调用它的对象也就是看它执行的时候是谁调用的。
4.3 使用apply或call调用
apply和call为函数原型上的方法。它可以更改函数内部this的指向。
var name 前端技术营;function foo() {console.log(this.name);
}
var obj1 {name: 张三
}
var obj2 {name: 李四
}
var obj3 {name: 王五
}
// this指向window打印“前端技术营”
foo();
// this指向 obj1打印“张三”
foo.apply(obj1);
// this指向 obj2打印“李四”
foo.call(obj2);
// this指向 obj3打印“王五”
foo.call(obj3);当函数foo 作为独立函数调用时this被绑定到了全局对象window当使用bind、call或者apply方法调用时this 被分别绑定到了不同的对象。
call和apply 的功能一样唯一不同的是传给函数的参数的方式第一个参数是this指向的新对象从第二个参数开始apply传数组这个数组包含函数所需要的参数apply只支持传入一个数组哪怕是一个参数也要是数组形式最终调用函数时候这个数组会拆分成一个个参数分别传入call 直接传参数多个参数逗号分割。
var obj1 {name: 张三
}
var obj2 {name: 李四
}function foo(arg1, arg2) {console.log(this);console.log(arg1 arg2);
};foo.call(obj1, 1, 2);
// {name: 张三}
// 3foo.apply(obj2, [1, 2]);
// {name: 李四}
// 3还有一个bind方法。bind方法和call使用方式一样作用也一样不一样的是实现方式call和apply传参结束后直接执行函数而bind只是更改this值和给函数传参函数并不执行所以bind可以作为事件的处理函数去使用。
var name 前端技术营;function foo() {console.log(this.name);
}
var obj {name: 张三
}foo.bind(obj);
console.log(foo.bind(obj))
// ƒ foo() { console.log(this.name); }foo.bind(obj)(); // 张三
function add(a, b){return a b
}
function sub(a, b){return a - b
}
add.bind(sub, 5, 3)(); // 84.4 作为构造函数调用
var name 前端技术营;function Foo(){this.name 张三;
}
var baz new Foo();
console.log(baz.name); // 张三这里之所以对象baz可以点出函数Foo里面的name是因为new关键字可以改变this的指向将这个this指向对象baz(因为用了new关键字就是创建一个对象实例)这里用变量baz创建了一个Foo的实例相当于复制了一份Foo到对象baz里面此时仅仅只是创建并没有执行而调用这个函数Foo的是对象baz那么this指向的自然是对象baz。那么为什么对象baz中会有name因为你已经复制了一份Foo函数到对象baz中用了new关键字就等同于复制了一份。
4.5 总结
当我们要判断当前函数内部的this绑定可以依照下面的原则
1函数是否在是通过 new 操作符调用如果是this 绑定为新创建的对象。
var bar new foo(); // this指向bar 2函数是否通过call或者apply调用如果是this 绑定为指定的对象
foo.call(obj1); // this指向obj1 foo.apply(obj2); // this指向obj2 3函数是否通过 对象 . 方法调用如果是this 绑定为当前对象
obj.foo(); // this指向obj 4函数是否独立调用如果是this 绑定为全局对象。
foo(); // this指向window