【JavaScript高级】this绑定、绑定优先级、相关面试题与箭头函数

this绑定

  • 函数在调用时,JavaScript会默认给this绑定一个值
  • this的绑定与定义的位置无关
  • this的绑定与调用方式以及调用的位置有关
  • this在运行时被绑定

this有四种绑定规则:

  • 默认绑定
  • 隐式绑定
  • 显示绑定
  • new绑定

默认绑定

独立函数调用时使用默认绑定。 如:

  • 普通的函数独立被调用
function foo(){
   console.log(this);
}

foo();

这里控制台输出:Window

  • 函数定义在对象中,但是独立调用
function foo(){
    console.log(this);
}
var obj={
    bar:function(){
        console.log(this);
    }
}

var temp=obj.bar
temp()

这里控制台输出:Window

  • 严格模式下'use strict',独立调用的函数中的this指向的是undefined

隐式绑定

通过某个对象进行调用。 如:

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

var obj = {
    bar: foo
}

obj.bar()//这里是obj这个对象调用的bar

控制台输出:{bar: ƒ}
这里this就绑定到了obj这个对象上,就是{bar: ƒ}

注意:隐式绑定有一个前提条件

  • 必须在调用的对象内部有一个对函数的引用(比如一个属性)
  • 如果没有这个引用,在调用时就会报找不到该函数的错误
  • 正是通过这个引用,间接的将this绑定到了这个对象上

如果我们不希望在对象内部包含这个函数的引用,同时又希望这个对象进行强制调用,则可以用显示绑定。

显示绑定

通过call或apply绑定this对象我们成为显示绑定。

需求:调用foo且this指向obj

var obj = {
    name:"name"
}
function foo() {
    console.log(this);
    this.name="name";
}

完成需求:

foo.call(obj)

注意,是foo.call(obj)不是foo().call(obj)foo()是foo函数调用后的返回值,是undefined
foo().call(obj)就是undefined.call(obj).

控制台:{name: 'name'}——它就是obj

如果call()传递的对象是如"123",123这种,则会自动转换成其包装类对象。如果传递的对象是undefined,则this会绑定到window

实际上,显示绑定有两个函数:applycall

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

foo()

foo.apply("apply")

foo.call("call")

控制台:

Window {window: Window, self: Window, document: document, name: '', location: Location,}
String {'apply'}
String {'call'}

apply的参数:

  • 第一个参数:绑定this
  • 第二个参数:传入额外的参数,以数组的形式

call的参数:

  • 第一个参数:绑定this
  • 参数列表:后续的参数以多参数的形式传递

bind

如果我们希望一个函数总是显示地绑定到一个对象上,我们可以使用bind方法。

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

var obj={name:"name"}
var bar=foo.bind(obj)

bar()//this->obj

控制台:{name: 'name'}

bind的其他参数:

  • 第一个参数:绑定this
  • 参数列表:后续的参数以多参数的形式传递

new绑定

JavaScript中的函数可以当作一个类的构造函数来使用,也就是使用new关键字。

new关键字会做的事情:

  1. 创建新的空对象
  2. 将this指向空对象
  3. 执行函数体中的代码
  4. 没有显示返回这个非空对象时,默认返回这个对象
function foo() {
    console.log(this);
    this.name="name";
}
new foo()

控制台:foo {}

翻译:new了一个foo(),this指向它,然后执行foo()函数里的代码。

绑定优先级

  • 默认绑定的优先级最低
  • 显式绑定>隐式绑定
  • bind>call和apply
  • new绑定>隐式绑定
  • new绑定>bind
  • new、call、apply不能一起使用,无可比性

总体优先级从大到小:

  • new
  • bind
  • apply/call
  • 隐式
  • 默认

this规则之外:忽略显示绑定

  • 显示绑定中传入null或undefined,那么显示绑定会被忽略,使用默认绑定规则
  • 创建一个函数的间接引用,使用默认绑定规则,如:
var obj1={
   name:"obj1",
    foo:function(){
        console.log(this);
    }
}
var obj2={
    name:"obj2"           
};
obj2.foo=obj1.foo
obj2.foo()

控制台:{name: 'obj2', foo: ƒ} 是obj2.

但如果把最后两句改成:

(obj2.foo=obj1.foo)()

则:Window

因为(obj2.foo=obj1.foo)的结果是foo函数,因此整个式子可以看作是一个独立函数的调用。因此this指向Window

箭头函数

箭头函数,即arrow function,它:

  • 不会绑定this,arguments属性
  • 不能作为构造函数来使用(不能和new一起使用)

写法:

  • 参数()
  • 函数体{}
//之前的方式
var foo1 = function (name, age) {
    console.log("函数体");
    console.log(name + " " + age);
}

//箭头函数:最完整的写法
var foo2 = (name, age) => {
    console.log("箭头函数函数体");
    console.log(name + " " + age);
}

一个示例:

var names = ["123", "345","456"]
names.forEach((item, index, arr) => {
	console.log(item, index, arr)
})

控制台:

js.html:60 123 0 (3) ['123', '345', '456']
js.html:60 345 1 (3) ['123', '345', '456']
js.html:60 456 2 (3) ['123', '345', '456']

箭头函数的编写优化

  • 只有一个参数()可以省略
  • 函数体中只有一行代码可以省略{},且这行代码的返回值会是整个函数的返回值(所以一行代码不能带return)
  • 如果函数体只有一个返回对象,那么这个对象要加上()(不然会以为是执行体)

如:

var names = ["123", "345", "456"]
names.forEach(item =>
	console.log(item)
)

控制台:

123
345
456

如:

var nums = [1, 2, 3, 4, 5]
var newnums = nums.filter(item => item % 2 === 0)
console.log(newnums);

