2.4 this 绑定例外

在某些场景下 this 的绑定行为会出乎意料,你认为应当应用其他绑定规则时,实际上应用的可能是默认绑定规则。

被忽略的 this

如果把 null 或者 undefined 作为 this 的绑定对象传入 callapply 或者 bind,这些值在调用时会被忽略,实际应用的是默认绑定规则:

function foo(){
    console.log(this.a)
}
var a = 2
foo.call(null)
-------------------------------------
> 2

什么情况下会传入 null 呢?
一种非常常见的做法是使用 apply(..) 来 “展开” 一个数组,并当作参数传入一个函数。类似的,bind(..) 可以对参数进行柯里化(预先设置一些参数),这种方法有时非常有用:

function foo(a, b) {
    console.log("a:" + a + ',b:' + b)
}
// 把数组“展开”成参数
foo.apply(null,[2,3])
// 使用 bind(..) 进行柯里化
var bar  = foo.bind(null,2)
bar(3)
------------------------------------------------------
> a:2,b:3
> a:2,b:3

这两种方法都需要传入参数当作 this 的绑定对象。如果函数并不关心 this 的话,仍然需要传入一个占位值,这时 null 可能是一个不错的选择就像代码所示。
然而,总是使用 null 来忽略 this 绑定可能产生一些副作用。如果某个函数确实使用了 this (比如第三方库中的一个函数),那默认绑定规则则会把 this 绑定到全局对象(在浏览器中这个对象是 window),这将导致不可预计的后果(比如修改全局对象)。

更安全的 this
一种“更安全”的做法是传入一个特殊的对象,把 this 绑定到这个对象不会对你的程序产生任何副作用。就像网络(以及军队)一样,可以创建一个 “DNZ”(demilitarized zone, 非军事区)对象——它就是一个空的非委托的对象。
如果再忽略 this 榜单时总是传入一个 DMZ 对象,就什么都不用担心了,因为任何对于 this 的使用都会被限制在这个空对象中,不会对全局对象产生任何影响。
由于这个对象完全是一个空对象,可以用变量名 ø 来表示它(也可以用其他符号表示)。
无论叫什么,在 JavaScript 中创建一个空对象最简单的方法都是 Object.create(null)Object.create(null){} 很像,但是并不会创建 Object.prototype 这个委托,所以 {} “更空”。

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

// 创建 DMZ 空对象
var ø = Object.create(null)

// 把数组展开成参数
foo.apply(ø,[2,3])

// 使用bind(..)进行柯里化
var bar = foo.bind(ø,2)
bar(3)
---------------------------------------------
> a:2,b:3
> a:2,b:3

使用变量名 ø 不仅让函数变得更加 “安全”,而且可以提高代码的可读性,因为 ø 表示“我希望 this是空”,这比 null 的含义更清楚。

间接引用

另一个需要注意的是,(有意或着无意地)创建一个函数的“间接引用”,在这种情况下,调用这个函数会应用默认绑定规则。
间接引用最容易在赋值时发生:

function foo(a, b) {
    console.log(this.a)
}

var a = 2
var o = {
    a:3,
    foo:foo
}
var p ={
    a:4
}
o.foo();
(p.foo = o.foo)();
-----------------------------------------
> 3
> 2

赋值表达式 p.foo = o.foo 的返回值是目标函数的引用,因此调用位置是 foo() 而不是 p.foo 或者 o.foo 。根据之前说的,这里会应用默认绑定。
注意:对于默认绑定来说,决定 this 绑定对象的并不是调用位置是否处于严格模式,而是函数体是否处于严格模式。如果函数体处于严格模式,this 会被绑定到 undefined,否则 this 会被绑定到全局对象。

软绑定

之前提到硬绑定这种方式可以把 this 强制绑定到指定的对象(除了使用 new 时),防止函数调用应用默认绑定规则。问题在于,硬绑定会大大降低函数的灵活性,使用硬绑定之后就无法使用隐式绑定或者显示绑定来修改 this
如果可以给默认绑定指定一个全局对象和 undefined 意外的值,那就可以实现和硬绑定相同的效果,同时保留隐式绑定或者显式绑定修改 this 的能力。
可以通过一种被称为软绑定的方法来实现想要的效果:

if (!Function.prototype.softBind) {
    Function.prototype.softBind = function (obj) {
        var fn = this
        // 捕获所有 curried 参数
        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
    }
}

除了软绑定之外,softBind(..) 的其他原理和 ES5 内置的 bind(..) 类似。它会对指定的函数进行封装,首先检查调用时的 this, 如果 this 绑定到全局对象或者 undefined,那就把指定的默认对象 obj 绑定到 this,否则不会修改 this。此外,这段代码还支持可选的柯里化。
接下来看看 softBind 是否实现了软绑定功能:

function foo() {
    console.log("name: " + this.name);
}
var obj = { name: "obj" },
    obj2 = { name: "obj2" },
    obj3 = { name: "obj3" };
var fooOBJ = foo.softBind(obj)
fooOBJ() // name: obj
obj2.foo = foo.softBind(obj)
obj2.foo() // name: obj2
fooOBJ.call(obj3) // name: obj3
setTimeout(obj2.foo, 10) // name: obj

可以看到,软绑定版本的 foo() 可以手动将 this 绑定到 obj2 或者 obj3 上,但如果应用默认绑定,则会将 this 绑定到 obj

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值