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

Posted karshey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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高级 |如何玩转箭头函数?

以上是关于JavaScript高级this绑定绑定优先级相关面试题与箭头函数的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript高级学习笔记目录(持续更新)

JS你不知道的JavaScript笔记- this - 四种绑定规则 - 绑定优先级 - 绑定例外 - 箭头函数

Javascript中this指向的四种绑定规则

一文搞定JavaScript的this指向问题

一文梳理JavaScript中的this

this到底指向什么?记住这4点就够了!