控制台:

 [2, 4]

箭头函数中的this使用

箭头函数中没有this:所以会往全局中找this,因此这里的this指向window

var bar = ()=>{console.log("bar",this)}
bar()//window
bar.apply("AAA")//window

箭头函数的查找规则

箭头函数中没有this,当箭头函数内部开始调用this时,js引擎就从作用域由里到外的找含有this指向的作用域。

如:
第一层,bar函数,是箭头函数,没有this,往外找;
第二层,foo函数,有this,指向obj;

var obj ={
	name:"obj",
  foo:function(){
    var bar = ()=>{
      console.log("bar",this);
    }
    return bar;
  }
}

如:
第一层,bar函数,是箭头函数,没有this,往外找;
第二层,foo函数,是箭头函数,没有this,往外找;
第三层,也就是全局,this指向window。(注意,对象里没有自己的作用域!)

var obj ={
	name:"obj",
  foo:()=>{
  	var bar =()=>{
      console.log("bar:",this);
    }
    return bar;
  }
}

相关面试题

问这些this都指向?

1

var name="window";

var person={
    name:"person",
    sayName:function(){
        console.log(this.name);
    }
};

function sayName(){
    var temp=person.sayName;
    temp();
    person.sayName();
    (person.sayName)();
    (b=person.sayName)();
}

sayName();

控制台:

window
person
person
window

解析:

  1. 独立函数调用,默认绑定,所以是window
  2. 隐式绑定
  3. 隐式绑定,跟上面一样。加一个括号就是优先获取到这个方法的意思。
  4. 简介函数引用(指路:this规则之外),b=person.sayName得到的就是一个函数,这里就是一个独立函数引用,所以是window

2

var person1 = {
    name: "person1",
    foo1: function () {
        console.log(this.name)
    },
    foo2:()=>console.log(this.name),
    foo3:function () {
        return function(){
            console.log(this.name)
        }
    },
    foo4:function(){
        return ()=>{
            console.log(this.name)
        }
    }
}

var person2={name:"person2"}

person1.foo1();
person1.foo1.call(person2);

person1.foo2();
person1.foo2.call(person2);

person1.foo3()();
person1.foo3.call(person2)();
person1.foo3().call(person2);

person1.foo4()();
person1.foo4.call(person2)();
person1.foo4().call(person2);

控制台:

person1
person2
window
window
window
window
person2
person1
person2
person1

解析:要找到上层作用域里的this指向的是什么

  1. 隐式绑定
  2. 显示绑定
  3. 箭头函数无this,会去上层作用域找this,会找到window
  4. 同上
  5. 默认绑定,这里person1.foo3()就是拿到一个函数,person1.foo3()();是独立函数调用
  6. 同上,person1.foo3.call(person2)这里相当于直接拿到一个函数
  7. 显示绑定
  8. 隐式绑定的是person1,且里面的箭头函数无this,所以它会向外找this,找到的就是隐式绑定的person1
  9. 显示绑定的是person2,且里面的箭头函数无this,所以它会向外找this,找到的就是显示绑定的person2
  10. 隐式绑定

3

var name = "window"

function Person(name) {
    this.name = name,
        this.foo1 = function () {
            console.log(this.name)
        },
        this.foo2 = () => console.log(this.name),
        this.foo3 = function () {
            return function () {
                console.log(this.name)
            }
        },
        this.foo4 = function () {
            return () => {
                console.log(this.name)
            }
        }
}

var person1 = new Person('person1')
var person2 = new Person('person2')

person1.foo1()
person1.foo1.call(person2)

person1.foo2()
person1.foo2.call(person2)

person1.foo3()()
person1.foo3.call(person2)()
person1.foo3().call(person2)

person1.foo4()()
person1.foo4.call(person2)()
person1.foo4().call(person2)

控制台:

person1
person2
person1
person1
window
window
person2
person1
person2
person1

解析:

  1. 隐式绑定
  2. 显示绑定
  3. 箭头函数无this,因此找上层作用域的this,是person1(注意,这里的上层是Person函数)
  4. 同上,无this所以call无效
  5. 独立函数调用,默认绑定
  6. 同上,因为返回的是函数
  7. 显示绑定
  8. 独立函数调用,但调用的是箭头函数,箭头函数里无this,所以往外层找,找到的this是person1(隐式绑定)
  9. 显示绑定了person2,独立函数调用,但调用的是箭头函数,箭头函数里无this,所以往外层找,找到的this是person2
  10. 对箭头函数call无效,箭头函数里无this,所以往外层找,找到的this是person1

4

var name = "window"
function Person(name) {
    this.name = name;
    this.obj = {
        name: 'obj',
        foo1: function () {
            return function(){
                console.log(this.name);
            }
        },
        foo2: function () {
            return () => {
                console.log(this.name);
            }
        }
    }
}

var person1 = new Person('person1');
var person2 = new Person('person2');

person1.obj.foo1()()
person1.obj.foo1.call(person2)()
person1.obj.foo1().call(person2)

person1.obj.foo2()()
person1.obj.foo2.call(person2)()
person1.obj.foo2().call(person2)

控制台:

window
window
person2
obj
person2
obj

解析:

  1. 独立函数调用,默认绑定
  2. 独立函数调用,默认绑定——就算绑定了返回的也是函数
  3. 显示绑定
  4. 独立函数调用,但是无this,往上找,找到的是obj(obj在调用,隐式绑定)
  5. 去上层作用域里找,找到了显示绑定的person2
  6. call但是无意义,去上层作用域里找,找到的是obj

参考资料

coderwhy的视频
JavaScript高级 |彻底搞清this指向问题
JavaScript高级 |如何玩转箭头函数?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

karshey

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值