this被自动定义在所有函数的作用域中。
this的使用
function identify() {
return this.name.toUpperCase();
}
function speak() {
var greeting="我是"+identify.call(this);
console.log(greeting);
}
var me={
name:"zhixing"
}
var you={
name:"juanjuan"
}
speak.call(me); //我是ZHIXING
speak.call(you); //我是JUANJUAN
这段代码可以在不同的上下问中使用speak()函数。
考率以下程序的执行的效果,统计foo的执行次数
foo.count=0;
function foo(num) {
console.log(num);
this.count++;
}
for(var i=0;i<10;i++){
if(i>5){
foo(i) ;
}
}
console.log(foo.count); //0
为什么会输出0而不是4;
显然this指向函数本身这个解释是错误的,
在执行foo.count=0时;的确向函数对象foo添加了一个属性count。但是函数内部代码this.count中的this并不是指向那个函数对象。属性名虽然相同,但是不是一个。
foo.count=0;
function foo(num) {
console.log(num);
data.count++;
}
var data={count:0}
for(var i=0;i<10;i++){
if(i>5){
foo(i) ; //6 7 8 9
}
}
console.log(data.count); //4
上面这个程序实现了统计程序的执行的次数。使用的是data的全局的词法作用域。
foo.count=0;
function foo(num) {
console.log(num);
foo.count++;
}
var data={count:0}
for(var i=0;i<10;i++){
if(i>5){
foo(i) ; //6 7 8 9
}
}
console.log(foo.count); //4
上面的程序同样的统计了foo的执行次数。但是回避了this的使用
foo.count=0;
function foo(num) {
console.log(num);
//foo.call(foo,i);在这种情况下this确实指向了foo
this.count++;
}
var data={count:0}
for(var i=0;i<10;i++){
if(i>5){
foo.call(foo,i);
}
}
console.log(foo.count); //4
上面的程序实现了统计foo的执行的次数,同时在foo.call(foo,i)这种调用的方式下,this确实指向了foo()。所以才实现了上述的功能。
var count=0;
foo.count=0;
function foo(num) {
console.log(num);
this.count++;
}
for(var i=0;i<10;i++){
if(i>5){
foo(i) ;
}
}
console.log(foo.count); //这其实是输出的 foo.count=0;不是foo中的
console.log(count); //这里输出的count就是window的count
上面程序实现了统计了程序的执行次数。
在使用this的注意事项:不能使用this来引用一个词法作用域内部的东西
this到底是什么?
this是在运行的时候绑定的,并不是在编写的时候绑定,它的上下文取决于函数调用时候的各种条件。所以this的绑定和函数声明的位置没有关系,只与函数的调用方式有关系。this是上下文的一个属性
上下文:
当一个函数调用的时候,会创建一个活动记录(上下文)这个记录会包含函数在哪里被调用,调用的方法,传入参数的信息。this就是这个记录(上下文)的一个属性。
调用栈:
为了达到当前执行位置所调用的所有函数
调用位置:
当前正在执行的函数的前一个调用中。
演示调用栈和调用位置
function baz() {
//当前调用栈是baz
//当前调用位置是全局作用域
console.log("baz");
bar(); //bar 的调用位置
}
function bar() {
//当前调用栈是baz->bar
//当前调用位置是baz
console.log("bar");
foo(); //foo()的调用位置
}
function foo() {
//当前调用栈是baz->bar->foo
//当前调用的的位置是bar
console.log("foo");
}
baz() ;//baz的调用位置
依次会输出 baz bar foo
this的绑定规则
1、默认绑定:可以把这条规则看作是无法应用其他规则时的选择。同时默认绑定会将this绑定到全局对象上。
独立函数调用:this就会是默认绑定
function foo() {
console.log(this.a); //2
}
var a=2;
foo();
但是如果使用“严格模式”全局对象将无法使用默认绑定,this会被绑定到undefined的上边
function foo() {
"use strict";
console.log(this.a); //Cannot read property 'a' of undefined
}
var a=2;
foo();
2、隐式绑定
需要考虑的规则是调用位置是否由上下文对象或者被某一个对象拥有或者包含。
隐式绑定就会把this绑定到这个上下文对象或者这个对象上
var a=2;
function foo() {
console.log(this.a); //Cannot read property 'a' of undefined
}
var obj1={
a:23,
foo:foo
}
var obj2={
a:42,
foo:foo
}
obj1.foo(); //23
obj2.foo(); //42
但是这个规则在使用的时候可能会发生隐式丢失。
var a=2;
function foo() {
console.log(this.a); //Cannot read property 'a' of undefined
}
var obj1={
a:23,
foo:foo
}
var bar=obj1.foo;
bar(); //2
虽然bar是obj.foo的一个引用,但是实际上他引用的foo函数本身,因此此时的bar()其实是一个不带任何修饰的函数的调用,因此使用的是默认绑定。
3、显式绑定
使用call()和apply()函数来实现显式绑定。这两个方法都是第一个参数是一个对象,他们会把这个这个对象绑定到this上,接着在调用函数时指定这个this。
function foo() {
console.log(this.a);
}
var obj1={
a:23,
}
foo.call(obj1); //23
硬绑定可以解决丢失绑定的问题
function bind(fn,obj) {
return function () {
console.log(arguments);
return fn.apply(obj,arguments); //实现硬绑定的关键步骤
}
}
function foo(some) {
console.log(this.a,some);
return this.a+some;
}
var obj={a:1};
var bar=bind(foo,obj);
var b=bar(3,4);
console.log(b);
4、new绑定
1、创建(构造)一个全新的对象
2、这个新对象会执行【原型】连接
3、这个新对象会绑定到函数调用的this
4、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
function foo(a) {
this.a=a;
}
var bar=new foo(2);
console.log(bar.a); //2
以上几种绑定规则的优先级:
显示绑定高于隐式绑定
function foo() {
console.log(this.a);
}
var obj1={
a:1,
foo:foo,
}
var obj2={
a:2,foo:foo,
}
obj1.foo(); //1
obj2.foo(); //2
obj1.foo.call(obj2); //2
obj2.foo.call(obj1); //1