JavaScript深入系列之this

1、为什么使用this

this 提供了用更加优雅的方式来隐式传递一个对象的引用,使得api设计的更加简洁易于复用。如果代码越来越复杂,使用显示传递上下文对象会让代码变得越来越混乱。

2、this 的指向

我们可以确定this的并不指向自身的函数,

3、this的作用域

在任何情况下,this都是不指向函数的词法作用域

4、this到底是什么?

this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。当一个函数被调用时,会创建一个活动记录(也称执行上下文)。这个记录会包含函数调用位置、函数的调用方式、传入参数化等信息。this就是这个记录的一个属性,会在函数调用过程中用到。

5、this的四条绑定规则

1)默认绑定规则

默认绑定规则可以看作无法应用其他规则时的默认规则。最常用的函数调用类型:独立函数调用,独立函数调用就是应用默认绑定规则。
使用默认绑定规则下,this指向全局对象。注意在严格模式下不能将全局对象用于默认绑定,因此this会绑定undefined。
思考下面的代码:


2)隐式绑定规则

先思考一段代码:

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

foo函数无论直接在obj中定义还是先定义再添加为引用属性,这个函数严格来说都不属于obj对象。然而,函数调用的位置会使用 obj 上下文来引用函数,因此可以说函数被调用时obj对象‘拥有’或者‘包含’函数的引用。所以,当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定在这个上下文对象。
还需要注意,对象属性引用链只有上一层或者说最后一层调用位置起作用。举例来说:

function foo() {
        console.log(this.a);
 }
 var obj2 = {
     a: 1,
     foo: foo
 }
 var obj1 = {
     a: 2,
     obj2: obj2
 }
 obj1.obj2.foo() //1
隐式丢失

一个常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是它会应用默认绑定。请看下面的代码:

//显示赋值
function foo() {
     console.log(this.a);
 }
 
 var obj = {
     a: "a",
     foo: foo
 }
 
 var a="global"
 
 var bar = obj.foo//函数别名
 
 bar()//"global"
 
 /*
 虽然 bar 是obj.foo的一个引用,但是实际上,它引用的是foo函数本身,
 因此这里bar()其实是一个不加任何修饰的函数的调用,
 我们也可以叫做独立函数调用,因此引用默认规则绑定。
 */
//隐式赋值,参数传递是一种隐式赋值,当传入的参数是函数也会被隐式赋值,所以结果跟上个例子一样如下

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

 function doFoo(fn) {
     fn() //调用位置
 }

 var obj = {
     a: 2,
     foo: foo
 }

 var a = 'global'

 doFoo(obj.foo)//'global'
//如果将函数传入语言内置函数,也是会出现隐式丢失

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

var obj = {
    a: 2,
    foo: foo
}

var a = 'global'

setTimeout(obj.foo, 100)//'global'

// JavaScript环境中内置的 setTimeout()函数实现和下面的伪代码类似:

function setTimeout(fn, delay) {
        fn()// <--调用位置
    };
3)显示绑定

可以使用函数的 call(…)apply(…) 方法,它们的第一参数是一个对象,是给this准备的,在调用函数时将其绑定到this。因为可以直接指定 this 的绑定对象,因此称之为 显示绑定。如果传入一个原始值作为this的绑定对象:字符串类型、布尔类型、数字类型,该原始值会被转换成它的对象形式:new String(…)、new Boolen(…),new number(…),这通常称为“装箱”。请看以下代码:

//call 和 apply,在this绑定是一样的,具体区别在其他参数上,这里不多讨论

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

显示绑定还是无法解决绑定丢失问题。

硬绑定

显示绑定的变种:硬绑定可以解决参数丢失绑定的问题,但是如果使用硬绑定,一旦绑定,修改不了this绑定对象。请思考下面的代码:

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

 var obj = {
     a: 2

 var bar = function () {
     foo.call(obj)
 }
 
 bar()//2
 setTimeout(bar, 100) //2
 
//硬绑定的bar 不可能修改它的this
var obj2 = {
    a:3
}
 bar.call(obj2) // 2
4)new绑定

在JavaScript中,构造函数只是一些使用 new 操作符时被调用的函数,不属于某个类,也不会实例化一个类。

使用new 来调用函数,会执行的的操作
/*
1、创建一个全新的对象
2、这个新对象会执行 [[Prototype]] 连接。
3、这个新对象会绑定到函数调用的 this
4、如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象
*/

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

使用new 来调用函数时,会构造一个新的对象并把它绑定在函数调用中的 this上。这种绑定方法称之为 new绑定

优先级

new绑定 > 显示绑定 > 隐式绑定

绑定例外

1、被忽略的this

null、undefined 作为this的绑定对象传入 call、apply和bind,这些值在调用时会被忽略,实际应用了默认绑定规则

更安全的this

当使用call、apply、bind方法,需要忽略绑定对象的时候,如果使用 null ,可能会有一些副作用,比如在默认绑定规则下,可能会发生修改全局对象的可怕后果。所以需要一种策略来防止这种情况:可以传入一个特殊对象,把this绑定在这个对象下不会对程序有任何的副作用,可以创建一个空的非委托对象,将this限制在这个空对象中。请看下面的代码

  function foo() {
      console.log("a:" + a, "b:" + b);
  }

  var o = Object.create(null)

  foo.apply(o, [2, 3])//a:2,b:3

软绑定

软绑定可以实现硬绑定的效果,但是同时可以解决硬绑定不可以修改this的缺点,从而提供函数的灵活性。

 if (!Function.prototype.softBind) {
        Function.prototype.softBind = function (obj) {
            var fn = this
            var curried = [].slice.call(arguments, 1)
            var bound = function () {
                return fn.apply(
                    (!this || this === (window || global)) ? obj : this,
                    curried.concat.apply(curried, arguments))
            }
            bound.prototype = Object.create(fn.prototype)
            return bound
        }
    }

this和箭头函数

所有的函数都试用于前面四条规则,es6的箭头函数则无法使用这些规则,而是根据当前的词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的this
绑定,这和es6之前的 var self= this 的机制一样

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值