你不知道的JavaScript--this

目录

 

困惑

调用点与调用栈

四种绑定规则

默认绑定(Default Binding)

 隐含绑定(Implicit Binding)

隐含丢失(Implicitly Lost)

明确绑定(Explicit Binding)

硬绑定(Hard Binding)

new 绑定(new Binding)

绑定优先级

判断方法:

绑定的特例

被忽略的 this

更安全的 this


 

困惑

对 this 的两大误解(不能说不正确,但肯定不准确)

  • this 指向自己
  • this 指向函数的作用域

 

调用点与调用栈

当一个函数被调用时,会建立一个称为执行环境的活动记录。这个记录包含函数是从何处被调用的,函数是如何被调用的,被传递了什么参数等信息。这个记录的属性之一,就是在函数执行期间将被使用的 this 引用。

这其中,函数代码中被调用的位置(注意,不是被声明的位置)就是调用点(call-site)。

是我们到达当前执行位置而被调用的所有方法的堆栈,就是调用栈(call-stack)

function baz() {
    // 调用栈是: `baz`
    // 调用点位于: global scope(全局)

    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`的调用点

 

四种绑定规则

this 不是编写时绑定,而是运行时绑定。它只与调用点和绑定方式有关,这里只需要记住4种绑定规则:

  • 默认绑定
  • 隐含绑定
  • 明确绑定
  • new绑定

 

默认绑定(Default Binding)

此规则适用于独立函数调用,这种 this 规则是在没有其他规则适用时的默认规则 

function foo() {
    // 指向全局
    console.log(this.a)
}

var a = 2

foo()    // 2

foo() 是被一个直白的,毫无修饰的函数引用调用的。此时使用默认绑定

 

 隐含绑定(Implicit Binding)

调用点是有一个 环境对象(context object),也称为拥有者(owning)或容器(containing)对象。

function foo() {
    // 指向容器
    console.log( this.a )
}

obj1 = {
    a: 2,
    foo: foo
}

obj1.foo()    // 2

obj1 对象在函数被调用的时间点上 “拥有” 或 “包含” 这个函数引用。

但要注意,只有对象属性引用链的最后一层是影响调用点的:

function foo() {
    // 指向最近的容器obj2
    console.log( this.a )
}

obj2 = {
    a: 2,
    foo: foo
}

obj1 = {
    a: 22,
    obj2: obj2
}

obj1.obj2.foo()    // 2

仔细想想,所谓的函数直接调用不就是window.fun()的简写?也是对象对象调用

隐含丢失(Implicitly Lost)

一个 隐含绑定 丢失了它的绑定,这通常意味着它会退回到 默认绑定(回调会丢失绑定):

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

obj1 = {
    a: 2,
    foo: foo
}

var bar = obj1.foo    // 一个函数引用!

var a = "oops,global!"

// 注意调用点!是一个直白无修饰的引用
bar()    // "oops,global!"

作用域章节可知 传参是一种隐含的赋值,所以传递回调函数时同上面情况一样:

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

function doFoo(fn) {    

    fn()    // 一个直白的引用
}

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

var a = "opps, global"

// 将obj.foo作为参数传递,实质是传递的 fn = obj.foo
doFoo( obj.foo )    // "opps, global"

 

明确绑定(Explicit Binding)

问:如何强制一个函数调用使用某个特定对象作为 this 绑定,而不在这个对象上放置一个函数引用属性呢(不用隐含绑定)?

答:通过JS的工具方法——call(…) 和 apply(…) 

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

var obj = {
    a:2
}

foo.call( obj )    // 2

明确绑定依旧会遇到绑定丢失的问题!

 

硬绑定(Hard Binding)

通过创建一个函数,并在它内部使用明确绑定来绑定this的方法叫硬绑定。

硬绑定用来解决绑定丢失的问题。

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

var obj = {
    a: "im obj"
}

var bar = function() {
    foo.call(obj)
}

var a = "im global"

bar()    // "im obj"

// 硬绑定不可以被覆盖!
bar.call(window)    // "im obj"

如上代码,我们创建了一个函数 bar(),在它的内部明确绑定 foo.call(obj),由此强制 this 绑定到 obj 并调用 foo。无论你过后怎样调用函数 bar,它总是手动使用 obj 调用 foo。这种绑定即明确又坚定,所以我们称之为 硬绑定。

硬绑定最典型的方法是,为所有传入的参数和传出的返回值创建一个通道:

function foo(something) {
    console.log( this.a , something)
    return this.a+something
}

var obj = {
    a: 2
}

// 硬绑定
var bar = function(arguments) {
    return foo.apply(obj, arguments)
}

var b = bar([3])    // 2 3
console.log(b)    // 5

另一种典型的用法是创建一个可复用的帮助函数:

function foo(something) { 
    console.log( this.a, something );
    return this.a + something; 
}    // 简单的辅助绑定函数

function bind(fn, obj) {
    return function() {
        return fn.apply( obj, arguments ); 
    }; 
}

var obj = { a:2 };

var bar = bind( foo, obj );

var b = bar( 3 ); // 2 3 
console.log( b ); // 5

由于硬绑定是一种非常常用的模式,所以在 ES5 中提供了内置的方法 Function.prototype.bind,它的用法如下:

function foo(something) { 
    console.log( this.a, something ); 
    return this.a + something; 
}

var obj = { a:2 };
var bar = foo.bind( obj );

var b = bar( 3 );    // 2 3 
console.log( b );    // 5

bind(..) 会返回一个硬编码的新函数,它会把参数设置为 this 的上下文并调用原始函数

 

new 绑定(new Binding)

让我们明确一个观点:JS中,构造器仅仅是一个函数

构造器首字母大写。

JS的对象函数都可以在前面加上 new 来被调用,这使函数调用成为一个构造器调用(constructor call)。这是一个重要而微妙的区别:实际上不存在“构造器函数”这样的东西,而只有函数的构造器调用(通过new调用)

当在函数前面被加入 new 调用时,也就是构造器调用时,下面这些事情会自动完成:

  1. 一个全新的对象会凭空创建(就是被构建)
  2. 这个新构建的对象会被接入原形链([[Prototype]]-linked)
  3. 这个新构建的对象被设置为函数调用的 this 绑定
  4. 除非函数返回一个它自己的其他 对象,否则这个被 new 调用的函数将 自动   返回这个新构建的对象。

一个new绑定:

function foo(a) {
    this.a = a
}

var bar = new foo(2)
console.log( bar.a )    // 2

 

绑定优先级

new绑定 > 明确绑定 > 隐含绑定 > 默认绑定

判断方法:

1、函数是通过 new 被调用的吗(new 绑定)?如果是,this 就是新构建的对象。

      var bar = new foo()

2、函数是通过 call 或 apply 被调用(明确绑定),甚至是隐藏在 bind 硬绑定 之中吗?如果是,this 就是那个被明确指定的对象。

      var bar = foo.call( obj2 )

3、函数是通过环境对象(也称为拥有者或容器对象)被调用的吗(隐含绑定)?如果是,this 就是那个环境对象。

     var bar = obj1.foo()

4、否则,使用默认的 this(默认绑定)。如果在 strict mode 下,就是 undefined,否则是 global 对象。

     var bar = foo()

 

绑定的特例

被忽略的 this

如果你传递 null 或 undefined 作为 call、apply 或 bind 的 this 绑定参数,那么这些值会被忽略掉,取而代之的是 默认绑定 规则将适用于这个调用。

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

var a =2

foo.call(null)    // 2

更安全的 this

当没有参数传递时,可以传递一个空对象 Object.create(null)

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

// 我们的 DMZ 空对象
var dmz = Object.create( null )

// 将数组散开作为参数
foo.apply( dmz,[2,3] )    // a:2,b:3

// 用 `bind(..)` 进行 currying
var bar = foo.bind( dmz,2 )
bar( 3 )    // a:2,b:3

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值