JavaScript笔记 | this和对象原型 |《你不知道的JavaScript(上卷)》第二部分

来自《你不知道的JavaScript(上卷)》第二部分

1.关于this

举例1:
foo被调用了几次?

function foo(num) {
   
    console.log( "foo: " + num );
    // 记录 foo 被调用的次数
    this.count++;
}
foo.count = 0;
var i;
for (i=0; i<10; i++) {
   
    if (i > 5) {
   
    foo( i );
}
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
// foo 被调用了多少次?
console.log( foo.count ); // 0

举例2:
匿名函数无法指向自身

function foo() {
   
    foo.count = 4; // foo 指向它自身
}
setTimeout( function(){
   
    // 匿名(没有名字的)函数无法指向自身
}, 10 );

解决办法:
(1)治标不治本:创建另一个带有count属性的对象

function foo(num) {
   
    console.log( "foo: " + num );
    // 记录 foo 被调用的次数
    data.count++;
}
var data = {
   
    count: 0
};
var i;
for (i=0; i<10; i++) {
   
    if (i > 5) {
   
        foo( i );
    }
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
// foo 被调用了多少次?
console.log( data.count ); // 4

(2)治标不治本:用foo标识符替代this来引用函数对象。
同样回避了this的问题,并且完全依赖于变量foo的词法作用域。

function foo(num) {
   
    console.log( "foo: " + num );
    // 记录 foo 被调用的次数
    foo.count++;
}
foo.count=0
var i;
for (i=0; i<10; i++) {
   
     if (i > 5) {
   
         foo( i );
     }
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
// foo 被调用了多少次?
console.log( foo.count ); // 4

(3)强制this指向foo函数对象

function foo(num) {
   
    console.log( "foo: " + num );
    // 记录 foo 被调用的次数
    // 注意,在当前的调用方式下(参见下方代码),this 确实指向 foo
    this.count++;
}
foo.count = 0;
var i;
for (i=0; i<10; i++) {
   
    if (i > 5) {
   
        // 使用 call(..) 可以确保 this 指向函数对象 foo 本身
foo.call( foo, i );
    }
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9
// foo 被调用了多少次?
console.log( foo.count ); // 4

1.2关于this的误解

(1)误解this是指向自身的
(2)误解this一直都指向函数的作用域
this在任何情况下都不指向函数的词法作用域

2.this全面解析

2.1调用位置

要分析调用栈(就是为了到达当前执行位置所调用的所有函数)

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 的调用位置

查看调用栈的方法:
就本例来说,你可以在浏览器开发者工具中给 foo() 函数的第一行代码设置一个断点,或者直接在第一行代码之前插入一条 debugger; 语句。运行代码时,调试器会在那个位置暂停,同时会展示当前位置的函数调用列表,这就是你的调用栈。

2.2绑定规则

2.2.1默认绑定

独立函数调用
无法应用其他规则时的默认规则

function foo(){
   
    console.log(this.a);
}
var a=2;
foo();//2

分析:

  • 在全局作用域中声明变量var a=2;
  • 调用foo()时,this指向全局对象,因为在本例中,函数调用时应用了this的默认绑定。
  • foo()是直接使用不带任何修饰的函数引用进行调用的,因此只能使用默认绑定,无法应用其他规则。
  • 如果使用严格模式,那么全局对象将无法使用默认绑定,因此this会绑定到undefined

这里有一个微妙但是非常重要的细节,虽然 this 的绑定规则完全取决于调用位置,但是只有 foo() 运行在非 strict mode 下时,默认绑定才能绑定到全局对象;严格模式下与 foo()的调用位置无关:

function foo(){
   
    console.log(this.a);
}
var a=2;
(function(){
   
    "use strict";
    foo();//2
})();
2.2.2 隐式绑定

隐式绑定的一般适用情况:
调用位置有上下文对象,或被某个对象拥有或包含

function foo(){
   
    console.log(this.a);
}
var obij={
   
    a=2,
    foo:foo
};
obj.foo();//2

分析:

  • 无论是直接在obj中定义还是先定义再添加为引用属性,这个函数严格来说都不属于obj对象。
    -但调用位置会适用obj上下文来引用函数,因此可以说函数被调用时obj对象“拥有”或“包含”它。
  • 当foo()被调用时,它的落脚点指向obj对象。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。因为调用foo()时,this被绑定到obj,因此this.a和obj.a是一样的。
    注意:
  • 对象属性引用链中只有最定测或者最后一层会影响调用位置。
function foo(){
     
   console.log(this.a);
}
var obj2={
   
    a:42,
    foo:foo
};
var obj1={
   
    a:2,
    obj2:obj2
};
obj1.obj2.foo();//42

隐式丢失

  • 被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把this绑定到全局对象或者undefined上,取决于是否严格模式。

例子1:

function foo(){
   
   console.log(this.a);
}
var obj={
   
    a:2,
    foo:foo
};
var bar=obj.foo;//函数别名
var a="oops,global";//a是全局对象的属性
bar();//"oops,global"

分析:
bar实际上引用的是foo函数本身,因此此时bar其实是一个不带任何修饰的函数调用,因此应用了默认绑定。

例子2
回调函数中的隐式丢失

function foo(){
   
    console.log(this.a);
}

function doFoo(fn)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值