JS 之 this 绑定规则

146 阅读3分钟

四种绑定规则

优先级从低到高:默认绑定隐式绑定显示绑定new绑定

默认绑定

function foo(){
    console.log(this.a)
}
var a = 'window.a'
foo() // window.a,函数调用模式

//严格模式下,全局对象将无法使用默认绑定,即执行会报undefined的错误:
function foo(){
    "use strict"
    console.log(this.a)
}
var a = 'window.a'
foo() // Uncaught TypeError: Cannot read property 'a' of undefined,函数调用模式

隐式绑定

除直接对函数进行调用外,有些情况,函数的调用是在某个对象上触发的,即调用位置上存在上下文对象:

function foo(){
    console.log(this.a)
}
var a = 'window.a'
var obj = {
    a: 'obj.a',      
    foo: foo
}
obj.foo() // obj.a,方法调用模式

多层调用链:

function foo(){
    console.log(this.a)
}
var a = 'window.a'
var obj1 = {
    a: 'obj1.a',     
    foo1: foo
}
var obj2 = {
    a: 'obj2.a',     
    foo2: obj1
}
obj2.foo2.foo1() // obj1.a,方法调用模式

函数别名(隐式丢失):

function foo(){
    console.log(this.a)
}
var a = 'window.a'
var obj = {
    a: 'obj.a',     
    foo: foo
}
var bar = obj.foo // obj.foo是引用属性,赋值给bar的实际上就是foo函数
bar() // window.a,函数调用模式

回调函数(隐式丢失):

function foo(){
    console.log(this.a)
}
var a = 'window.a'
var obj = {
    a: 'obj.a',      
    foo: foo
}
setTimeout(obj.foo,100) // window.a,函数调用模式

显示绑定

通过改变对象的prototype关联对象,call和apply作用一样,区别是接受参数方式不同:

function foo(){
    console.log(this.a)
}
var a = 'window.a'
var obj1 = {
    a: 'obj1.a'
}
var obj2 = {
    a: 'obj2.a'
}
foo.call(obj1) // obj1.a ,间接调用函数
foo.apply(obj2)// obj2.a ,间接调用函数

硬绑定:

function foo(){
    console.log(this.a)
}
var a = 'window.a'
var obj1 = {
    a: 'obj1.a'
}
var obj2 = {
    a: 'obj2.a'
}
var bar = function(){
    foo.call(obj1) // foo已经显示绑定了obj1,this指向obj1.
}
setTimeout(bar ,100) // obj1.a ,函数调用模式
bar.call(obj2) // obj1.a , 间接调用函数

new 绑定

function foo(a) { 
    this.a = a;
}
var a = 'window.a'
var bar1 = new foo('bar1.a');
console.log(bar1.a) // bar1.a ,构造函数调用模式
var bar2 = new foo('bar2.a');
console.log(bar2.a) // bar2.a ,构造函数调用模式

箭头函数(拓展)

箭头函数的this绑定取决于外层(函数或全局)作用域:

①正常调用:

//普通函数
function foo(){     
    console.log( this.a );
}
var a = 'window.a'
var obj = { 
    a: 'obj.a',
    foo: foo 
};
obj.foo(); // obj.a

// 箭头函数
var foo = () => {     
    console.log( this.a );
}
var a = 'window.a'
var obj = { 
    a: 'obj.a',
    foo: foo 
};
obj.foo(); // window.a
foo.call(obj) // window.a

②回调函数:

// 普通函数
function foo(){ 
    return function(){
        console.log( this.a );
    }   
}
var a = 'window.a'
var obj = { 
    a: 'obj.a',
    foo: foo 
};
var bar = obj.foo();
bar(); // window.a

// 箭头函数
var foo = () => { 
    return function(){
        console.log( this.a );
    }   
}
var a = 'window.a'
var obj = { 
    a: 'obj.a',
    foo: foo 
};
var bar = obj.foo();
bar(); // obj.a

函数调用

4种调用模式:函数调用模式方法调用模式构造函数调用模式间接调用模式

函数调用模式

function foo(){
    console.log(this.a)
}
var a = 'window.a'
foo() // window.a ,函数调用模式

方法调用模式

一个函数被保存为对象的一个属性时,称它为一个方法。当一个方法被调用时,this被绑定到该对象。如果调用表达式包含一个提取属性的动作,那么它就是被当做一个方法来调用:

function foo(){
    console.log(this.a)
}
var a = 'window.a'
var obj = {
    a: 'obj.a',      
    foo: foo
}
obj.foo() // obj.a ,方法调用模式

构造函数调用模式

函数或者方法调用之前带有关键字new,它就构成构造函数调用:

function foo(a) { 
    this.a = a;
}
var a = 'window.a'
var bar1 = new foo('bar1.a');
console.log(bar1.a) // bar1.a ,构造函数调用模式

间接调用模式

function foo(){
    console.log(this.a)
}
var a = 'window.a'
var obj1 = {
    a: 'obj1.a'
}
var obj2 = {
    a: 'obj2.a'
}
foo.call(obj1) // obj1.a ,间接调用函数
foo.apply(obj2) // obj2.a ,间接调用函数

New操作符

new操作符新建一个对象,这个对象原型指向构造函数的prototype,执行构造函数后返回这个对象:

var Func = function(){}  //声明构造函数Fun
var func = new Func()     //新建实例fun

创建一个空对象

var obj = new Object()

链接到原型

//当调用构造函数创建一个实例后,实例内部将包含一个指针(内部属性__proto__)指向构造函数的原型对象
obj.__proto__ = Func.prototype

绑定this指向,执行构造函数

//创建新对象后,将构造函数的作用域赋给新对象,因此this就指向了这个新对象
var result = Func.call(obj)

确保返回的是对象

if(typeof(result) == "object"){//判断返回值类型
    func = result    //引用类型,返回这个引用类型的对象
}else {
    func = obj       //基本数据类型,返回新创建的对象obj
}